diff --git a/bin/node/runtime/src/lib.rs b/bin/node/runtime/src/lib.rs index 6e07dcad53fc3..69dedfb599583 100644 --- a/bin/node/runtime/src/lib.rs +++ b/bin/node/runtime/src/lib.rs @@ -1251,6 +1251,7 @@ impl pallet_contracts::Config for Runtime { type MaxStorageKeyLen = ConstU32<128>; type UnsafeUnstableInterface = ConstBool; type MaxDebugBufferLen = ConstU32<{ 2 * 1024 * 1024 }>; + type RuntimeHoldReason = RuntimeHoldReason; #[cfg(not(feature = "runtime-benchmarks"))] type Migrations = (); #[cfg(feature = "runtime-benchmarks")] diff --git a/frame/contracts/Cargo.toml b/frame/contracts/Cargo.toml index 3c236e075de77..feaa7cf73fbcc 100644 --- a/frame/contracts/Cargo.toml +++ b/frame/contracts/Cargo.toml @@ -39,6 +39,7 @@ environmental = { version = "1.1.4", default-features = false } frame-benchmarking = { version = "4.0.0-dev", default-features = false, path = "../benchmarking", optional = true } frame-support = { version = "4.0.0-dev", default-features = false, path = "../support" } frame-system = { version = "4.0.0-dev", default-features = false, path = "../system" } +pallet-balances = { version = "4.0.0-dev", path = "../balances", optional = true, default-features = false } pallet-contracts-primitives = { version = "24.0.0", default-features = false, path = "primitives" } pallet-contracts-proc-macro = { version = "4.0.0-dev", path = "proc-macro" } sp-api = { version = "4.0.0-dev", default-features = false, path = "../../primitives/api" } @@ -82,7 +83,7 @@ std = [ "log/std", "rand/std", "environmental/std", - "pallet-balances/std", + "pallet-balances?/std", "pallet-insecure-randomness-collective-flip/std", "pallet-proxy/std", "pallet-timestamp/std", @@ -95,9 +96,9 @@ runtime-benchmarks = [ "rand", "rand_pcg", "wasm-instrument", + "pallet-balances/runtime-benchmarks", "frame-support/runtime-benchmarks", "frame-system/runtime-benchmarks", - "pallet-balances/runtime-benchmarks", "pallet-proxy/runtime-benchmarks", "pallet-timestamp/runtime-benchmarks", "pallet-utility/runtime-benchmarks", diff --git a/frame/contracts/README.md b/frame/contracts/README.md index 2b5fec6ca53d2..aeb30cef32fc8 100644 --- a/frame/contracts/README.md +++ b/frame/contracts/README.md @@ -9,8 +9,8 @@ The Contracts module provides functionality for the runtime to deploy and execut ## Overview -This module extends accounts based on the `Currency` trait to have smart-contract functionality. It can -be used with other modules that implement accounts based on `Currency`. These "smart-contract accounts" +This module extends accounts based on the [`frame_support::traits::fungible`] traits to have smart-contract functionality. It can +be used with other modules that implement accounts based on [`frame_support::traits::fungible`]. These "smart-contract accounts" have the ability to instantiate smart-contracts and make calls to other contract and non-contract accounts. The smart-contract code is stored once, and later retrievable via its `code_hash`. diff --git a/frame/contracts/src/benchmarking/mod.rs b/frame/contracts/src/benchmarking/mod.rs index 873ba572405ec..84e46e47b609f 100644 --- a/frame/contracts/src/benchmarking/mod.rs +++ b/frame/contracts/src/benchmarking/mod.rs @@ -30,14 +30,20 @@ use self::{ }; use crate::{ exec::{AccountIdOf, Key}, - migration::{codegen::LATEST_MIGRATION_VERSION, v09, v10, v11, v12, v13, MigrationStep}, + migration::{codegen::LATEST_MIGRATION_VERSION, v09, v10, v11, v12, v13, v14, MigrationStep}, wasm::CallFlags, Pallet as Contracts, *, }; use codec::{Encode, MaxEncodedLen}; use frame_benchmarking::v1::{account, benchmarks, whitelisted_caller}; -use frame_support::{pallet_prelude::StorageVersion, weights::Weight}; +use frame_support::{ + self, + pallet_prelude::StorageVersion, + traits::{fungible::InspectHold, Currency}, + weights::Weight, +}; use frame_system::RawOrigin; +use pallet_balances; use sp_runtime::traits::{Bounded, Hash}; use sp_std::prelude::*; use wasm_instrument::parity_wasm::elements::{BlockType, Instruction, ValueType}; @@ -63,8 +69,9 @@ struct Contract { value: BalanceOf, } -impl Contract +impl Contract where + T: Config + pallet_balances::Config, as HasCompact>::Type: Clone + Eq + PartialEq + Debug + TypeInfo + Encode, { /// Create new contract and use a default account id as instantiator. @@ -88,7 +95,7 @@ where data: Vec, ) -> Result, &'static str> { let value = Pallet::::min_balance(); - T::Currency::make_free_balance_be(&caller, caller_funding::()); + T::Currency::set_balance(&caller, caller_funding::()); let salt = vec![0xff]; let addr = Contracts::::contract_address(&caller, &module.hash, &data, &salt); @@ -154,7 +161,7 @@ where /// 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); + T::Currency::set_balance(&self.account_id, balance); } /// Returns `true` iff all storage entries related to code storage exist. @@ -170,7 +177,9 @@ where /// The funding that each account that either calls or instantiates contracts is funded with. fn caller_funding() -> BalanceOf { - BalanceOf::::max_value() / 2u32.into() + // Minting can overflow, so we can't abuse of the funding. This value happens to be big enough, + // but not too big to make the total supply overflow. + BalanceOf::::max_value() / 10_000u32.into() } /// Load the specified contract file from disk by including it into the runtime. @@ -194,6 +203,9 @@ macro_rules! load_benchmark { benchmarks! { where_clause { where as codec::HasCompact>::Type: Clone + Eq + PartialEq + sp_std::fmt::Debug + scale_info::TypeInfo + codec::Encode, + T: Config + pallet_balances::Config, + BalanceOf: From< as Currency>::Balance>, + as Currency>::Balance: From>, } // The base weight consumed on processing contracts deletion queue. @@ -229,8 +241,8 @@ benchmarks! { whitelisted_caller(), WasmModule::dummy(), vec![], )?; - v10::store_old_contract_info::(contract.account_id.clone(), contract.info()?); - let mut m = v10::Migration::::default(); + v10::store_old_contract_info::>(contract.account_id.clone(), contract.info()?); + let mut m = v10::Migration::>::default(); }: { m.step(); } @@ -251,8 +263,11 @@ benchmarks! { #[pov_mode = Measured] v12_migration_step { let c in 0 .. T::MaxCodeLen::get(); - v12::store_old_dummy_code::(c as usize, account::("account", 0, 0)); - let mut m = v12::Migration::::default(); + v12::store_old_dummy_code::< + T, + pallet_balances::Pallet + >(c as usize, account::("account", 0, 0)); + let mut m = v12::Migration::>::default(); }: { m.step(); } @@ -270,6 +285,17 @@ benchmarks! { m.step(); } + // This benchmarks the v14 migration step (Move code owners' reserved balance to be held instead). + #[pov_mode = Measured] + v14_migration_step { + let account = account::("account", 0, 0); + T::Currency::set_balance(&account, caller_funding::()); + v14::store_dummy_code::>(account); + let mut m = v14::Migration::>::default(); + }: { + m.step(); + } + // This benchmarks the weight of executing Migration::migrate to execute a noop migration. #[pov_mode = Measured] migration_noop { @@ -365,22 +391,22 @@ benchmarks! { let salt = vec![42u8; s as usize]; let value = Pallet::::min_balance(); let caller = whitelisted_caller(); - T::Currency::make_free_balance_be(&caller, caller_funding::()); + T::Currency::set_balance(&caller, caller_funding::()); let WasmModule { code, hash, .. } = WasmModule::::sized(c, Location::Call); let origin = RawOrigin::Signed(caller.clone()); let addr = Contracts::::contract_address(&caller, &hash, &input, &salt); }: _(origin, value, Weight::MAX, None, code, input, salt) verify { let deposit_account = Contract::::address_info(&addr)?.deposit_account().clone(); - let deposit = T::Currency::free_balance(&deposit_account); + let deposit = T::Currency::balance(&deposit_account); // uploading the code reserves some balance in the callers account - let code_deposit = T::Currency::reserved_balance(&caller); + let code_deposit = T::Currency::total_balance_on_hold(&caller); assert_eq!( - T::Currency::free_balance(&caller), + T::Currency::balance(&caller), caller_funding::() - value - deposit - code_deposit - Pallet::::min_balance(), ); // contract has the full value - assert_eq!(T::Currency::free_balance(&addr), value + Pallet::::min_balance()); + assert_eq!(T::Currency::balance(&addr), value + Pallet::::min_balance()); } // Instantiate uses a dummy contract constructor to measure the overhead of the instantiate. @@ -394,7 +420,7 @@ benchmarks! { let salt = vec![42u8; s as usize]; let value = Pallet::::min_balance(); let caller = whitelisted_caller(); - T::Currency::make_free_balance_be(&caller, caller_funding::()); + T::Currency::set_balance(&caller, caller_funding::()); let WasmModule { code, hash, .. } = WasmModule::::dummy(); let origin = RawOrigin::Signed(caller.clone()); let addr = Contracts::::contract_address(&caller, &hash, &input, &salt); @@ -402,14 +428,14 @@ benchmarks! { }: _(origin, value, Weight::MAX, None, hash, input, salt) verify { let deposit_account = Contract::::address_info(&addr)?.deposit_account().clone(); - let deposit = T::Currency::free_balance(&deposit_account); + let deposit = T::Currency::balance(&deposit_account); // value was removed from the caller assert_eq!( - T::Currency::free_balance(&caller), + T::Currency::balance(&caller), caller_funding::() - value - deposit - Pallet::::min_balance(), ); // contract has the full value - assert_eq!(T::Currency::free_balance(&addr), value + Pallet::::min_balance()); + assert_eq!(T::Currency::balance(&addr), value + Pallet::::min_balance()); } // We just call a dummy contract to measure the overhead of the call extrinsic. @@ -429,18 +455,18 @@ benchmarks! { let value = Pallet::::min_balance(); let origin = RawOrigin::Signed(instance.caller.clone()); let callee = instance.addr.clone(); - let before = T::Currency::free_balance(&instance.account_id); - let before_deposit = T::Currency::free_balance(&deposit_account); + let before = T::Currency::balance(&instance.account_id); + let before_deposit = T::Currency::balance(&deposit_account); }: _(origin, callee, value, Weight::MAX, None, data) verify { - let deposit = T::Currency::free_balance(&deposit_account); + let deposit = T::Currency::balance(&deposit_account); // value and value transferred via call should be removed from the caller assert_eq!( - T::Currency::free_balance(&instance.caller), + T::Currency::balance(&instance.caller), caller_funding::() - instance.value - value - deposit - Pallet::::min_balance(), ); // contract should have received the value - assert_eq!(T::Currency::free_balance(&instance.account_id), before + value); + assert_eq!(T::Currency::balance(&instance.account_id), before + value); // contract should still exist instance.info()?; } @@ -452,13 +478,13 @@ benchmarks! { upload_code { let c in 0 .. T::MaxCodeLen::get(); let caller = whitelisted_caller(); - T::Currency::make_free_balance_be(&caller, caller_funding::()); + T::Currency::set_balance(&caller, caller_funding::()); let WasmModule { code, hash, .. } = WasmModule::::sized(c, Location::Call); let origin = RawOrigin::Signed(caller.clone()); }: _(origin, code, None, Determinism::Enforced) verify { // uploading the code reserves some balance in the callers account - assert!(T::Currency::reserved_balance(&caller) > 0u32.into()); + assert!(T::Currency::total_balance_on_hold(&caller) > 0u32.into()); assert!(>::code_exists(&hash)); } @@ -468,17 +494,17 @@ benchmarks! { #[pov_mode = Measured] remove_code { let caller = whitelisted_caller(); - T::Currency::make_free_balance_be(&caller, caller_funding::()); + T::Currency::set_balance(&caller, caller_funding::()); let WasmModule { code, hash, .. } = WasmModule::::dummy(); let origin = RawOrigin::Signed(caller.clone()); let uploaded = >::bare_upload_code(caller.clone(), code, None, Determinism::Enforced)?; assert_eq!(uploaded.code_hash, hash); - assert_eq!(uploaded.deposit, T::Currency::reserved_balance(&caller)); + assert_eq!(uploaded.deposit, T::Currency::total_balance_on_hold(&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_eq!(T::Currency::total_balance_on_hold(&caller), 0u32.into()); assert!(>::code_removed(&hash)); } @@ -850,12 +876,15 @@ benchmarks! { let beneficiary = account::("beneficiary", 0, 0); let beneficiary_bytes = beneficiary.encode(); let beneficiary_len = beneficiary_bytes.len(); + let caller = whitelisted_caller(); + + T::Currency::set_balance(&caller, caller_funding::()); // Maximize the delegate_dependencies to account for the worst-case scenario. let code_hashes = (0..T::MaxDelegateDependencies::get()) .map(|i| { let new_code = WasmModule::::dummy_with_bytes(65 + i); - Contracts::::store_code_raw(new_code.code, whitelisted_caller())?; + Contracts::::store_code_raw(new_code.code, caller.clone())?; Ok(new_code.hash) }) .collect::, &'static str>>()?; @@ -902,15 +931,15 @@ benchmarks! { let instance = Contract::::new(code, vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); let deposit_account = instance.info()?.deposit_account().clone(); - assert_eq!(>::total_balance(&beneficiary), 0u32.into()); - assert_eq!(T::Currency::free_balance(&instance.account_id), Pallet::::min_balance() * 2u32.into()); - assert_ne!(T::Currency::free_balance(&deposit_account), 0u32.into()); + assert_eq!(T::Currency::total_balance(&beneficiary), 0u32.into()); + assert_eq!(T::Currency::balance(&instance.account_id), Pallet::::min_balance() * 2u32.into()); + assert_ne!(T::Currency::balance(&deposit_account), 0u32.into()); }: call(origin, instance.addr.clone(), 0u32.into(), Weight::MAX, None, vec![]) verify { if r > 0 { - assert_eq!(>::total_balance(&instance.account_id), 0u32.into()); - assert_eq!(>::total_balance(&deposit_account), 0u32.into()); - assert_eq!(>::total_balance(&beneficiary), Pallet::::min_balance() * 2u32.into()); + assert_eq!(T::Currency::total_balance(&instance.account_id), 0u32.into()); + assert_eq!(T::Currency::total_balance(&deposit_account), 0u32.into()); + assert_eq!(T::Currency::total_balance(&beneficiary), Pallet::::min_balance() * 2u32.into()); } } @@ -1679,12 +1708,12 @@ benchmarks! { instance.set_balance(value * (r + 1).into()); let origin = RawOrigin::Signed(instance.caller.clone()); for account in &accounts { - assert_eq!(>::total_balance(account), 0u32.into()); + assert_eq!(T::Currency::total_balance(account), 0u32.into()); } }: call(origin, instance.addr, 0u32.into(), Weight::MAX, None, vec![]) verify { for account in &accounts { - assert_eq!(>::total_balance(account), value); + assert_eq!(T::Currency::total_balance(account), value); } } @@ -1763,7 +1792,7 @@ benchmarks! { }); let instance = Contract::::new(code, vec![])?; let origin = RawOrigin::Signed(instance.caller.clone()); - }: call(origin, instance.addr, 0u32.into(), Weight::MAX, Some(BalanceOf::::from(u32::MAX).into()), vec![]) + }: call(origin, instance.addr, 0u32.into(), Weight::MAX, Some(BalanceOf::::from(u32::MAX.into()).into()), vec![]) // This is a slow call: We redeuce the number of runs. #[pov_mode = Measured] @@ -1772,7 +1801,9 @@ benchmarks! { let hashes = (0..r) .map(|i| { let code = WasmModule::::dummy_with_bytes(i); - Contracts::::store_code_raw(code.code, whitelisted_caller())?; + let caller = whitelisted_caller(); + T::Currency::set_balance(&caller, caller_funding::()); + Contracts::::store_code_raw(code.code, caller)?; Ok(code.hash) }) .collect::, &'static str>>()?; @@ -1892,7 +1923,9 @@ benchmarks! { ])), .. Default::default() }); - Contracts::::store_code_raw(code.code, whitelisted_caller())?; + let caller = whitelisted_caller(); + T::Currency::set_balance(&caller, caller_funding::()); + Contracts::::store_code_raw(code.code, caller)?; Ok(code.hash) }) .collect::, &'static str>>()?; @@ -1996,7 +2029,9 @@ benchmarks! { let hash = callee_code.hash; let hash_bytes = callee_code.hash.encode(); let hash_len = hash_bytes.len(); - Contracts::::store_code_raw(callee_code.code, whitelisted_caller())?; + let caller = whitelisted_caller(); + T::Currency::set_balance(&caller, caller_funding::()); + Contracts::::store_code_raw(callee_code.code, caller)?; let value: BalanceOf = t.into(); let value_bytes = value.encode(); @@ -2338,7 +2373,9 @@ benchmarks! { let code_hashes = (0..r) .map(|i| { let new_code = WasmModule::::dummy_with_bytes(i); - Contracts::::store_code_raw(new_code.code, whitelisted_caller())?; + let caller = whitelisted_caller(); + T::Currency::set_balance(&caller, caller_funding::()); + Contracts::::store_code_raw(new_code.code, caller)?; Ok(new_code.hash) }) .collect::, &'static str>>()?; @@ -2379,7 +2416,9 @@ benchmarks! { let code_hashes = (0..r) .map(|i| { let new_code = WasmModule::::dummy_with_bytes(65 + i); - Contracts::::store_code_raw(new_code.code, whitelisted_caller())?; + let caller = whitelisted_caller(); + T::Currency::set_balance(&caller, caller_funding::()); + Contracts::::store_code_raw(new_code.code, caller)?; Ok(new_code.hash) }) .collect::, &'static str>>()?; @@ -2415,7 +2454,9 @@ benchmarks! { let code_hashes = (0..r) .map(|i| { let new_code = WasmModule::::dummy_with_bytes(65 + i); - Contracts::::store_code_raw(new_code.code, whitelisted_caller())?; + let caller = whitelisted_caller(); + T::Currency::set_balance(&caller, caller_funding::()); + Contracts::::store_code_raw(new_code.code, caller)?; Ok(new_code.hash) }) .collect::, &'static str>>()?; diff --git a/frame/contracts/src/exec.rs b/frame/contracts/src/exec.rs index 97fdd17e21aa8..6203b31f67a5b 100644 --- a/frame/contracts/src/exec.rs +++ b/frame/contracts/src/exec.rs @@ -30,8 +30,9 @@ use frame_support::{ ensure, storage::{with_transaction, TransactionOutcome}, traits::{ - tokens::{Fortitude::Polite, Preservation::Expendable}, - Contains, Currency, ExistenceRequirement, OriginTrait, Randomness, Time, + fungible::{Inspect, Mutate}, + tokens::{Fortitude::Polite, Preservation}, + Contains, OriginTrait, Randomness, Time, }, weights::Weight, Blake2_128Concat, BoundedVec, StorageHasher, @@ -1100,13 +1101,15 @@ where /// Transfer some funds from `from` to `to`. fn transfer( - existence_requirement: ExistenceRequirement, + preservation: Preservation, from: &T::AccountId, to: &T::AccountId, value: BalanceOf, ) -> DispatchResult { - T::Currency::transfer(from, to, value, existence_requirement) - .map_err(|_| Error::::TransferFailed)?; + if !value.is_zero() && from != to { + T::Currency::transfer(from, to, value, preservation) + .map_err(|_| Error::::TransferFailed)?; + } Ok(()) } @@ -1130,7 +1133,7 @@ where Origin::Root if value.is_zero() => return Ok(()), Origin::Root => return DispatchError::RootNotAllowed.into(), }; - Self::transfer(ExistenceRequirement::KeepAlive, &caller, &frame.account_id, value) + Self::transfer(Preservation::Preserve, &caller, &frame.account_id, value) } /// Reference to the current (top) frame. @@ -1278,7 +1281,6 @@ where } fn terminate(&mut self, beneficiary: &AccountIdOf) -> Result<(), DispatchError> { - use frame_support::traits::fungible::Inspect; if self.is_recursive() { return Err(Error::::TerminatedWhileReentrant.into()) } @@ -1286,11 +1288,11 @@ where let info = frame.terminate(); frame.nested_storage.terminate(&info); System::::dec_consumers(&frame.account_id); - T::Currency::transfer( + Self::transfer( + Preservation::Expendable, &frame.account_id, beneficiary, - T::Currency::reducible_balance(&frame.account_id, Expendable, Polite), - ExistenceRequirement::AllowDeath, + T::Currency::reducible_balance(&frame.account_id, Preservation::Expendable, Polite), )?; info.queue_trie_for_deletion(); ContractInfoOf::::remove(&frame.account_id); @@ -1314,7 +1316,7 @@ where } fn transfer(&mut self, to: &T::AccountId, value: BalanceOf) -> DispatchResult { - Self::transfer(ExistenceRequirement::KeepAlive, &self.top_frame().account_id, to, value) + Self::transfer(Preservation::Preserve, &self.top_frame().account_id, to, value) } fn get_storage(&mut self, key: &Key) -> Option> { @@ -1377,7 +1379,7 @@ where } fn balance(&self) -> BalanceOf { - T::Currency::free_balance(&self.top_frame().account_id) + T::Currency::balance(&self.top_frame().account_id) } fn value_transferred(&self) -> BalanceOf { @@ -1808,7 +1810,7 @@ mod tests { set_balance(&origin, 100); set_balance(&dest, 0); - MockStack::transfer(ExistenceRequirement::KeepAlive, &origin, &dest, 55).unwrap(); + MockStack::transfer(Preservation::Preserve, &origin, &dest, 55).unwrap(); assert_eq!(get_balance(&origin), 45); assert_eq!(get_balance(&dest), 55); @@ -1946,7 +1948,7 @@ mod tests { ExtBuilder::default().build().execute_with(|| { set_balance(&origin, 0); - let result = MockStack::transfer(ExistenceRequirement::KeepAlive, &origin, &dest, 100); + let result = MockStack::transfer(Preservation::Preserve, &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 59457ec78815c..158e4802cf186 100644 --- a/frame/contracts/src/lib.rs +++ b/frame/contracts/src/lib.rs @@ -25,10 +25,10 @@ //! //! ## Overview //! -//! This module extends accounts based on the [`Currency`] trait to have smart-contract -//! functionality. It can be used with other modules that implement accounts based on [`Currency`]. -//! These "smart-contract accounts" have the ability to instantiate smart-contracts and make calls -//! to other contract and non-contract accounts. +//! This module extends accounts based on the [`frame_support::traits::fungible`] traits to have +//! smart-contract functionality. It can be used with other modules that implement accounts based on +//! the [`frame_support::traits::fungible`] traits. These "smart-contract accounts" have the ability +//! to instantiate smart-contracts and make calls to other contract and non-contract accounts. //! //! The smart-contract code is stored once, and later retrievable via its hash. //! This means that multiple smart-contracts can be instantiated from the same hash, without @@ -117,8 +117,8 @@ use frame_support::{ ensure, error::BadOrigin, traits::{ - tokens::fungible::Inspect, ConstU32, Contains, Currency, Get, Randomness, - ReservableCurrency, Time, + fungible::{Inspect, Mutate, MutateHold}, + ConstU32, Contains, Get, Randomness, Time, }, weights::Weight, BoundedVec, RuntimeDebugNoBound, @@ -133,7 +133,6 @@ use scale_info::TypeInfo; use smallvec::Array; use sp_runtime::traits::{Convert, Hash, Saturating, StaticLookup, Zero}; use sp_std::{fmt::Debug, prelude::*}; -pub use weights::WeightInfo; pub use crate::{ address::{AddressGenerator, DefaultAddressGenerator}, @@ -143,6 +142,7 @@ pub use crate::{ schedule::{HostFnWeights, InstructionWeights, Limits, Schedule}, wasm::Determinism, }; +pub use weights::WeightInfo; #[cfg(doc)] pub use crate::wasm::api_doc; @@ -150,7 +150,7 @@ pub use crate::wasm::api_doc; type CodeHash = ::Hash; type TrieId = BoundedVec>; type BalanceOf = - <::Currency as Currency<::AccountId>>::Balance; + <::Currency as Inspect<::AccountId>>::Balance; type CodeVec = BoundedVec::MaxCodeLen>; type AccountIdLookupOf = <::Lookup as StaticLookup>::Source; type DebugBufferVec = BoundedVec::MaxDebugBufferLen>; @@ -186,7 +186,7 @@ pub mod pallet { use sp_runtime::Perbill; /// The current storage version. - pub(crate) const STORAGE_VERSION: StorageVersion = StorageVersion::new(13); + pub(crate) const STORAGE_VERSION: StorageVersion = StorageVersion::new(14); #[pallet::pallet] #[pallet::storage_version(STORAGE_VERSION)] @@ -207,9 +207,10 @@ pub mod pallet { /// to supply a dummy implementation for this type (because it is never used). type Randomness: Randomness>; - /// The currency in which fees are paid and contract balances are held. - type Currency: ReservableCurrency // TODO: Move to fungible traits - + Inspect>; + /// The fungible in which fees are paid and contract balances are held. + type Currency: Inspect + + Mutate + + MutateHold; /// The overarching event type. type RuntimeEvent: From> + IsType<::RuntimeEvent>; @@ -329,20 +330,25 @@ pub mod pallet { #[pallet::constant] type MaxDebugBufferLen: Get; + /// Overarching hold reason. + type RuntimeHoldReason: From; + /// The sequence of migration steps that will be applied during a migration. /// /// # Examples /// ``` /// use pallet_contracts::migration::{v10, v11}; /// # struct Runtime {}; - /// type Migrations = (v10::Migration, v11::Migration); + /// # struct Currency {}; + /// type Migrations = (v10::Migration, v11::Migration); /// ``` /// /// If you have a single migration step, you can use a tuple with a single element: /// ``` /// use pallet_contracts::migration::v10; /// # struct Runtime {}; - /// type Migrations = (v10::Migration,); + /// # struct Currency {}; + /// type Migrations = (v10::Migration,); /// ``` type Migrations: MigrateSequence; } @@ -832,7 +838,7 @@ pub mod pallet { }, /// Code with the specified hash has been stored. - CodeStored { code_hash: T::Hash }, + CodeStored { code_hash: T::Hash, deposit_held: BalanceOf, uploader: T::AccountId }, /// A custom event emitted by the contract. ContractEmitted { @@ -844,7 +850,7 @@ pub mod pallet { }, /// A code with the specified hash was removed. - CodeRemoved { code_hash: T::Hash }, + CodeRemoved { code_hash: T::Hash, deposit_released: BalanceOf, remover: T::AccountId }, /// A contract's code was updated. ContractCodeUpdated { @@ -979,6 +985,13 @@ pub mod pallet { CannotAddSelfAsDelegateDependency, } + /// A reason for the pallet contracts placing a hold on funds. + #[pallet::composite_enum] + pub enum HoldReason { + /// The Pallet has reserved it for storing code on-chain. + CodeUploadDepositReserve, + } + /// A mapping from a contract's code hash to its code. #[pallet::storage] pub(crate) type PristineCode = StorageMap<_, Identity, CodeHash, CodeVec>; diff --git a/frame/contracts/src/migration.rs b/frame/contracts/src/migration.rs index cfa5ce86dcf6c..106b68dc4416e 100644 --- a/frame/contracts/src/migration.rs +++ b/frame/contracts/src/migration.rs @@ -33,7 +33,8 @@ //! ``` //! use pallet_contracts::migration::{v10, v11}; //! # pub enum Runtime {}; -//! type Migrations = (v10::Migration, v11::Migration); +//! # struct Currency; +//! type Migrations = (v10::Migration, v11::Migration); //! ``` //! //! ## Notes: @@ -61,6 +62,7 @@ pub mod v10; pub mod v11; pub mod v12; pub mod v13; +pub mod v14; include!(concat!(env!("OUT_DIR"), "/migration_codegen.rs")); use crate::{weights::WeightInfo, Config, Error, MigrationInProgress, Pallet, Weight, LOG_TARGET}; diff --git a/frame/contracts/src/migration/v10.rs b/frame/contracts/src/migration/v10.rs index 40e007bf710ba..3fc7cabe94942 100644 --- a/frame/contracts/src/migration/v10.rs +++ b/frame/contracts/src/migration/v10.rs @@ -23,7 +23,7 @@ use crate::{ exec::AccountIdOf, migration::{IsFinished, MigrationStep}, weights::WeightInfo, - BalanceOf, CodeHash, Config, Pallet, TrieId, Weight, LOG_TARGET, + CodeHash, Config, Pallet, TrieId, Weight, LOG_TARGET, }; use codec::{Decode, Encode}; use core::cmp::{max, min}; @@ -32,9 +32,8 @@ use frame_support::{ pallet_prelude::*, storage_alias, traits::{ - fungible::Inspect, - tokens::{Fortitude::Polite, Preservation::Preserve}, - Currency, ExistenceRequirement, ReservableCurrency, + tokens::{fungible::Inspect, Fortitude::Polite, Preservation::Preserve}, + ExistenceRequirement, ReservableCurrency, }, DefaultNoBound, }; @@ -47,29 +46,41 @@ use sp_std::{ops::Deref, prelude::*}; mod old { use super::*; + pub type BalanceOf = ::AccountId, + >>::Balance; + #[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)] - #[scale_info(skip_type_params(T))] - pub struct ContractInfo { + #[scale_info(skip_type_params(T, OldCurrency))] + pub struct ContractInfo + where + OldCurrency: ReservableCurrency<::AccountId>, + { pub trie_id: TrieId, pub code_hash: CodeHash, pub storage_bytes: u32, pub storage_items: u32, - pub storage_byte_deposit: BalanceOf, - pub storage_item_deposit: BalanceOf, - pub storage_base_deposit: BalanceOf, + pub storage_byte_deposit: BalanceOf, + pub storage_item_deposit: BalanceOf, + pub storage_base_deposit: BalanceOf, } #[storage_alias] - pub type ContractInfoOf = StorageMap< + pub type ContractInfoOf = StorageMap< Pallet, Twox64Concat, ::AccountId, - ContractInfo, + ContractInfo, >; } #[cfg(feature = "runtime-benchmarks")] -pub fn store_old_contract_info(account: T::AccountId, info: crate::ContractInfo) { +pub fn store_old_contract_info( + account: T::AccountId, + info: crate::ContractInfo, +) where + OldCurrency: ReservableCurrency<::AccountId> + 'static, +{ let info = old::ContractInfo { trie_id: info.trie_id, code_hash: info.code_hash, @@ -79,7 +90,7 @@ pub fn store_old_contract_info(account: T::AccountId, info: crate::Co storage_item_deposit: Default::default(), storage_base_deposit: Default::default(), }; - old::ContractInfoOf::::insert(account, info); + old::ContractInfoOf::::insert(account, info); } #[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebugNoBound, TypeInfo, MaxEncodedLen)] @@ -95,28 +106,40 @@ impl Deref for DepositAccount { } #[derive(Encode, Decode, Clone, PartialEq, Eq, RuntimeDebug, TypeInfo, MaxEncodedLen)] -#[scale_info(skip_type_params(T))] -pub struct ContractInfo { +#[scale_info(skip_type_params(T, OldCurrency))] +pub struct ContractInfo +where + OldCurrency: ReservableCurrency<::AccountId>, +{ pub trie_id: TrieId, deposit_account: DepositAccount, pub code_hash: CodeHash, storage_bytes: u32, storage_items: u32, - pub storage_byte_deposit: BalanceOf, - storage_item_deposit: BalanceOf, - storage_base_deposit: BalanceOf, + pub storage_byte_deposit: old::BalanceOf, + storage_item_deposit: old::BalanceOf, + storage_base_deposit: old::BalanceOf, } #[derive(Encode, Decode, MaxEncodedLen, DefaultNoBound)] -pub struct Migration { +pub struct Migration { last_account: Option, + _phantom: PhantomData<(T, OldCurrency)>, } #[storage_alias] -type ContractInfoOf = - StorageMap, Twox64Concat, ::AccountId, ContractInfo>; - -impl MigrationStep for Migration { +type ContractInfoOf = StorageMap< + Pallet, + Twox64Concat, + ::AccountId, + ContractInfo, +>; + +impl MigrationStep for Migration +where + OldCurrency: ReservableCurrency<::AccountId> + + Inspect<::AccountId, Balance = old::BalanceOf>, +{ const VERSION: u16 = 10; fn max_step_weight() -> Weight { @@ -125,15 +148,17 @@ impl MigrationStep for Migration { fn step(&mut self) -> (IsFinished, Weight) { let mut iter = if let Some(last_account) = self.last_account.take() { - old::ContractInfoOf::::iter_from(old::ContractInfoOf::::hashed_key_for( - last_account, - )) + old::ContractInfoOf::::iter_from( + old::ContractInfoOf::::hashed_key_for(last_account), + ) } else { - old::ContractInfoOf::::iter() + old::ContractInfoOf::::iter() }; if let Some((account, contract)) = iter.next() { - let min_balance = Pallet::::min_balance(); + let min_balance = ::AccountId, + >>::minimum_balance(); log::debug!(target: LOG_TARGET, "Account: 0x{} ", HexDisplay::from(&account.encode())); // Get the new deposit account address @@ -148,7 +173,7 @@ impl MigrationStep for Migration { // Unreserve the existing deposit // Note we can't use repatriate_reserve, because it only works with existing accounts - let remaining = T::Currency::unreserve(&account, old_deposit); + let remaining = OldCurrency::unreserve(&account, old_deposit); if !remaining.is_zero() { log::warn!( target: LOG_TARGET, @@ -161,9 +186,9 @@ impl MigrationStep for Migration { // Attempt to transfer the old deposit to the deposit account. let amount = old_deposit .saturating_sub(min_balance) - .min(T::Currency::reducible_balance(&account, Preserve, Polite)); + .min(OldCurrency::reducible_balance(&account, Preserve, Polite)); - let new_deposit = T::Currency::transfer( + let new_deposit = OldCurrency::transfer( &account, &deposit_account, amount, @@ -184,7 +209,7 @@ impl MigrationStep for Migration { "Failed to transfer the base deposit, reason: {:?}", err ); - T::Currency::deposit_creating(&deposit_account, min_balance); + OldCurrency::deposit_creating(&deposit_account, min_balance); min_balance }); @@ -224,7 +249,7 @@ impl MigrationStep for Migration { storage_base_deposit, }; - ContractInfoOf::::insert(&account, new_contract_info); + ContractInfoOf::::insert(&account, new_contract_info); // Store last key for next migration step self.last_account = Some(account); @@ -238,7 +263,7 @@ impl MigrationStep for Migration { #[cfg(feature = "try-runtime")] fn pre_upgrade_step() -> Result, TryRuntimeError> { - let sample: Vec<_> = old::ContractInfoOf::::iter().take(10).collect(); + let sample: Vec<_> = old::ContractInfoOf::::iter().take(10).collect(); log::debug!(target: LOG_TARGET, "Taking sample of {} contracts", sample.len()); Ok(sample.encode()) @@ -246,23 +271,24 @@ impl MigrationStep for Migration { #[cfg(feature = "try-runtime")] fn post_upgrade_step(state: Vec) -> Result<(), TryRuntimeError> { - let sample = )> as Decode>::decode(&mut &state[..]) - .expect("pre_upgrade_step provides a valid state; qed"); + let sample = )> as Decode>::decode( + &mut &state[..], + ) + .expect("pre_upgrade_step provides a valid state; qed"); log::debug!(target: LOG_TARGET, "Validating sample of {} contracts", sample.len()); for (account, old_contract) in sample { log::debug!(target: LOG_TARGET, "==="); log::debug!(target: LOG_TARGET, "Account: 0x{} ", HexDisplay::from(&account.encode())); - let contract = ContractInfoOf::::get(&account).unwrap(); + let contract = ContractInfoOf::::get(&account).unwrap(); ensure!(old_contract.trie_id == contract.trie_id, "invalid trie_id"); ensure!(old_contract.code_hash == contract.code_hash, "invalid code_hash"); ensure!(old_contract.storage_bytes == contract.storage_bytes, "invalid storage_bytes"); ensure!(old_contract.storage_items == contract.storage_items, "invalid storage_items"); - let deposit = - <::Currency as frame_support::traits::Currency<_>>::total_balance( - &contract.deposit_account, - ); + let deposit = >::total_balance( + &contract.deposit_account, + ); ensure!( deposit == contract diff --git a/frame/contracts/src/migration/v12.rs b/frame/contracts/src/migration/v12.rs index 4fb7ca76fea92..75018f943d100 100644 --- a/frame/contracts/src/migration/v12.rs +++ b/frame/contracts/src/migration/v12.rs @@ -37,13 +37,20 @@ use sp_std::prelude::*; mod old { use super::*; + pub type BalanceOf = ::AccountId, + >>::Balance; + #[derive(Encode, Decode, scale_info::TypeInfo, MaxEncodedLen)] #[codec(mel_bound())] - #[scale_info(skip_type_params(T))] - pub struct OwnerInfo { + #[scale_info(skip_type_params(T, OldCurrency))] + pub struct OwnerInfo + where + OldCurrency: ReservableCurrency<::AccountId>, + { pub owner: AccountIdOf, #[codec(compact)] - pub deposit: BalanceOf, + pub deposit: BalanceOf, #[codec(compact)] pub refcount: u64, } @@ -63,7 +70,8 @@ mod old { } #[storage_alias] - pub type OwnerInfoOf = StorageMap, Identity, CodeHash, OwnerInfo>; + pub type OwnerInfoOf = + StorageMap, Identity, CodeHash, OwnerInfo>; #[storage_alias] pub type CodeStorage = @@ -72,11 +80,14 @@ mod old { #[derive(Encode, Decode, scale_info::TypeInfo, MaxEncodedLen)] #[codec(mel_bound())] -#[scale_info(skip_type_params(T))] -pub struct CodeInfo { +#[scale_info(skip_type_params(T, OldCurrency))] +pub struct CodeInfo +where + OldCurrency: ReservableCurrency<::AccountId>, +{ owner: AccountIdOf, #[codec(compact)] - deposit: BalanceOf, + deposit: old::BalanceOf, #[codec(compact)] refcount: u64, determinism: Determinism, @@ -84,13 +95,17 @@ pub struct CodeInfo { } #[storage_alias] -pub type CodeInfoOf = StorageMap, Twox64Concat, CodeHash, CodeInfo>; +pub type CodeInfoOf = + StorageMap, Twox64Concat, CodeHash, CodeInfo>; #[storage_alias] pub type PristineCode = StorageMap, Identity, CodeHash, Vec>; #[cfg(feature = "runtime-benchmarks")] -pub fn store_old_dummy_code(len: usize, account: T::AccountId) { +pub fn store_old_dummy_code(len: usize, account: T::AccountId) +where + OldCurrency: ReservableCurrency<::AccountId> + 'static, +{ use sp_runtime::traits::Hash; let code = vec![42u8; len]; @@ -107,15 +122,24 @@ pub fn store_old_dummy_code(len: usize, account: T::AccountId) { old::CodeStorage::::insert(hash, module); let info = old::OwnerInfo { owner: account, deposit: u32::MAX.into(), refcount: u64::MAX }; - old::OwnerInfoOf::::insert(hash, info); + old::OwnerInfoOf::::insert(hash, info); } #[derive(Encode, Decode, MaxEncodedLen, DefaultNoBound)] -pub struct Migration { +pub struct Migration +where + OldCurrency: ReservableCurrency<::AccountId>, + OldCurrency::Balance: From>, +{ last_code_hash: Option>, + _phantom: PhantomData, } -impl MigrationStep for Migration { +impl MigrationStep for Migration +where + OldCurrency: ReservableCurrency<::AccountId> + 'static, + OldCurrency::Balance: From>, +{ const VERSION: u16 = 12; fn max_step_weight() -> Weight { @@ -124,9 +148,11 @@ impl MigrationStep for Migration { fn step(&mut self) -> (IsFinished, Weight) { let mut iter = if let Some(last_key) = self.last_code_hash.take() { - old::OwnerInfoOf::::iter_from(old::OwnerInfoOf::::hashed_key_for(last_key)) + old::OwnerInfoOf::::iter_from( + old::OwnerInfoOf::::hashed_key_for(last_key), + ) } else { - old::OwnerInfoOf::::iter() + old::OwnerInfoOf::::iter() }; if let Some((hash, old_info)) = iter.next() { log::debug!(target: LOG_TARGET, "Migrating OwnerInfo for code_hash {:?}", hash); @@ -158,7 +184,8 @@ impl MigrationStep for Migration { let bytes_before = module .encoded_size() .saturating_add(code_len) - .saturating_add(old::OwnerInfo::::max_encoded_len()) as u32; + .saturating_add(old::OwnerInfo::::max_encoded_len()) + as u32; let items_before = 3u32; let deposit_expected_before = price_per_byte .saturating_mul(bytes_before.into()) @@ -166,24 +193,25 @@ impl MigrationStep for Migration { let ratio = FixedU128::checked_from_rational(old_info.deposit, deposit_expected_before) .unwrap_or_default() .min(FixedU128::from_u32(1)); - let bytes_after = code_len.saturating_add(CodeInfo::::max_encoded_len()) as u32; + let bytes_after = + code_len.saturating_add(CodeInfo::::max_encoded_len()) as u32; let items_after = 2u32; let deposit_expected_after = price_per_byte .saturating_mul(bytes_after.into()) .saturating_add(price_per_item.saturating_mul(items_after.into())); let deposit = ratio.saturating_mul_int(deposit_expected_after); - let info = CodeInfo:: { + let info = CodeInfo:: { determinism: module.determinism, owner: old_info.owner, - deposit, + deposit: deposit.into(), refcount: old_info.refcount, code_len: code_len as u32, }; let amount = old_info.deposit.saturating_sub(info.deposit); if !amount.is_zero() { - T::Currency::unreserve(&info.owner, amount); + OldCurrency::unreserve(&info.owner, amount); log::debug!( target: LOG_TARGET, "Deposit refunded: {:?} Balance, to: {:?}", @@ -198,7 +226,7 @@ impl MigrationStep for Migration { &old_info.deposit ); } - CodeInfoOf::::insert(hash, info); + CodeInfoOf::::insert(hash, info); self.last_code_hash = Some(hash); @@ -213,12 +241,12 @@ impl MigrationStep for Migration { fn pre_upgrade_step() -> Result, TryRuntimeError> { let len = 100; log::debug!(target: LOG_TARGET, "Taking sample of {} OwnerInfo(s)", len); - let sample: Vec<_> = old::OwnerInfoOf::::iter() + let sample: Vec<_> = old::OwnerInfoOf::::iter() .take(len) .map(|(k, v)| { let module = old::CodeStorage::::get(k) .expect("No PrefabWasmModule found for code_hash: {:?}"); - let info: CodeInfo = CodeInfo { + let info: CodeInfo = CodeInfo { determinism: module.determinism, deposit: v.deposit, refcount: v.refcount, @@ -231,22 +259,24 @@ impl MigrationStep for Migration { let storage: u32 = old::CodeStorage::::iter().map(|(_k, v)| v.encoded_size() as u32).sum(); - let mut deposit: BalanceOf = Default::default(); - old::OwnerInfoOf::::iter().for_each(|(_k, v)| deposit += v.deposit); + let mut deposit: old::BalanceOf = Default::default(); + old::OwnerInfoOf::::iter().for_each(|(_k, v)| deposit += v.deposit); Ok((sample, deposit, storage).encode()) } #[cfg(feature = "try-runtime")] fn post_upgrade_step(state: Vec) -> Result<(), TryRuntimeError> { - let state = <(Vec<(CodeHash, CodeInfo)>, BalanceOf, u32) as Decode>::decode( - &mut &state[..], - ) + let state = <( + Vec<(CodeHash, CodeInfo)>, + old::BalanceOf, + u32, + ) as Decode>::decode(&mut &state[..]) .unwrap(); log::debug!(target: LOG_TARGET, "Validating state of {} Codeinfo(s)", state.0.len()); for (hash, old) in state.0 { - let info = CodeInfoOf::::get(&hash) + let info = CodeInfoOf::::get(&hash) .expect(format!("CodeInfo for code_hash {:?} not found!", hash).as_str()); ensure!(info.determinism == old.determinism, "invalid determinism"); ensure!(info.owner == old.owner, "invalid owner"); @@ -262,7 +292,7 @@ impl MigrationStep for Migration { } else { log::debug!(target: LOG_TARGET, "CodeStorage is empty."); } - if let Some((k, _)) = old::OwnerInfoOf::::iter().next() { + if let Some((k, _)) = old::OwnerInfoOf::::iter().next() { log::warn!( target: LOG_TARGET, "OwnerInfoOf is still NOT empty, found code_hash: {:?}", @@ -272,10 +302,10 @@ impl MigrationStep for Migration { log::debug!(target: LOG_TARGET, "OwnerInfoOf is empty."); } - let mut deposit: BalanceOf = Default::default(); + let mut deposit: old::BalanceOf = Default::default(); let mut items = 0u32; let mut storage_info = 0u32; - CodeInfoOf::::iter().for_each(|(_k, v)| { + CodeInfoOf::::iter().for_each(|(_k, v)| { deposit += v.deposit; items += 1; storage_info += v.encoded_size() as u32; diff --git a/frame/contracts/src/migration/v14.rs b/frame/contracts/src/migration/v14.rs new file mode 100644 index 0000000000000..ebf97af562ed4 --- /dev/null +++ b/frame/contracts/src/migration/v14.rs @@ -0,0 +1,269 @@ +// 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. + +//! Update the code owner balance, make the storage deposit reserved balance to be held instead. + +use crate::{ + exec::AccountIdOf, + migration::{IsFinished, MigrationStep}, + weights::WeightInfo, + BalanceOf, CodeHash, Config, Determinism, HoldReason, Pallet, Weight, LOG_TARGET, +}; +use codec::{Decode, Encode}; +#[cfg(feature = "try-runtime")] +use environmental::Vec; +#[cfg(feature = "try-runtime")] +use frame_support::traits::fungible::{Inspect, InspectHold}; +use frame_support::{ + codec, + pallet_prelude::*, + storage_alias, + traits::{fungible::MutateHold, ReservableCurrency}, + DefaultNoBound, +}; +use sp_core::hexdisplay::HexDisplay; +#[cfg(feature = "try-runtime")] +use sp_runtime::TryRuntimeError; +use sp_runtime::{traits::Zero, Saturating}; +#[cfg(feature = "try-runtime")] +use sp_std::collections::btree_map::BTreeMap; + +mod old { + use super::*; + + pub type BalanceOf = ::AccountId, + >>::Balance; + + #[derive(Encode, Decode, scale_info::TypeInfo, MaxEncodedLen)] + #[codec(mel_bound())] + #[scale_info(skip_type_params(T, OldCurrency))] + pub struct CodeInfo + where + T: Config, + OldCurrency: ReservableCurrency<::AccountId>, + { + pub owner: AccountIdOf, + #[codec(compact)] + pub deposit: old::BalanceOf, + #[codec(compact)] + pub refcount: u64, + pub determinism: Determinism, + pub code_len: u32, + } + + #[storage_alias] + pub type CodeInfoOf = + StorageMap, Twox64Concat, CodeHash, CodeInfo>; +} + +#[cfg(feature = "runtime-benchmarks")] +pub fn store_dummy_code(account: T::AccountId) +where + T: Config, + OldCurrency: ReservableCurrency<::AccountId> + 'static, +{ + use sp_runtime::traits::Hash; + use sp_std::vec; + + let len = T::MaxCodeLen::get(); + let code = vec![42u8; len as usize]; + let hash = T::Hashing::hash(&code); + + let info = old::CodeInfo { + owner: account, + deposit: 10_000u32.into(), + refcount: u64::MAX, + determinism: Determinism::Enforced, + code_len: len, + }; + old::CodeInfoOf::::insert(hash, info); +} + +#[cfg(feature = "try-runtime")] +#[derive(Encode, Decode)] +/// Accounts for the balance allocation of a code owner. +struct BalanceAllocation +where + T: Config, + OldCurrency: ReservableCurrency<::AccountId>, +{ + /// Total reserved balance as storage deposit for the owner. + reserved: old::BalanceOf, + /// Total balance of the owner. + total: old::BalanceOf, +} + +#[derive(Encode, Decode, MaxEncodedLen, DefaultNoBound)] +pub struct Migration +where + T: Config, + OldCurrency: ReservableCurrency<::AccountId>, +{ + last_code_hash: Option>, + _phantom: PhantomData<(T, OldCurrency)>, +} + +impl MigrationStep for Migration +where + T: Config, + OldCurrency: 'static + ReservableCurrency<::AccountId>, + BalanceOf: From, +{ + const VERSION: u16 = 14; + + fn max_step_weight() -> Weight { + T::WeightInfo::v14_migration_step() + } + + fn step(&mut self) -> (IsFinished, Weight) { + let mut iter = if let Some(last_hash) = self.last_code_hash.take() { + old::CodeInfoOf::::iter_from( + old::CodeInfoOf::::hashed_key_for(last_hash), + ) + } else { + old::CodeInfoOf::::iter() + }; + + if let Some((hash, code_info)) = iter.next() { + log::debug!(target: LOG_TARGET, "Migrating storage deposit for 0x{:?}", HexDisplay::from(&code_info.owner.encode())); + + let remaining = OldCurrency::unreserve(&code_info.owner, code_info.deposit); + + if remaining > Zero::zero() { + log::warn!( + target: LOG_TARGET, + "Code owner's account 0x{:?} for code {:?} has some non-unreservable deposit {:?} from a total of {:?} that will remain in reserved.", + HexDisplay::from(&code_info.owner.encode()), + hash, + remaining, + code_info.deposit + ); + } + + let unreserved = code_info.deposit.saturating_sub(remaining); + let amount = BalanceOf::::from(unreserved); + + log::debug!( + target: LOG_TARGET, + "Holding {:?} on the code owner's account 0x{:?} for code {:?}.", + amount, + HexDisplay::from(&code_info.owner.encode()), + hash, + ); + + T::Currency::hold( + &HoldReason::CodeUploadDepositReserve.into(), + &code_info.owner, + amount, + ) + .unwrap_or_else(|err| { + log::error!( + target: LOG_TARGET, + "Failed to hold {:?} from the code owner's account 0x{:?} for code {:?}, reason: {:?}.", + amount, + HexDisplay::from(&code_info.owner.encode()), + hash, + err + ); + }); + + self.last_code_hash = Some(hash); + (IsFinished::No, T::WeightInfo::v14_migration_step()) + } else { + log::debug!(target: LOG_TARGET, "No more storage deposit to migrate"); + (IsFinished::Yes, T::WeightInfo::v14_migration_step()) + } + } + + #[cfg(feature = "try-runtime")] + fn pre_upgrade_step() -> Result, TryRuntimeError> { + let info: Vec<_> = old::CodeInfoOf::::iter().collect(); + + let mut owner_balance_allocation = + BTreeMap::, BalanceAllocation>::new(); + + // Calculates the balance allocation by accumulating the storage deposits of all codes owned + // by an owner. + for (_, code_info) in info { + owner_balance_allocation + .entry(code_info.owner.clone()) + .and_modify(|alloc| { + alloc.reserved = alloc.reserved.saturating_add(code_info.deposit); + }) + .or_insert(BalanceAllocation { + reserved: code_info.deposit, + total: OldCurrency::total_balance(&code_info.owner), + }); + } + + Ok(owner_balance_allocation.encode()) + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade_step(state: Vec) -> Result<(), TryRuntimeError> { + let owner_balance_allocation = + , BalanceAllocation> as Decode>::decode( + &mut &state[..], + ) + .expect("pre_upgrade_step provides a valid state; qed"); + + let mut total_held: BalanceOf = Zero::zero(); + let count = owner_balance_allocation.len(); + for (owner, old_balance_allocation) in owner_balance_allocation { + let held = + T::Currency::balance_on_hold(&HoldReason::CodeUploadDepositReserve.into(), &owner); + log::debug!( + target: LOG_TARGET, + "Validating storage deposit for owner 0x{:?}, reserved: {:?}, held: {:?}", + HexDisplay::from(&owner.encode()), + old_balance_allocation.reserved, + held + ); + ensure!(held == old_balance_allocation.reserved.into(), "Held amount mismatch"); + + log::debug!( + target: LOG_TARGET, + "Validating total balance for owner 0x{:?}, new: {:?}, old: {:?}", + HexDisplay::from(&owner.encode()), + T::Currency::total_balance(&owner), + old_balance_allocation.total + ); + ensure!( + T::Currency::total_balance(&owner) == + BalanceOf::::decode(&mut &old_balance_allocation.total.encode()[..]) + .unwrap(), + "Balance mismatch " + ); + total_held += held; + } + + log::info!( + target: LOG_TARGET, + "Code owners processed: {:?}.", + count + ); + + log::info!( + target: LOG_TARGET, + "Total held amount for storage deposit: {:?}", + total_held + ); + + Ok(()) + } +} diff --git a/frame/contracts/src/storage/meter.rs b/frame/contracts/src/storage/meter.rs index 5b3a2388bbafe..93885b37b4795 100644 --- a/frame/contracts/src/storage/meter.rs +++ b/frame/contracts/src/storage/meter.rs @@ -26,8 +26,9 @@ use frame_support::{ dispatch::{fmt::Debug, DispatchError}, ensure, traits::{ - tokens::{Fortitude::Polite, Preservation::Protect, WithdrawConsequence}, - Currency, ExistenceRequirement, Get, + fungible::Mutate, + tokens::{Fortitude::Polite, Preservation, WithdrawConsequence}, + Get, }, DefaultNoBound, RuntimeDebugNoBound, }; @@ -37,7 +38,7 @@ use sp_runtime::{ }; use sp_std::{marker::PhantomData, vec::Vec}; -/// Deposit that uses the native currency's balance type. +/// Deposit that uses the native fungible's balance type. pub type DepositOf = Deposit>; /// A production root storage meter that actually charges from its origin. @@ -89,7 +90,7 @@ pub trait Ext { /// This [`Ext`] is used for actual on-chain execution when balance needs to be charged. /// -/// It uses [`frame_support::traits::ReservableCurrency`] in order to do accomplish the reserves. +/// It uses [`frame_support::traits::fungible::Mutate`] in order to do accomplish the reserves. pub enum ReservingExt {} /// Used to implement a type state pattern for the meter. @@ -453,7 +454,7 @@ where System::::inc_consumers(contract_info.deposit_account())?; // We also need to make sure that the contract's account itself exists. - T::Currency::transfer(origin, contract, ed, ExistenceRequirement::KeepAlive)?; + T::Currency::transfer(origin, contract, ed, Preservation::Preserve)?; System::::inc_consumers(contract)?; Ok(deposit) @@ -517,7 +518,7 @@ impl Ext for ReservingExt { // We are sending the `min_leftover` and the `min_balance` from the origin // account as part of a contract call. Hence origin needs to have those left over // as free balance after accounting for all deposits. - let max = T::Currency::reducible_balance(origin, Protect, Polite) + let max = T::Currency::reducible_balance(origin, Preservation::Preserve, Polite) .saturating_sub(min_leftover) .saturating_sub(Pallet::::min_balance()); let default = max.min(T::DefaultDepositLimit::get()); @@ -537,12 +538,11 @@ impl Ext for ReservingExt { terminated: bool, ) -> Result<(), DispatchError> { match amount { - Deposit::Charge(amount) => T::Currency::transfer( - origin, - deposit_account, - *amount, - ExistenceRequirement::KeepAlive, - ), + Deposit::Charge(amount) | Deposit::Refund(amount) if amount.is_zero() => return Ok(()), + Deposit::Charge(amount) => { + T::Currency::transfer(origin, deposit_account, *amount, Preservation::Preserve)?; + Ok(()) + }, Deposit::Refund(amount) => { if terminated { System::::dec_consumers(&deposit_account); @@ -551,9 +551,11 @@ impl Ext for ReservingExt { deposit_account, origin, *amount, - // We can safely use `AllowDeath` because our own consumer prevents an removal. - ExistenceRequirement::AllowDeath, - ) + // We can safely make it `Expendable` because our own consumer prevents a + // removal. + Preservation::Expendable, + )?; + Ok(()) }, } } diff --git a/frame/contracts/src/tests.rs b/frame/contracts/src/tests.rs index 3b41c83a932b0..2e6161dcff544 100644 --- a/frame/contracts/src/tests.rs +++ b/frame/contracts/src/tests.rs @@ -31,8 +31,8 @@ use crate::{ wasm::{Determinism, ReturnCode as RuntimeReturnCode}, weights::WeightInfo, BalanceOf, Code, CodeHash, CodeInfoOf, CollectEvents, Config, ContractInfo, ContractInfoOf, - DebugInfo, DefaultAddressGenerator, DeletionQueueCounter, Error, MigrationInProgress, Origin, - Pallet, PristineCode, Schedule, + DebugInfo, DefaultAddressGenerator, DeletionQueueCounter, Error, HoldReason, + MigrationInProgress, Origin, Pallet, PristineCode, Schedule, }; use assert_matches::assert_matches; use codec::Encode; @@ -42,8 +42,9 @@ use frame_support::{ parameter_types, storage::child, traits::{ - ConstU32, ConstU64, Contains, Currency, ExistenceRequirement, LockableCurrency, OnIdle, - OnInitialize, StorageVersion, WithdrawReasons, + fungible::{BalancedHold, Inspect, InspectHold, Mutate, MutateHold}, + tokens::Preservation, + ConstU32, ConstU64, Contains, OnIdle, OnInitialize, StorageVersion, }, weights::{constants::WEIGHT_REF_TIME_PER_SECOND, Weight}, }; @@ -70,7 +71,7 @@ frame_support::construct_runtime!( Timestamp: pallet_timestamp::{Pallet, Call, Storage, Inherent}, Randomness: pallet_insecure_randomness_collective_flip::{Pallet, Storage}, Utility: pallet_utility::{Pallet, Call, Storage, Event}, - Contracts: pallet_contracts::{Pallet, Call, Storage, Event}, + Contracts: pallet_contracts::{Pallet, Call, Storage, Event, HoldReason}, Proxy: pallet_proxy::{Pallet, Call, Storage, Event}, Dummy: pallet_dummy } @@ -90,13 +91,14 @@ macro_rules! assert_refcount { } pub mod test_utils { - use super::{Balances, DepositPerByte, DepositPerItem, Hash, SysConfig, Test}; + + use super::{DepositPerByte, DepositPerItem, Hash, SysConfig, Test}; use crate::{ exec::AccountIdOf, BalanceOf, CodeHash, CodeInfo, CodeInfoOf, Config, ContractInfo, ContractInfoOf, Nonce, PristineCode, }; use codec::{Encode, MaxEncodedLen}; - use frame_support::traits::Currency; + use frame_support::traits::{fungible::Mutate, Currency}; pub fn place_contract(address: &AccountIdOf, code_hash: CodeHash) { let nonce = >::mutate(|counter| { @@ -109,11 +111,10 @@ pub mod test_utils { >::insert(address, contract); } pub fn set_balance(who: &AccountIdOf, amount: u64) { - let imbalance = Balances::deposit_creating(who, amount); - drop(imbalance); + let _ = ::Currency::set_balance(who, amount); } pub fn get_balance(who: &AccountIdOf) -> u64 { - Balances::free_balance(who) + ::Currency::free_balance(who) } pub fn get_contract(addr: &AccountIdOf) -> ContractInfo { get_contract_checked(addr).unwrap() @@ -350,8 +351,8 @@ impl pallet_balances::Config for Test { type WeightInfo = (); type FreezeIdentifier = (); type MaxFreezes = (); - type RuntimeHoldReason = (); - type MaxHolds = (); + type RuntimeHoldReason = RuntimeHoldReason; + type MaxHolds = ConstU32<1>; } impl pallet_timestamp::Config for Test { @@ -459,6 +460,7 @@ impl Config for Test { type MaxStorageKeyLen = ConstU32<128>; type UnsafeUnstableInterface = UnstableInterface; type MaxDebugBufferLen = ConstU32<{ 2 * 1024 * 1024 }>; + type RuntimeHoldReason = RuntimeHoldReason; type Migrations = crate::migration::codegen::BenchMigrations; type CodeHashLockupDepositPercent = CodeHashLockupDepositPercent; type MaxDelegateDependencies = MaxDelegateDependencies; @@ -592,7 +594,7 @@ impl Default for Origin { #[test] fn calling_plain_account_fails() { ExtBuilder::default().build().execute_with(|| { - let _ = Balances::deposit_creating(&ALICE, 100_000_000); + let _ = ::Currency::set_balance(&ALICE, 100_000_000); let base_cost = <::WeightInfo as WeightInfo>::call(); assert_eq!( @@ -634,7 +636,7 @@ fn migration_in_progress_works() { let (wasm, code_hash) = compile_module::("dummy").unwrap(); ExtBuilder::default().existential_deposit(1).build().execute_with(|| { - let _ = Balances::deposit_creating(&ALICE, 1_000_000); + let _ = ::Currency::set_balance(&ALICE, 1_000_000); MigrationInProgress::::set(Some(Default::default())); assert_err!( @@ -690,7 +692,7 @@ fn instantiate_and_call_and_deposit_event() { let (wasm, code_hash) = compile_module::("event_and_return_on_deploy").unwrap(); ExtBuilder::default().existential_deposit(1).build().execute_with(|| { - let _ = Balances::deposit_creating(&ALICE, 1_000_000); + let _ = ::Currency::set_balance(&ALICE, 1_000_000); let min_balance = ::Currency::minimum_balance(); let value = 100; @@ -813,7 +815,7 @@ fn deposit_event_max_value_limit() { ExtBuilder::default().existential_deposit(50).build().execute_with(|| { // Create - let _ = Balances::deposit_creating(&ALICE, 1_000_000); + let _ = ::Currency::set_balance(&ALICE, 1_000_000); let addr = Contracts::bare_instantiate( ALICE, 30_000, @@ -860,7 +862,7 @@ fn run_out_of_fuel_engine() { let (wasm, _code_hash) = compile_module::("run_out_of_gas").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let min_balance = ::Currency::minimum_balance(); - let _ = Balances::deposit_creating(&ALICE, 1_000_000); + let _ = ::Currency::set_balance(&ALICE, 1_000_000); let addr = Contracts::bare_instantiate( ALICE, @@ -899,7 +901,7 @@ fn run_out_of_fuel_host() { let (code, _hash) = compile_module::("chain_extension").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let min_balance = ::Currency::minimum_balance(); - let _ = Balances::deposit_creating(&ALICE, 1000 * min_balance); + let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); let addr = Contracts::bare_instantiate( ALICE, @@ -941,7 +943,7 @@ fn gas_syncs_work() { let (wasm1, _code_hash) = compile_module::("seal_input_once").unwrap(); let (wasm2, _code_hash) = compile_module::("seal_input_twice").unwrap(); ExtBuilder::default().existential_deposit(200).build().execute_with(|| { - let _ = Balances::deposit_creating(&ALICE, 1_000_000); + let _ = ::Currency::set_balance(&ALICE, 1_000_000); // Instantiate noop contract. let addr0 = Contracts::bare_instantiate( ALICE, @@ -1049,7 +1051,7 @@ fn instantiate_unique_trie_id() { let (wasm, code_hash) = compile_module::("self_destruct").unwrap(); ExtBuilder::default().existential_deposit(500).build().execute_with(|| { - let _ = Balances::deposit_creating(&ALICE, 1_000_000); + let _ = ::Currency::set_balance(&ALICE, 1_000_000); Contracts::upload_code(RuntimeOrigin::signed(ALICE), wasm, None, Determinism::Enforced) .unwrap(); @@ -1116,7 +1118,7 @@ fn storage_max_value_limit() { ExtBuilder::default().existential_deposit(50).build().execute_with(|| { // Create - let _ = Balances::deposit_creating(&ALICE, 1_000_000); + let _ = ::Currency::set_balance(&ALICE, 1_000_000); let addr = Contracts::bare_instantiate( ALICE, 30_000, @@ -1167,7 +1169,7 @@ fn deploy_and_call_other_contract() { let min_balance = ::Currency::minimum_balance(); // Create - let _ = Balances::deposit_creating(&ALICE, 1_000_000); + let _ = ::Currency::set_balance(&ALICE, 1_000_000); let caller_addr = Contracts::bare_instantiate( ALICE, 100_000, @@ -1315,7 +1317,7 @@ fn delegate_call() { let (callee_wasm, callee_code_hash) = compile_module::("delegate_call_lib").unwrap(); ExtBuilder::default().existential_deposit(500).build().execute_with(|| { - let _ = Balances::deposit_creating(&ALICE, 1_000_000); + let _ = ::Currency::set_balance(&ALICE, 1_000_000); // Instantiate the 'caller' let caller_addr = Contracts::bare_instantiate( @@ -1355,7 +1357,7 @@ fn delegate_call() { fn transfer_allow_death_cannot_kill_account() { 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 _ = ::Currency::set_balance(&ALICE, 1_000_000); // Instantiate the BOB contract. let addr = Contracts::bare_instantiate( @@ -1379,11 +1381,11 @@ fn transfer_allow_death_cannot_kill_account() { let total_balance = ::Currency::total_balance(&addr); assert_err!( - <::Currency as Currency>::transfer( + <::Currency as Mutate>::transfer( &addr, &ALICE, total_balance, - ExistenceRequirement::AllowDeath, + Preservation::Expendable, ), TokenError::Frozen, ); @@ -1396,7 +1398,7 @@ fn transfer_allow_death_cannot_kill_account() { fn cannot_self_destruct_through_draning() { let (wasm, _code_hash) = compile_module::("drain").unwrap(); ExtBuilder::default().existential_deposit(200).build().execute_with(|| { - let _ = Balances::deposit_creating(&ALICE, 1_000_000); + let _ = ::Currency::set_balance(&ALICE, 1_000_000); // Instantiate the BOB contract. let addr = Contracts::bare_instantiate( @@ -1440,7 +1442,7 @@ fn cannot_self_destruct_through_draning() { fn cannot_self_destruct_through_storage_refund_after_price_change() { let (wasm, _code_hash) = compile_module::("store_call").unwrap(); ExtBuilder::default().existential_deposit(200).build().execute_with(|| { - let _ = Balances::deposit_creating(&ALICE, 1_000_000); + let _ = ::Currency::set_balance(&ALICE, 1_000_000); let min_balance = ::Currency::minimum_balance(); // Instantiate the BOB contract. @@ -1500,7 +1502,7 @@ fn cannot_self_destruct_through_storage_refund_after_price_change() { fn cannot_self_destruct_while_live() { 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 _ = ::Currency::set_balance(&ALICE, 1_000_000); // Instantiate the BOB contract. let addr = Contracts::bare_instantiate( @@ -1544,8 +1546,8 @@ fn cannot_self_destruct_while_live() { fn self_destruct_works() { let (wasm, code_hash) = compile_module::("self_destruct").unwrap(); 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); + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + let _ = ::Currency::set_balance(&DJANGO, 1_000_000); // Instantiate the BOB contract. let addr = Contracts::bare_instantiate( @@ -1580,11 +1582,11 @@ fn self_destruct_works() { // Check that account is gone assert!(get_contract_checked(&addr).is_none()); - assert_eq!(Balances::total_balance(&addr), 0); + assert_eq!(::Currency::total_balance(&addr), 0); // check that the beneficiary (django) got remaining balance let ed = ::Currency::minimum_balance(); - assert_eq!(Balances::free_balance(DJANGO), 1_000_000 + 100_000 + ed); + assert_eq!(::Currency::free_balance(DJANGO), 1_000_000 + 100_000 + ed); pretty_assertions::assert_eq!( System::events(), @@ -1651,7 +1653,7 @@ fn destroy_contract_and_transfer_funds() { ExtBuilder::default().existential_deposit(50).build().execute_with(|| { // Create code hash for bob to instantiate - let _ = Balances::deposit_creating(&ALICE, 1_000_000); + let _ = ::Currency::set_balance(&ALICE, 1_000_000); Contracts::bare_upload_code(ALICE, callee_wasm, None, Determinism::Enforced).unwrap(); // This deploys the BOB contract, which in turn deploys the CHARLIE contract during @@ -1695,7 +1697,7 @@ fn destroy_contract_and_transfer_funds() { fn cannot_self_destruct_in_constructor() { let (wasm, _) = compile_module::("self_destructing_constructor").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { - let _ = Balances::deposit_creating(&ALICE, 1_000_000); + let _ = ::Currency::set_balance(&ALICE, 1_000_000); // Fail to instantiate the BOB because the contructor calls seal_terminate. assert_err_ignore_postinfo!( @@ -1718,7 +1720,7 @@ fn crypto_hashes() { let (wasm, _code_hash) = compile_module::("crypto_hashes").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { - let _ = Balances::deposit_creating(&ALICE, 1_000_000); + let _ = ::Currency::set_balance(&ALICE, 1_000_000); // Instantiate the CRYPTO_HASHES contract. let addr = Contracts::bare_instantiate( @@ -1781,7 +1783,7 @@ fn transfer_return_code() { let (wasm, _code_hash) = compile_module::("transfer_return_code").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let min_balance = ::Currency::minimum_balance(); - let _ = Balances::deposit_creating(&ALICE, 1000 * min_balance); + let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); let addr = Contracts::bare_instantiate( ALICE, @@ -1799,7 +1801,7 @@ fn transfer_return_code() { .account_id; // Contract has only the minimal balance so any transfer will fail. - Balances::make_free_balance_be(&addr, min_balance); + ::Currency::set_balance(&addr, min_balance); let result = Contracts::bare_call( ALICE, addr.clone(), @@ -1823,8 +1825,8 @@ fn call_return_code() { let (callee_code, _callee_hash) = compile_module::("ok_trap_revert").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let min_balance = ::Currency::minimum_balance(); - let _ = Balances::deposit_creating(&ALICE, 1000 * min_balance); - let _ = Balances::deposit_creating(&CHARLIE, 1000 * min_balance); + let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); + let _ = ::Currency::set_balance(&CHARLIE, 1000 * min_balance); let addr_bob = Contracts::bare_instantiate( ALICE, @@ -1840,7 +1842,7 @@ fn call_return_code() { .result .unwrap() .account_id; - Balances::make_free_balance_be(&addr_bob, min_balance); + ::Currency::set_balance(&addr_bob, min_balance); // Contract calls into Django which is no valid contract let result = Contracts::bare_call( @@ -1872,7 +1874,7 @@ fn call_return_code() { .result .unwrap() .account_id; - Balances::make_free_balance_be(&addr_django, min_balance); + ::Currency::set_balance(&addr_django, min_balance); // Contract has only the minimal balance so any transfer will fail. let result = Contracts::bare_call( @@ -1895,7 +1897,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, min_balance + 1000); + ::Currency::set_balance(&addr_bob, min_balance + 1000); let result = Contracts::bare_call( ALICE, addr_bob.clone(), @@ -1943,8 +1945,8 @@ fn instantiate_return_code() { let (callee_code, callee_hash) = compile_module::("ok_trap_revert").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let min_balance = ::Currency::minimum_balance(); - let _ = Balances::deposit_creating(&ALICE, 1000 * min_balance); - let _ = Balances::deposit_creating(&CHARLIE, 1000 * min_balance); + let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); + let _ = ::Currency::set_balance(&CHARLIE, 1000 * min_balance); let callee_hash = callee_hash.as_ref().to_vec(); assert_ok!(Contracts::instantiate_with_code( @@ -1973,7 +1975,7 @@ fn instantiate_return_code() { .account_id; // Contract has only the minimal balance so any transfer will fail. - Balances::make_free_balance_be(&addr, min_balance); + ::Currency::set_balance(&addr, min_balance); let result = Contracts::bare_call( ALICE, addr.clone(), @@ -1990,7 +1992,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, min_balance + 10_000); + ::Currency::set_balance(&addr, min_balance + 10_000); let result = Contracts::bare_call( ALICE, addr.clone(), @@ -2045,7 +2047,7 @@ fn disabled_chain_extension_wont_deploy() { let (code, _hash) = compile_module::("chain_extension").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let min_balance = ::Currency::minimum_balance(); - let _ = Balances::deposit_creating(&ALICE, 1000 * min_balance); + let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); TestExtension::disable(); assert_err_ignore_postinfo!( Contracts::instantiate_with_code( @@ -2067,7 +2069,7 @@ fn disabled_chain_extension_errors_on_call() { let (code, _hash) = compile_module::("chain_extension").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let min_balance = ::Currency::minimum_balance(); - let _ = Balances::deposit_creating(&ALICE, 1000 * min_balance); + let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); let addr = Contracts::bare_instantiate( ALICE, min_balance * 100, @@ -2095,7 +2097,7 @@ fn chain_extension_works() { let (code, _hash) = compile_module::("chain_extension").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let min_balance = ::Currency::minimum_balance(); - let _ = Balances::deposit_creating(&ALICE, 1000 * min_balance); + let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); let addr = Contracts::bare_instantiate( ALICE, min_balance * 100, @@ -2242,7 +2244,7 @@ fn chain_extension_temp_storage_works() { let (code, _hash) = compile_module::("chain_extension_temp_storage").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let min_balance = ::Currency::minimum_balance(); - let _ = Balances::deposit_creating(&ALICE, 1000 * min_balance); + let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); let addr = Contracts::bare_instantiate( ALICE, min_balance * 100, @@ -2289,7 +2291,7 @@ fn lazy_removal_works() { let (code, _hash) = compile_module::("self_destruct").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let min_balance = ::Currency::minimum_balance(); - let _ = Balances::deposit_creating(&ALICE, 1000 * min_balance); + let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); let addr = Contracts::bare_instantiate( ALICE, @@ -2341,7 +2343,7 @@ fn lazy_batch_removal_works() { let (code, _hash) = compile_module::("self_destruct").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let min_balance = ::Currency::minimum_balance(); - let _ = Balances::deposit_creating(&ALICE, 1000 * min_balance); + let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); let mut tries: Vec = vec![]; for i in 0..3u8 { @@ -2409,7 +2411,7 @@ fn lazy_removal_partial_remove_works() { let trie = ext.execute_with(|| { let min_balance = ::Currency::minimum_balance(); - let _ = Balances::deposit_creating(&ALICE, 1000 * min_balance); + let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); let addr = Contracts::bare_instantiate( ALICE, @@ -2491,7 +2493,7 @@ fn lazy_removal_does_no_run_on_low_remaining_weight() { let (code, _hash) = compile_module::("self_destruct").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let min_balance = ::Currency::minimum_balance(); - let _ = Balances::deposit_creating(&ALICE, 1000 * min_balance); + let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); let addr = Contracts::bare_instantiate( ALICE, @@ -2563,7 +2565,7 @@ fn lazy_removal_does_not_use_all_weight() { let (trie, vals, weight_per_key) = ext.execute_with(|| { let min_balance = ::Currency::minimum_balance(); - let _ = Balances::deposit_creating(&ALICE, 1000 * min_balance); + let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); let addr = Contracts::bare_instantiate( ALICE, @@ -2651,7 +2653,7 @@ fn deletion_queue_ring_buffer_overflow() { ext.execute_with(|| { let min_balance = ::Currency::minimum_balance(); - let _ = Balances::deposit_creating(&ALICE, 1000 * min_balance); + let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); let mut tries: Vec = vec![]; // add 3 contracts to the deletion queue @@ -2710,7 +2712,7 @@ fn deletion_queue_ring_buffer_overflow() { 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 _ = ::Currency::set_balance(&ALICE, 1_000_000); let min_balance = ::Currency::minimum_balance(); // Create two contracts with the same code and check that they do in fact share it. @@ -2807,7 +2809,7 @@ fn debug_message_works() { let (wasm, _code_hash) = compile_module::("debug_message_works").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { - let _ = Balances::deposit_creating(&ALICE, 1_000_000); + let _ = ::Currency::set_balance(&ALICE, 1_000_000); let addr = Contracts::bare_instantiate( ALICE, 30_000, @@ -2844,7 +2846,7 @@ fn debug_message_logging_disabled() { let (wasm, _code_hash) = compile_module::("debug_message_logging_disabled").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { - let _ = Balances::deposit_creating(&ALICE, 1_000_000); + let _ = ::Currency::set_balance(&ALICE, 1_000_000); let addr = Contracts::bare_instantiate( ALICE, 30_000, @@ -2883,7 +2885,7 @@ fn debug_message_invalid_utf8() { let (wasm, _code_hash) = compile_module::("debug_message_invalid_utf8").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { - let _ = Balances::deposit_creating(&ALICE, 1_000_000); + let _ = ::Currency::set_balance(&ALICE, 1_000_000); let addr = Contracts::bare_instantiate( ALICE, 30_000, @@ -2920,7 +2922,7 @@ fn gas_estimation_nested_call_fixed_limit() { let (callee_code, _callee_hash) = compile_module::("dummy").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let min_balance = ::Currency::minimum_balance(); - let _ = Balances::deposit_creating(&ALICE, 1000 * min_balance); + let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); let addr_caller = Contracts::bare_instantiate( ALICE, @@ -3016,8 +3018,8 @@ fn gas_estimation_call_runtime() { let (callee_code, _callee_hash) = compile_module::("dummy").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let min_balance = ::Currency::minimum_balance(); - let _ = Balances::deposit_creating(&ALICE, 1000 * min_balance); - let _ = Balances::deposit_creating(&CHARLIE, 1000 * min_balance); + let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); + let _ = ::Currency::set_balance(&CHARLIE, 1000 * min_balance); let addr_caller = Contracts::bare_instantiate( ALICE, @@ -3094,8 +3096,8 @@ fn call_runtime_reentrancy_guarded() { let (callee_code, _callee_hash) = compile_module::("dummy").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let min_balance = ::Currency::minimum_balance(); - let _ = Balances::deposit_creating(&ALICE, 1000 * min_balance); - let _ = Balances::deposit_creating(&CHARLIE, 1000 * min_balance); + let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); + let _ = ::Currency::set_balance(&CHARLIE, 1000 * min_balance); let addr_caller = Contracts::bare_instantiate( ALICE, @@ -3161,7 +3163,7 @@ fn ecdsa_recover() { let (wasm, _code_hash) = compile_module::("ecdsa_recover").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { - let _ = Balances::deposit_creating(&ALICE, 1_000_000); + let _ = ::Currency::set_balance(&ALICE, 1_000_000); // Instantiate the ecdsa_recover contract. let addr = Contracts::bare_instantiate( @@ -3225,7 +3227,7 @@ fn bare_instantiate_returns_events() { let (wasm, _code_hash) = compile_module::("transfer_return_code").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let min_balance = ::Currency::minimum_balance(); - let _ = Balances::deposit_creating(&ALICE, 1000 * min_balance); + let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); let result = Contracts::bare_instantiate( ALICE, @@ -3250,7 +3252,7 @@ fn bare_instantiate_does_not_return_events() { let (wasm, _code_hash) = compile_module::("transfer_return_code").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let min_balance = ::Currency::minimum_balance(); - let _ = Balances::deposit_creating(&ALICE, 1000 * min_balance); + let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); let result = Contracts::bare_instantiate( ALICE, @@ -3275,7 +3277,7 @@ fn bare_call_returns_events() { let (wasm, _code_hash) = compile_module::("transfer_return_code").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let min_balance = ::Currency::minimum_balance(); - let _ = Balances::deposit_creating(&ALICE, 1000 * min_balance); + let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); let addr = Contracts::bare_instantiate( ALICE, @@ -3316,7 +3318,7 @@ fn bare_call_does_not_return_events() { let (wasm, _code_hash) = compile_module::("transfer_return_code").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { let min_balance = ::Currency::minimum_balance(); - let _ = Balances::deposit_creating(&ALICE, 1000 * min_balance); + let _ = ::Currency::set_balance(&ALICE, 1000 * min_balance); let addr = Contracts::bare_instantiate( ALICE, @@ -3357,7 +3359,7 @@ fn sr25519_verify() { let (wasm, _code_hash) = compile_module::("sr25519_verify").unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { - let _ = Balances::deposit_creating(&ALICE, 1_000_000); + let _ = ::Currency::set_balance(&ALICE, 1_000_000); // Instantiate the sr25519_verify contract. let addr = Contracts::bare_instantiate( @@ -3428,7 +3430,7 @@ fn failed_deposit_charge_should_roll_back_call() { let execute = || { ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { - let _ = Balances::deposit_creating(&ALICE, 1_000_000); + let _ = ::Currency::set_balance(&ALICE, 1_000_000); // Instantiate both contracts. let addr_caller = Contracts::bare_instantiate( @@ -3507,7 +3509,7 @@ 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); + let _ = ::Currency::set_balance(&ALICE, 1_000_000); // Drop previous events initialize_block(2); @@ -3525,21 +3527,15 @@ fn upload_code_works() { assert_eq!( System::events(), - vec![ - EventRecord { - phase: Phase::Initialization, - event: RuntimeEvent::Balances(pallet_balances::Event::Reserved { - who: ALICE, - amount: deposit_expected, - }), - topics: vec![], - }, - EventRecord { - phase: Phase::Initialization, - event: RuntimeEvent::Contracts(crate::Event::CodeStored { code_hash }), - topics: vec![code_hash], - }, - ] + vec![EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Contracts(crate::Event::CodeStored { + code_hash, + deposit_held: deposit_expected, + uploader: ALICE + }), + topics: vec![code_hash], + },] ); }); } @@ -3551,7 +3547,7 @@ fn upload_code_limit_too_low() { let deposit_insufficient = deposit_expected.saturating_sub(1); ExtBuilder::default().existential_deposit(100).build().execute_with(|| { - let _ = Balances::deposit_creating(&ALICE, 1_000_000); + let _ = ::Currency::set_balance(&ALICE, 1_000_000); // Drop previous events initialize_block(2); @@ -3577,7 +3573,7 @@ fn upload_code_not_enough_balance() { let deposit_insufficient = deposit_expected.saturating_sub(1); ExtBuilder::default().existential_deposit(100).build().execute_with(|| { - let _ = Balances::deposit_creating(&ALICE, deposit_insufficient); + let _ = ::Currency::set_balance(&ALICE, deposit_insufficient); // Drop previous events initialize_block(2); @@ -3601,7 +3597,7 @@ 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); + let _ = ::Currency::set_balance(&ALICE, 1_000_000); // Drop previous events initialize_block(2); @@ -3621,28 +3617,20 @@ fn remove_code_works() { vec![ EventRecord { phase: Phase::Initialization, - event: RuntimeEvent::Balances(pallet_balances::Event::Reserved { - who: ALICE, - amount: deposit_expected, + event: RuntimeEvent::Contracts(crate::Event::CodeStored { + code_hash, + deposit_held: deposit_expected, + uploader: ALICE }), - topics: vec![], - }, - EventRecord { - phase: Phase::Initialization, - event: RuntimeEvent::Contracts(crate::Event::CodeStored { code_hash }), topics: vec![code_hash], }, EventRecord { phase: Phase::Initialization, - event: RuntimeEvent::Balances(pallet_balances::Event::Unreserved { - who: ALICE, - amount: deposit_expected, + event: RuntimeEvent::Contracts(crate::Event::CodeRemoved { + code_hash, + deposit_released: deposit_expected, + remover: ALICE }), - topics: vec![], - }, - EventRecord { - phase: Phase::Initialization, - event: RuntimeEvent::Contracts(crate::Event::CodeRemoved { code_hash }), topics: vec![code_hash], }, ] @@ -3655,7 +3643,7 @@ 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); + let _ = ::Currency::set_balance(&ALICE, 1_000_000); // Drop previous events initialize_block(2); @@ -3676,21 +3664,15 @@ fn remove_code_wrong_origin() { assert_eq!( System::events(), - vec![ - EventRecord { - phase: Phase::Initialization, - event: RuntimeEvent::Balances(pallet_balances::Event::Reserved { - who: ALICE, - amount: deposit_expected, - }), - topics: vec![], - }, - EventRecord { - phase: Phase::Initialization, - event: RuntimeEvent::Contracts(crate::Event::CodeStored { code_hash }), - topics: vec![code_hash], - }, - ] + vec![EventRecord { + phase: Phase::Initialization, + event: RuntimeEvent::Contracts(crate::Event::CodeStored { + code_hash, + deposit_held: deposit_expected, + uploader: ALICE + }), + topics: vec![code_hash], + },] ); }); } @@ -3700,7 +3682,7 @@ 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); + let _ = ::Currency::set_balance(&ALICE, 1_000_000); assert_ok!(Contracts::instantiate_with_code( RuntimeOrigin::signed(ALICE), @@ -3729,7 +3711,7 @@ 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); + let _ = ::Currency::set_balance(&ALICE, 1_000_000); // Drop previous events initialize_block(2); @@ -3747,7 +3729,7 @@ fn remove_code_not_found() { 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 _ = ::Currency::set_balance(&ALICE, 1_000_000); let min_balance = ::Currency::minimum_balance(); // Drop previous events @@ -3784,15 +3766,11 @@ fn instantiate_with_zero_balance_works() { vec![ EventRecord { phase: Phase::Initialization, - event: RuntimeEvent::Balances(pallet_balances::Event::Reserved { - who: ALICE, - amount: deposit_expected, + event: RuntimeEvent::Contracts(crate::Event::CodeStored { + code_hash, + deposit_held: deposit_expected, + uploader: ALICE }), - topics: vec![], - }, - EventRecord { - phase: Phase::Initialization, - event: RuntimeEvent::Contracts(crate::Event::CodeStored { code_hash }), topics: vec![code_hash], }, EventRecord { @@ -3860,7 +3838,7 @@ fn instantiate_with_zero_balance_works() { 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 _ = ::Currency::set_balance(&ALICE, 1_000_000); let min_balance = ::Currency::minimum_balance(); // Drop previous events @@ -3896,15 +3874,11 @@ fn instantiate_with_below_existential_deposit_works() { vec![ EventRecord { phase: Phase::Initialization, - event: RuntimeEvent::Balances(pallet_balances::Event::Reserved { - who: ALICE, - amount: deposit_expected, + event: RuntimeEvent::Contracts(crate::Event::CodeStored { + code_hash, + deposit_held: deposit_expected, + uploader: ALICE }), - topics: vec![], - }, - EventRecord { - phase: Phase::Initialization, - event: RuntimeEvent::Contracts(crate::Event::CodeStored { code_hash }), topics: vec![code_hash], }, EventRecord { @@ -3981,7 +3955,7 @@ fn instantiate_with_below_existential_deposit_works() { 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 _ = ::Currency::set_balance(&ALICE, 1_000_000); let mut deposit = ::Currency::minimum_balance(); let addr = Contracts::bare_instantiate( @@ -4120,7 +4094,7 @@ fn storage_deposit_callee_works() { let (wasm_callee, _code_hash_callee) = compile_module::("store_call").unwrap(); const ED: u64 = 200; ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { - let _ = Balances::deposit_creating(&ALICE, 1_000_000); + let _ = ::Currency::set_balance(&ALICE, 1_000_000); // Create both contracts: Constructors do nothing. let addr_caller = Contracts::bare_instantiate( @@ -4177,7 +4151,7 @@ fn set_code_extrinsic() { assert_ne!(code_hash, new_code_hash); ExtBuilder::default().existential_deposit(100).build().execute_with(|| { - let _ = Balances::deposit_creating(&ALICE, 1_000_000); + let _ = ::Currency::set_balance(&ALICE, 1_000_000); let addr = Contracts::bare_instantiate( ALICE, @@ -4261,12 +4235,15 @@ fn set_code_extrinsic() { #[test] fn slash_cannot_kill_account() { let (wasm, _code_hash) = compile_module::("dummy").unwrap(); - ExtBuilder::default().existential_deposit(200).build().execute_with(|| { - let _ = Balances::deposit_creating(&ALICE, 1_000_000); + const ED: u64 = 200; + ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { + let value = 700; + let balance_held = 500; + let _ = ::Currency::set_balance(&ALICE, 1_000_000); let addr = Contracts::bare_instantiate( ALICE, - 700, + value, GAS_LIMIT, None, Code::Upload(wasm), @@ -4282,25 +4259,28 @@ fn slash_cannot_kill_account() { // Drop previous events initialize_block(2); - // Try to destroy the account of the contract by slashing. + // We need to hold some balances in order to have something to slash. As slashing can only + // affect balances held under certain HoldReason. + ::Currency::hold( + &HoldReason::CodeUploadDepositReserve.into(), + &addr, + balance_held, + ) + .unwrap(); + + assert_eq!(::Currency::total_balance_on_hold(&addr), balance_held); + + // Try to destroy the account of the contract by slashing the total balance. // The account does not get destroyed because of the consumer reference. // Slashing can for example happen if the contract takes part in staking. let _ = ::Currency::slash( + &HoldReason::CodeUploadDepositReserve.into(), &addr, ::Currency::total_balance(&addr), ); - assert_eq!( - System::events(), - vec![EventRecord { - phase: Phase::Initialization, - event: RuntimeEvent::Balances(pallet_balances::Event::Slashed { - who: addr.clone(), - amount: 700, // slash didn't remove the minimum balance - }), - topics: vec![], - },] - ); + // Slashing only removed the balance held. + assert_eq!(::Currency::total_balance(&addr), value + ED - balance_held,); }); } @@ -4309,7 +4289,7 @@ fn contract_reverted() { let (wasm, code_hash) = compile_module::("return_with_data").unwrap(); ExtBuilder::default().existential_deposit(100).build().execute_with(|| { - let _ = Balances::deposit_creating(&ALICE, 1_000_000); + let _ = ::Currency::set_balance(&ALICE, 1_000_000); let flags = ReturnFlags::REVERT; let buffer = [4u8, 8, 15, 16, 23, 42]; let input = (flags.bits(), buffer).encode(); @@ -4422,7 +4402,7 @@ fn contract_reverted() { #[test] fn code_rejected_error_works() { ExtBuilder::default().existential_deposit(200).build().execute_with(|| { - let _ = Balances::deposit_creating(&ALICE, 1_000_000); + let _ = ::Currency::set_balance(&ALICE, 1_000_000); let (wasm, _) = compile_module::("invalid_module").unwrap(); assert_noop!( @@ -4515,7 +4495,7 @@ fn set_code_hash() { let (new_wasm, new_code_hash) = compile_module::("new_set_code_hash_contract").unwrap(); ExtBuilder::default().existential_deposit(100).build().execute_with(|| { - let _ = Balances::deposit_creating(&ALICE, 1_000_000); + let _ = ::Currency::set_balance(&ALICE, 1_000_000); // Instantiate the 'caller' let contract_addr = Contracts::bare_instantiate( @@ -4619,7 +4599,7 @@ fn storage_deposit_limit_is_enforced() { let ed = 200; let (wasm, _code_hash) = compile_module::("store_call").unwrap(); ExtBuilder::default().existential_deposit(ed).build().execute_with(|| { - let _ = Balances::deposit_creating(&ALICE, 1_000_000); + let _ = ::Currency::set_balance(&ALICE, 1_000_000); let min_balance = ::Currency::minimum_balance(); // Setting insufficient storage_deposit should fail. @@ -4712,7 +4692,7 @@ fn deposit_limit_in_nested_calls() { compile_module::("create_storage_and_call").unwrap(); let (wasm_callee, _code_hash_callee) = compile_module::("store_call").unwrap(); ExtBuilder::default().existential_deposit(200).build().execute_with(|| { - let _ = Balances::deposit_creating(&ALICE, 1_000_000); + let _ = ::Currency::set_balance(&ALICE, 1_000_000); // Create both contracts: Constructors do nothing. let addr_caller = Contracts::bare_instantiate( @@ -4821,7 +4801,7 @@ fn deposit_limit_in_nested_calls() { >::StorageDepositLimitExhausted, ); - let _ = Balances::make_free_balance_be(&ALICE, 1_000); + let _ = ::Currency::set_balance(&ALICE, 1_000); // Require more than the sender's balance. // We don't set a special limit for the nested call. @@ -4858,8 +4838,8 @@ fn deposit_limit_in_nested_instantiate() { let (wasm_callee, code_hash_callee) = compile_module::("store_deploy").unwrap(); const ED: u64 = 5; ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { - let _ = Balances::deposit_creating(&ALICE, 1_000_000); - let _ = Balances::deposit_creating(&BOB, 1_000_000); + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + let _ = ::Currency::set_balance(&BOB, 1_000_000); // Create caller contract let addr_caller = Contracts::bare_instantiate( ALICE, @@ -5008,9 +4988,11 @@ fn deposit_limit_in_nested_instantiate() { #[test] fn deposit_limit_honors_liquidity_restrictions() { let (wasm, _code_hash) = compile_module::("store_call").unwrap(); - ExtBuilder::default().existential_deposit(200).build().execute_with(|| { - let _ = Balances::deposit_creating(&ALICE, 1_000_000); - let _ = Balances::deposit_creating(&BOB, 1_000); + const ED: u64 = 200; + ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { + let bobs_balance = 1_000; + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + let _ = ::Currency::set_balance(&BOB, bobs_balance); let min_balance = ::Currency::minimum_balance(); // Instantiate the BOB contract. @@ -5033,8 +5015,13 @@ fn deposit_limit_honors_liquidity_restrictions() { assert_eq!(get_contract(&addr).total_deposit(), min_balance); assert_eq!(::Currency::total_balance(&addr), min_balance); - // check that the lock ins honored - Balances::set_lock([0; 8], &BOB, 1_000, WithdrawReasons::TRANSFER); + // check that the hold is honored + ::Currency::hold( + &HoldReason::CodeUploadDepositReserve.into(), + &BOB, + bobs_balance - ED, + ) + .unwrap(); assert_err_ignore_postinfo!( Contracts::call( RuntimeOrigin::signed(BOB), @@ -5046,7 +5033,7 @@ fn deposit_limit_honors_liquidity_restrictions() { ), >::StorageDepositNotEnoughFunds, ); - assert_eq!(Balances::free_balance(&BOB), 1_000); + assert_eq!(::Currency::free_balance(&BOB), ED); }); } @@ -5054,8 +5041,8 @@ fn deposit_limit_honors_liquidity_restrictions() { fn deposit_limit_honors_existential_deposit() { let (wasm, _code_hash) = compile_module::("store_call").unwrap(); ExtBuilder::default().existential_deposit(200).build().execute_with(|| { - let _ = Balances::deposit_creating(&ALICE, 1_000_000); - let _ = Balances::deposit_creating(&BOB, 1_000); + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + let _ = ::Currency::set_balance(&BOB, 1_000); let min_balance = ::Currency::minimum_balance(); // Instantiate the BOB contract. @@ -5090,7 +5077,7 @@ fn deposit_limit_honors_existential_deposit() { ), >::StorageDepositNotEnoughFunds, ); - assert_eq!(Balances::free_balance(&BOB), 1_000); + assert_eq!(::Currency::free_balance(&BOB), 1_000); }); } @@ -5098,8 +5085,8 @@ fn deposit_limit_honors_existential_deposit() { fn deposit_limit_honors_min_leftover() { let (wasm, _code_hash) = compile_module::("store_call").unwrap(); ExtBuilder::default().existential_deposit(200).build().execute_with(|| { - let _ = Balances::deposit_creating(&ALICE, 1_000_000); - let _ = Balances::deposit_creating(&BOB, 1_000); + let _ = ::Currency::set_balance(&ALICE, 1_000_000); + let _ = ::Currency::set_balance(&BOB, 1_000); let min_balance = ::Currency::minimum_balance(); // Instantiate the BOB contract. @@ -5134,7 +5121,7 @@ fn deposit_limit_honors_min_leftover() { ), >::StorageDepositNotEnoughFunds, ); - assert_eq!(Balances::free_balance(&BOB), 1_000); + assert_eq!(::Currency::free_balance(&BOB), 1_000); }); } @@ -5143,7 +5130,7 @@ fn cannot_instantiate_indeterministic_code() { let (wasm, code_hash) = compile_module::("float_instruction").unwrap(); let (caller_wasm, _) = compile_module::("instantiate_return_code").unwrap(); ExtBuilder::default().existential_deposit(200).build().execute_with(|| { - let _ = Balances::deposit_creating(&ALICE, 1_000_000); + let _ = ::Currency::set_balance(&ALICE, 1_000_000); // Try to instantiate directly from code assert_err_ignore_postinfo!( @@ -5278,7 +5265,7 @@ fn cannot_set_code_indeterministic_code() { let (wasm, code_hash) = compile_module::("float_instruction").unwrap(); let (caller_wasm, _) = compile_module::("set_code_hash").unwrap(); ExtBuilder::default().existential_deposit(200).build().execute_with(|| { - let _ = Balances::deposit_creating(&ALICE, 1_000_000); + let _ = ::Currency::set_balance(&ALICE, 1_000_000); // Put the non deterministic contract on-chain assert_ok!(Contracts::upload_code( @@ -5328,7 +5315,7 @@ fn delegate_call_indeterministic_code() { let (wasm, code_hash) = compile_module::("float_instruction").unwrap(); let (caller_wasm, _) = compile_module::("delegate_call_simple").unwrap(); ExtBuilder::default().existential_deposit(200).build().execute_with(|| { - let _ = Balances::deposit_creating(&ALICE, 1_000_000); + let _ = ::Currency::set_balance(&ALICE, 1_000_000); // Put the non deterministic contract on-chain assert_ok!(Contracts::upload_code( @@ -5439,7 +5426,7 @@ fn add_remove_delegate_dependency_works() { const ED: u64 = 2000; ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { - let _ = Balances::deposit_creating(&ALICE, 1_000_000); + let _ = Balances::set_balance(&ALICE, 1_000_000); // Instantiate with add_delegate_dependency should fail since the code is not yet on chain. assert_err!( @@ -5544,7 +5531,7 @@ fn native_dependency_deposit_works() { // Test with both existing and uploaded code for code in [Code::Upload(wasm.clone()), Code::Existing(code_hash)] { ExtBuilder::default().existential_deposit(ED).build().execute_with(|| { - let _ = Balances::deposit_creating(&ALICE, 1_000_000); + let _ = Balances::set_balance(&ALICE, 1_000_000); let lockup_deposit_percent = CodeHashLockupDepositPercent::get(); let per_byte = DepositPerByte::get(); let per_item = DepositPerItem::get(); @@ -5629,7 +5616,7 @@ fn reentrance_count_works_with_call() { let (wasm, _code_hash) = compile_module::("reentrance_count_call").unwrap(); ExtBuilder::default().existential_deposit(100).build().execute_with(|| { - let _ = Balances::deposit_creating(&ALICE, 1_000_000); + let _ = ::Currency::set_balance(&ALICE, 1_000_000); let contract_addr = Contracts::bare_instantiate( ALICE, @@ -5670,7 +5657,7 @@ fn reentrance_count_works_with_delegated_call() { let (wasm, code_hash) = compile_module::("reentrance_count_delegated_call").unwrap(); ExtBuilder::default().existential_deposit(100).build().execute_with(|| { - let _ = Balances::deposit_creating(&ALICE, 1_000_000); + let _ = ::Currency::set_balance(&ALICE, 1_000_000); let contract_addr = Contracts::bare_instantiate( ALICE, @@ -5713,7 +5700,7 @@ fn account_reentrance_count_works() { compile_module::("reentrance_count_call").unwrap(); ExtBuilder::default().existential_deposit(100).build().execute_with(|| { - let _ = Balances::deposit_creating(&ALICE, 1_000_000); + let _ = ::Currency::set_balance(&ALICE, 1_000_000); let contract_addr = Contracts::bare_instantiate( ALICE, @@ -5829,7 +5816,7 @@ fn root_can_call() { let (wasm, _) = compile_module::("dummy").unwrap(); ExtBuilder::default().existential_deposit(100).build().execute_with(|| { - let _ = Balances::deposit_creating(&ALICE, 1_000_000); + let _ = ::Currency::set_balance(&ALICE, 1_000_000); let addr = Contracts::bare_instantiate( ALICE, diff --git a/frame/contracts/src/wasm/mod.rs b/frame/contracts/src/wasm/mod.rs index 5bf0b0cc94b7b..551bfba3fbe6f 100644 --- a/frame/contracts/src/wasm/mod.rs +++ b/frame/contracts/src/wasm/mod.rs @@ -37,14 +37,14 @@ use crate::{ gas::{GasMeter, Token}, wasm::prepare::LoadedModule, weights::WeightInfo, - AccountIdOf, BadOrigin, BalanceOf, CodeHash, CodeInfoOf, CodeVec, Config, Error, Event, Pallet, - PristineCode, Schedule, Weight, LOG_TARGET, + AccountIdOf, BadOrigin, BalanceOf, CodeHash, CodeInfoOf, CodeVec, Config, Error, Event, + HoldReason, Pallet, PristineCode, Schedule, Weight, LOG_TARGET, }; use codec::{Decode, Encode, MaxEncodedLen}; use frame_support::{ dispatch::{DispatchError, DispatchResult}, ensure, - traits::ReservableCurrency, + traits::{fungible::MutateHold, tokens::Precision::BestEffort}, }; use sp_core::Get; use sp_runtime::RuntimeDebug; @@ -237,12 +237,24 @@ impl WasmBlob { // the `owner` is always the origin of the current transaction. None => { let deposit = self.code_info.deposit; - T::Currency::reserve(&self.code_info.owner, deposit) - .map_err(|_| >::StorageDepositNotEnoughFunds)?; + T::Currency::hold( + &HoldReason::CodeUploadDepositReserve.into(), + &self.code_info.owner, + deposit, + ) + .map_err(|_| >::StorageDepositNotEnoughFunds)?; + self.code_info.refcount = 0; >::insert(code_hash, &self.code); *stored_code_info = Some(self.code_info.clone()); - >::deposit_event(vec![code_hash], Event::CodeStored { code_hash }); + >::deposit_event( + vec![code_hash], + Event::CodeStored { + code_hash, + deposit_held: deposit, + uploader: self.code_info.owner.clone(), + }, + ); Ok(deposit) }, } @@ -255,10 +267,21 @@ impl WasmBlob { if let Some(code_info) = existing { ensure!(code_info.refcount == 0, >::CodeInUse); ensure!(&code_info.owner == origin, BadOrigin); - T::Currency::unreserve(&code_info.owner, code_info.deposit); + let _ = T::Currency::release( + &HoldReason::CodeUploadDepositReserve.into(), + &code_info.owner, + code_info.deposit, + BestEffort, + ); + let deposit_released = code_info.deposit; + let remover = code_info.owner.clone(); + *existing = None; >::remove(&code_hash); - >::deposit_event(vec![code_hash], Event::CodeRemoved { code_hash }); + >::deposit_event( + vec![code_hash], + Event::CodeRemoved { code_hash, deposit_released, remover }, + ); Ok(()) } else { Err(>::CodeNotFound.into()) diff --git a/frame/contracts/src/weights.rs b/frame/contracts/src/weights.rs index a3e289b0763f6..d8f873b0615be 100644 --- a/frame/contracts/src/weights.rs +++ b/frame/contracts/src/weights.rs @@ -56,6 +56,7 @@ pub trait WeightInfo { fn v11_migration_step(k: u32, ) -> Weight; fn v12_migration_step(c: u32, ) -> Weight; fn v13_migration_step() -> Weight; + fn v14_migration_step() -> Weight; fn migration_noop() -> Weight; fn migrate() -> Weight; fn on_runtime_upgrade_noop() -> Weight; @@ -240,6 +241,20 @@ impl WeightInfo for SubstrateWeight { } /// Storage: `Contracts::MigrationInProgress` (r:1 w:1) /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) + /// Storage: `Contracts::CodeInfoOf` (r:2 w:0) + /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:0) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + fn v14_migration_step() -> Weight { + // Proof Size summary in bytes: + // Measured: `260` + // Estimated: `6200` + // Minimum execution time: 27_995_000 picoseconds. + Weight::from_parts(28_661_000, 6200) + .saturating_add(T::DbWeight::get().reads(3_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 { // Proof Size summary in bytes: // Measured: `142` @@ -2096,6 +2111,20 @@ impl WeightInfo for () { } /// Storage: `Contracts::MigrationInProgress` (r:1 w:1) /// Proof: `Contracts::MigrationInProgress` (`max_values`: Some(1), `max_size`: Some(1026), added: 1521, mode: `Measured`) + /// Storage: `Contracts::CodeInfoOf` (r:2 w:0) + /// Proof: `Contracts::CodeInfoOf` (`max_values`: None, `max_size`: Some(93), added: 2568, mode: `Measured`) + /// Storage: `System::Account` (r:1 w:0) + /// Proof: `System::Account` (`max_values`: None, `max_size`: Some(128), added: 2603, mode: `Measured`) + fn v14_migration_step() -> Weight { + // Proof Size summary in bytes: + // Measured: `260` + // Estimated: `6200` + // Minimum execution time: 27_995_000 picoseconds. + Weight::from_parts(28_661_000, 6200) + .saturating_add(RocksDbWeight::get().reads(3_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 { // Proof Size summary in bytes: // Measured: `142` diff --git a/frame/support/src/traits/tokens/fungible/hold.rs b/frame/support/src/traits/tokens/fungible/hold.rs index ddcb8c6ac1da8..2605d1797ed2e 100644 --- a/frame/support/src/traits/tokens/fungible/hold.rs +++ b/frame/support/src/traits/tokens/fungible/hold.rs @@ -99,7 +99,7 @@ pub trait Inspect: super::Inspect { /// Check to see if some `amount` of funds of `who` may be placed on hold for the given /// `reason`. Reasons why this may not be true: /// - /// - The implementor supports only a limited number of concurrernt holds on an account which is + /// - The implementor supports only a limited number of concurrent holds on an account which is /// the possible values of `reason`; /// - The main balance of the account is less than `amount`; /// - Removing `amount` from the main balance would kill the account and remove the only @@ -118,7 +118,7 @@ pub trait Inspect: super::Inspect { /// **WARNING** /// Do not use this directly unless you want trouble, since it allows you to alter account balances /// without keeping the issuance up to date. It has no safeguards against accidentally creating -/// token imbalances in your system leading to accidental imflation or deflation. It's really just +/// token imbalances in your system leading to accidental inflation or deflation. It's really just /// for the underlying datatype to implement so the user gets the much safer `Balanced` trait to /// use. pub trait Unbalanced: Inspect { @@ -131,7 +131,7 @@ pub trait Unbalanced: Inspect { /// invariants such as any Existential Deposits needed or overflows/underflows. /// If this cannot be done for some reason (e.g. because the account doesn't exist) then an /// `Err` is returned. - // Implmentation note: This should increment the consumer refs if it moves total on hold from + // Implementation note: This should increment the consumer refs if it moves total on hold from // zero to non-zero and decrement in the opposite direction. // // Since this was not done in the previous logic, this will need either a migration or a diff --git a/frame/support/src/traits/tokens/fungibles/hold.rs b/frame/support/src/traits/tokens/fungibles/hold.rs index 8fc038c57be3d..2adc00bb4d05a 100644 --- a/frame/support/src/traits/tokens/fungibles/hold.rs +++ b/frame/support/src/traits/tokens/fungibles/hold.rs @@ -133,7 +133,7 @@ pub trait Inspect: super::Inspect { /// **WARNING** /// Do not use this directly unless you want trouble, since it allows you to alter account balances /// without keeping the issuance up to date. It has no safeguards against accidentally creating -/// token imbalances in your system leading to accidental imflation or deflation. It's really just +/// token imbalances in your system leading to accidental inflation or deflation. It's really just /// for the underlying datatype to implement so the user gets the much safer `Balanced` trait to /// use. pub trait Unbalanced: Inspect { @@ -146,7 +146,7 @@ pub trait Unbalanced: Inspect { /// invariants such as any Existential Deposits needed or overflows/underflows. /// If this cannot be done for some reason (e.g. because the account doesn't exist) then an /// `Err` is returned. - // Implmentation note: This should increment the consumer refs if it moves total on hold from + // Implementation note: This should increment the consumer refs if it moves total on hold from // zero to non-zero and decrement in the opposite direction. // // Since this was not done in the previous logic, this will need either a migration or a diff --git a/frame/support/src/traits/tokens/fungibles/regular.rs b/frame/support/src/traits/tokens/fungibles/regular.rs index e17dfed0f3e8e..b6cea15284d39 100644 --- a/frame/support/src/traits/tokens/fungibles/regular.rs +++ b/frame/support/src/traits/tokens/fungibles/regular.rs @@ -136,7 +136,7 @@ impl> Dust { /// **WARNING** /// Do not use this directly unless you want trouble, since it allows you to alter account balances /// without keeping the issuance up to date. It has no safeguards against accidentally creating -/// token imbalances in your system leading to accidental imflation or deflation. It's really just +/// token imbalances in your system leading to accidental inflation or deflation. It's really just /// for the underlying datatype to implement so the user gets the much safer `Balanced` trait to /// use. pub trait Unbalanced: Inspect {