diff --git a/Cargo.lock b/Cargo.lock index 383ae1891dbbb..f1ece88e5fc7c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -5922,6 +5922,7 @@ dependencies = [ "frame-benchmarking", "frame-support", "frame-system", + "log", "pallet-balances", "parity-scale-codec", "scale-info", diff --git a/frame/multisig/Cargo.toml b/frame/multisig/Cargo.toml index 6c8b5fbaa7362..bfd0870d30c22 100644 --- a/frame/multisig/Cargo.toml +++ b/frame/multisig/Cargo.toml @@ -22,6 +22,9 @@ sp-io = { version = "6.0.0", default-features = false, path = "../../primitives/ sp-runtime = { version = "6.0.0", default-features = false, path = "../../primitives/runtime" } sp-std = { version = "4.0.0", default-features = false, path = "../../primitives/std" } +# third party +log = { version = "0.4.17", default-features = false } + [dev-dependencies] pallet-balances = { version = "4.0.0-dev", path = "../balances" } sp-core = { version = "6.0.0", path = "../../primitives/core" } diff --git a/frame/multisig/src/benchmarking.rs b/frame/multisig/src/benchmarking.rs index c0b0097b07236..badac24f929de 100644 --- a/frame/multisig/src/benchmarking.rs +++ b/frame/multisig/src/benchmarking.rs @@ -31,7 +31,7 @@ const SEED: u32 = 0; fn setup_multi( s: u32, z: u32, -) -> Result<(Vec, OpaqueCall), &'static str> { +) -> Result<(Vec, Box<::Call>), &'static str> { let mut signatories: Vec = Vec::new(); for i in 0..s { let signatory = account("signatory", i, SEED); @@ -44,8 +44,7 @@ fn setup_multi( // Must first convert to runtime call type. let call: ::RuntimeCall = frame_system::Call::::remark { remark: vec![0; z as usize] }.into(); - let call_data = OpaqueCall::::from_encoded(call.encode()); - Ok((signatories, call_data)) + Ok((signatories, Box::new(call))) } benchmarks! { @@ -74,35 +73,15 @@ benchmarks! { // Transaction Length let z in 0 .. 10_000; let (mut signatories, call) = setup_multi::(s, z)?; - let call_hash = blake2_256(call.encoded()); - let multi_account_id = Multisig::::multi_account_id(&signatories, s.try_into().unwrap()); - let caller = signatories.pop().ok_or("signatories should have len 2 or more")?; - // Whitelist caller account from further DB operations. - let caller_key = frame_system::Account::::hashed_key_for(&caller); - frame_benchmarking::benchmarking::add_to_whitelist(caller_key.into()); - }: as_multi(RawOrigin::Signed(caller), s as u16, signatories, None, call, false, Weight::zero()) - verify { - assert!(Multisigs::::contains_key(multi_account_id, call_hash)); - assert!(!Calls::::contains_key(call_hash)); - } - - as_multi_create_store { - // Signatories, need at least 2 total people - let s in 2 .. T::MaxSignatories::get() as u32; - // Transaction Length - let z in 0 .. 10_000; - let (mut signatories, call) = setup_multi::(s, z)?; - let call_hash = blake2_256(call.encoded()); + let call_hash = call.using_encoded(blake2_256); let multi_account_id = Multisig::::multi_account_id(&signatories, s.try_into().unwrap()); let caller = signatories.pop().ok_or("signatories should have len 2 or more")?; - T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); // Whitelist caller account from further DB operations. let caller_key = frame_system::Account::::hashed_key_for(&caller); frame_benchmarking::benchmarking::add_to_whitelist(caller_key.into()); - }: as_multi(RawOrigin::Signed(caller), s as u16, signatories, None, call, true, Weight::zero()) + }: as_multi(RawOrigin::Signed(caller), s as u16, signatories, None, call, Weight::zero()) verify { assert!(Multisigs::::contains_key(multi_account_id, call_hash)); - assert!(Calls::::contains_key(call_hash)); } as_multi_approve { @@ -111,49 +90,22 @@ benchmarks! { // Transaction Length let z in 0 .. 10_000; let (mut signatories, call) = setup_multi::(s, z)?; - let call_hash = blake2_256(call.encoded()); - let multi_account_id = Multisig::::multi_account_id(&signatories, s.try_into().unwrap()); - let mut signatories2 = signatories.clone(); - let caller = signatories.pop().ok_or("signatories should have len 2 or more")?; - // before the call, get the timepoint - let timepoint = Multisig::::timepoint(); - // Create the multi, storing for worst case - Multisig::::as_multi(RawOrigin::Signed(caller).into(), s as u16, signatories, None, call.clone(), true, Weight::zero())?; - assert!(Calls::::contains_key(call_hash)); - let caller2 = signatories2.remove(0); - // Whitelist caller account from further DB operations. - let caller_key = frame_system::Account::::hashed_key_for(&caller2); - frame_benchmarking::benchmarking::add_to_whitelist(caller_key.into()); - }: as_multi(RawOrigin::Signed(caller2), s as u16, signatories2, Some(timepoint), call, false, Weight::zero()) - verify { - let multisig = Multisigs::::get(multi_account_id, call_hash).ok_or("multisig not created")?; - assert_eq!(multisig.approvals.len(), 2); - } - - as_multi_approve_store { - // Signatories, need at least 3 people (so we don't complete the multisig) - let s in 3 .. T::MaxSignatories::get() as u32; - // Transaction Length - let z in 0 .. 10_000; - let (mut signatories, call) = setup_multi::(s, z)?; - let call_hash = blake2_256(call.encoded()); + let call_hash = call.using_encoded(blake2_256); let multi_account_id = Multisig::::multi_account_id(&signatories, s.try_into().unwrap()); let mut signatories2 = signatories.clone(); let caller = signatories.pop().ok_or("signatories should have len 2 or more")?; // before the call, get the timepoint let timepoint = Multisig::::timepoint(); - // Create the multi, not storing - Multisig::::as_multi(RawOrigin::Signed(caller).into(), s as u16, signatories, None, call.clone(), false, Weight::zero())?; - assert!(!Calls::::contains_key(call_hash)); + // Create the multi + Multisig::::as_multi(RawOrigin::Signed(caller).into(), s as u16, signatories, None, call.clone(), Weight::zero())?; let caller2 = signatories2.remove(0); // Whitelist caller account from further DB operations. let caller_key = frame_system::Account::::hashed_key_for(&caller2); frame_benchmarking::benchmarking::add_to_whitelist(caller_key.into()); - }: as_multi(RawOrigin::Signed(caller2), s as u16, signatories2, Some(timepoint), call, true, Weight::zero()) + }: as_multi(RawOrigin::Signed(caller2), s as u16, signatories2, Some(timepoint), call, Weight::zero()) verify { let multisig = Multisigs::::get(multi_account_id, call_hash).ok_or("multisig not created")?; assert_eq!(multisig.approvals.len(), 2); - assert!(Calls::::contains_key(call_hash)); } as_multi_complete { @@ -162,27 +114,27 @@ benchmarks! { // Transaction Length let z in 0 .. 10_000; let (mut signatories, call) = setup_multi::(s, z)?; - let call_hash = blake2_256(call.encoded()); + let call_hash = call.using_encoded(blake2_256); let multi_account_id = Multisig::::multi_account_id(&signatories, s.try_into().unwrap()); let mut signatories2 = signatories.clone(); let caller = signatories.pop().ok_or("signatories should have len 2 or more")?; // before the call, get the timepoint let timepoint = Multisig::::timepoint(); - // Create the multi, storing it for worst case - Multisig::::as_multi(RawOrigin::Signed(caller).into(), s as u16, signatories, None, call.clone(), true, Weight::zero())?; + // Create the multi + Multisig::::as_multi(RawOrigin::Signed(caller).into(), s as u16, signatories, None, call.clone(), Weight::zero())?; // Everyone except the first person approves for i in 1 .. s - 1 { let mut signatories_loop = signatories2.clone(); let caller_loop = signatories_loop.remove(i as usize); let o = RawOrigin::Signed(caller_loop).into(); - Multisig::::as_multi(o, s as u16, signatories_loop, Some(timepoint), call.clone(), false, Weight::zero())?; + Multisig::::as_multi(o, s as u16, signatories_loop, Some(timepoint), call.clone(), Weight::zero())?; } let caller2 = signatories2.remove(0); assert!(Multisigs::::contains_key(&multi_account_id, call_hash)); // Whitelist caller account from further DB operations. let caller_key = frame_system::Account::::hashed_key_for(&caller2); frame_benchmarking::benchmarking::add_to_whitelist(caller_key.into()); - }: as_multi(RawOrigin::Signed(caller2), s as u16, signatories2, Some(timepoint), call, false, Weight::MAX) + }: as_multi(RawOrigin::Signed(caller2), s as u16, signatories2, Some(timepoint), call, Weight::MAX) verify { assert!(!Multisigs::::contains_key(&multi_account_id, call_hash)); } @@ -195,7 +147,7 @@ benchmarks! { let (mut signatories, call) = setup_multi::(s, z)?; let multi_account_id = Multisig::::multi_account_id(&signatories, s.try_into().unwrap()); let caller = signatories.pop().ok_or("signatories should have len 2 or more")?; - let call_hash = blake2_256(call.encoded()); + let call_hash = call.using_encoded(blake2_256); // Whitelist caller account from further DB operations. let caller_key = frame_system::Account::::hashed_key_for(&caller); frame_benchmarking::benchmarking::add_to_whitelist(caller_key.into()); @@ -214,7 +166,7 @@ benchmarks! { let mut signatories2 = signatories.clone(); let multi_account_id = Multisig::::multi_account_id(&signatories, s.try_into().unwrap()); let caller = signatories.pop().ok_or("signatories should have len 2 or more")?; - let call_hash = blake2_256(call.encoded()); + let call_hash = call.using_encoded(blake2_256); // before the call, get the timepoint let timepoint = Multisig::::timepoint(); // Create the multi @@ -224,7 +176,6 @@ benchmarks! { signatories, None, call, - false, Weight::zero() )?; let caller2 = signatories2.remove(0); @@ -237,45 +188,6 @@ benchmarks! { assert_eq!(multisig.approvals.len(), 2); } - approve_as_multi_complete { - // Signatories, need at least 2 people - let s in 2 .. T::MaxSignatories::get() as u32; - // Transaction Length, not a component - let z = 10_000; - let (mut signatories, call) = setup_multi::(s, z)?; - let multi_account_id = Multisig::::multi_account_id(&signatories, s.try_into().unwrap()); - let mut signatories2 = signatories.clone(); - let caller = signatories.pop().ok_or("signatories should have len 2 or more")?; - T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); - let call_hash = blake2_256(call.encoded()); - // before the call, get the timepoint - let timepoint = Multisig::::timepoint(); - // Create the multi - Multisig::::as_multi(RawOrigin::Signed(caller).into(), s as u16, signatories, None, call.clone(), true, Weight::zero())?; - // Everyone except the first person approves - for i in 1 .. s - 1 { - let mut signatories_loop = signatories2.clone(); - let caller_loop = signatories_loop.remove(i as usize); - let o = RawOrigin::Signed(caller_loop).into(); - Multisig::::as_multi(o, s as u16, signatories_loop, Some(timepoint), call.clone(), false, Weight::zero())?; - } - let caller2 = signatories2.remove(0); - assert!(Multisigs::::contains_key(&multi_account_id, call_hash)); - // Whitelist caller account from further DB operations. - let caller_key = frame_system::Account::::hashed_key_for(&caller2); - frame_benchmarking::benchmarking::add_to_whitelist(caller_key.into()); - }: approve_as_multi( - RawOrigin::Signed(caller2), - s as u16, - signatories2, - Some(timepoint), - call_hash, - Weight::MAX - ) - verify { - assert!(!Multisigs::::contains_key(multi_account_id, call_hash)); - } - cancel_as_multi { // Signatories, need at least 2 people let s in 2 .. T::MaxSignatories::get() as u32; @@ -284,20 +196,18 @@ benchmarks! { let (mut signatories, call) = setup_multi::(s, z)?; let multi_account_id = Multisig::::multi_account_id(&signatories, s.try_into().unwrap()); let caller = signatories.pop().ok_or("signatories should have len 2 or more")?; - let call_hash = blake2_256(call.encoded()); + let call_hash = call.using_encoded(blake2_256); let timepoint = Multisig::::timepoint(); // Create the multi let o = RawOrigin::Signed(caller.clone()).into(); - Multisig::::as_multi(o, s as u16, signatories.clone(), None, call, true, Weight::zero())?; + Multisig::::as_multi(o, s as u16, signatories.clone(), None, call, Weight::zero())?; assert!(Multisigs::::contains_key(&multi_account_id, call_hash)); - assert!(Calls::::contains_key(call_hash)); // Whitelist caller account from further DB operations. let caller_key = frame_system::Account::::hashed_key_for(&caller); frame_benchmarking::benchmarking::add_to_whitelist(caller_key.into()); }: _(RawOrigin::Signed(caller), s as u16, signatories, timepoint, call_hash) verify { assert!(!Multisigs::::contains_key(multi_account_id, call_hash)); - assert!(!Calls::::contains_key(call_hash)); } impl_benchmark_test_suite!(Multisig, crate::tests::new_test_ext(), crate::tests::Test); diff --git a/frame/multisig/src/lib.rs b/frame/multisig/src/lib.rs index 3bdb47ffc4568..e3031cc830209 100644 --- a/frame/multisig/src/lib.rs +++ b/frame/multisig/src/lib.rs @@ -47,6 +47,7 @@ #![cfg_attr(not(feature = "std"), no_std)] mod benchmarking; +pub mod migrations; mod tests; pub mod weights; @@ -57,7 +58,7 @@ use frame_support::{ PostDispatchInfo, }, ensure, - traits::{Currency, Get, ReservableCurrency, WrapperKeepOpaque}, + traits::{Currency, Get, ReservableCurrency}, weights::Weight, RuntimeDebug, }; @@ -73,6 +74,20 @@ pub use weights::WeightInfo; pub use pallet::*; +/// The log target of this pallet. +pub const LOG_TARGET: &'static str = "runtime::multisig"; + +// syntactic sugar for logging. +#[macro_export] +macro_rules! log { + ($level:tt, $patter:expr $(, $values:expr)* $(,)?) => { + log::$level!( + target: crate::LOG_TARGET, + concat!("[{:?}] ✍️ ", $patter), >::block_number() $(, $values)* + ) + }; +} + type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; @@ -100,12 +115,10 @@ pub struct Multisig { approvals: Vec, } -type OpaqueCall = WrapperKeepOpaque<::RuntimeCall>; - type CallHash = [u8; 32]; enum CallOrHash { - Call(OpaqueCall, bool), + Call(::RuntimeCall), Hash([u8; 32]), } @@ -152,9 +165,13 @@ pub mod pallet { type WeightInfo: WeightInfo; } + /// The current storage version. + const STORAGE_VERSION: StorageVersion = StorageVersion::new(1); + #[pallet::pallet] #[pallet::generate_store(pub(super) trait Store)] #[pallet::without_storage_info] + #[pallet::storage_version(STORAGE_VERSION)] pub struct Pallet(_); /// The set of open multisig operations. @@ -168,10 +185,6 @@ pub mod pallet { Multisig, T::AccountId>, >; - #[pallet::storage] - pub type Calls = - StorageMap<_, Identity, [u8; 32], (OpaqueCall, T::AccountId, BalanceOf)>; - #[pallet::error] pub enum Error { /// Threshold must be 2 or greater. @@ -343,13 +356,13 @@ pub mod pallet { /// taken for its lifetime of `DepositBase + threshold * DepositFactor`. /// ------------------------------- /// - DB Weight: - /// - Reads: Multisig Storage, [Caller Account], Calls (if `store_call`) - /// - Writes: Multisig Storage, [Caller Account], Calls (if `store_call`) + /// - Reads: Multisig Storage, [Caller Account] + /// - Writes: Multisig Storage, [Caller Account] /// - Plus Call Weight /// # #[pallet::weight({ let s = other_signatories.len() as u32; - let z = call.encoded_len() as u32; + let z = call.using_encoded(|d| d.len()) as u32; T::WeightInfo::as_multi_create(s, z) .max(T::WeightInfo::as_multi_create_store(s, z)) @@ -362,8 +375,7 @@ pub mod pallet { threshold: u16, other_signatories: Vec, maybe_timepoint: Option>, - call: OpaqueCall, - store_call: bool, + call: Box<::RuntimeCall>, max_weight: Weight, ) -> DispatchResultWithPostInfo { let who = ensure_signed(origin)?; @@ -372,7 +384,7 @@ pub mod pallet { threshold, other_signatories, maybe_timepoint, - CallOrHash::Call(call, store_call), + CallOrHash::Call(*call), max_weight, ) } @@ -462,8 +474,8 @@ pub mod pallet { /// - Storage: removes one item. /// ---------------------------------- /// - DB Weight: - /// - Read: Multisig Storage, [Caller Account], Refund Account, Calls - /// - Write: Multisig Storage, [Caller Account], Refund Account, Calls + /// - Read: Multisig Storage, [Caller Account], Refund Account + /// - Write: Multisig Storage, [Caller Account], Refund Account /// # #[pallet::weight(T::WeightInfo::cancel_as_multi(other_signatories.len() as u32))] pub fn cancel_as_multi( @@ -489,7 +501,6 @@ pub mod pallet { let err_amount = T::Currency::unreserve(&m.depositor, m.deposit); debug_assert!(err_amount.is_zero()); >::remove(&id, &call_hash); - Self::clear_call(&call_hash); Self::deposit_event(Event::MultisigCancelled { cancelling: who, @@ -531,13 +542,12 @@ impl Pallet { let id = Self::multi_account_id(&signatories, threshold); // Threshold > 1; this means it's a multi-step operation. We extract the `call_hash`. - let (call_hash, call_len, maybe_call, store) = match call_or_hash { - CallOrHash::Call(call, should_store) => { - let call_hash = blake2_256(call.encoded()); - let call_len = call.encoded_len(); - (call_hash, call_len, Some(call), should_store) + let (call_hash, call_len, maybe_call) = match call_or_hash { + CallOrHash::Call(call) => { + let (call_hash, call_len) = call.using_encoded(|d| (blake2_256(d), d.len())); + (call_hash, call_len, Some(call)) }, - CallOrHash::Hash(h) => (h, 0, None, false), + CallOrHash::Hash(h) => (h, 0, None), }; // Branch on whether the operation has already started or not. @@ -556,13 +566,7 @@ impl Pallet { } // We only bother fetching/decoding call if we know that we're ready to execute. - let maybe_approved_call = if approvals >= threshold { - Self::get_call(&call_hash, maybe_call.as_ref()) - } else { - None - }; - - if let Some((call, call_len)) = maybe_approved_call { + if let Some(call) = maybe_call.filter(|_| approvals >= threshold) { // verify weight ensure!( call.get_dispatch_info().weight.all_lte(max_weight), @@ -572,7 +576,6 @@ impl Pallet { // Clean up storage before executing call to avoid an possibility of reentrancy // attack. >::remove(&id, call_hash); - Self::clear_call(&call_hash); T::Currency::unreserve(&m.depositor, m.deposit); let result = call.dispatch(RawOrigin::Signed(id.clone()).into()); @@ -596,19 +599,6 @@ impl Pallet { // We cannot dispatch the call now; either it isn't available, or it is, but we // don't have threshold approvals even with our signature. - // Store the call if desired. - let stored = if let Some(data) = maybe_call.filter(|_| store) { - Self::store_call_and_reserve( - who.clone(), - &call_hash, - data, - BalanceOf::::zero(), - )?; - true - } else { - false - }; - if let Some(pos) = maybe_pos { // Record approval. m.approvals.insert(pos, who.clone()); @@ -622,17 +612,11 @@ impl Pallet { } else { // If we already approved and didn't store the Call, then this was useless and // we report an error. - ensure!(stored, Error::::AlreadyApproved); + Err(Error::::AlreadyApproved)? } - let final_weight = if stored { - T::WeightInfo::as_multi_approve_store( - other_signatories_len as u32, - call_len as u32, - ) - } else { - T::WeightInfo::as_multi_approve(other_signatories_len as u32, call_len as u32) - }; + let final_weight = + T::WeightInfo::as_multi_approve(other_signatories_len as u32, call_len as u32); // Call is not made, so the actual weight does not include call Ok(Some(final_weight).into()) } @@ -643,14 +627,7 @@ impl Pallet { // Just start the operation by recording it in storage. let deposit = T::DepositBase::get() + T::DepositFactor::get() * threshold.into(); - // Store the call if desired. - let stored = if let Some(data) = maybe_call.filter(|_| store) { - Self::store_call_and_reserve(who.clone(), &call_hash, data, deposit)?; - true - } else { - T::Currency::reserve(&who, deposit)?; - false - }; + T::Currency::reserve(&who, deposit)?; >::insert( &id, @@ -664,58 +641,13 @@ impl Pallet { ); Self::deposit_event(Event::NewMultisig { approving: who, multisig: id, call_hash }); - let final_weight = if stored { - T::WeightInfo::as_multi_create_store(other_signatories_len as u32, call_len as u32) - } else { - T::WeightInfo::as_multi_create(other_signatories_len as u32, call_len as u32) - }; + let final_weight = + T::WeightInfo::as_multi_create(other_signatories_len as u32, call_len as u32); // Call is not made, so the actual weight does not include call Ok(Some(final_weight).into()) } } - /// Place a call's encoded data in storage, reserving funds as appropriate. - /// - /// We store `data` here because storing `call` would result in needing another `.encode`. - /// - /// Returns a `bool` indicating whether the data did end up being stored. - fn store_call_and_reserve( - who: T::AccountId, - hash: &[u8; 32], - data: OpaqueCall, - other_deposit: BalanceOf, - ) -> DispatchResult { - ensure!(!Calls::::contains_key(hash), Error::::AlreadyStored); - let deposit = other_deposit + - T::DepositBase::get() + - T::DepositFactor::get() * - BalanceOf::::from(((data.encoded_len() + 31) / 32) as u32); - T::Currency::reserve(&who, deposit)?; - Calls::::insert(&hash, (data, who, deposit)); - Ok(()) - } - - /// Attempt to decode and return the call, provided by the user or from storage. - fn get_call( - hash: &[u8; 32], - maybe_known: Option<&OpaqueCall>, - ) -> Option<(::RuntimeCall, usize)> { - maybe_known.map_or_else( - || { - Calls::::get(hash) - .and_then(|(data, ..)| Some((data.try_decode()?, data.encoded_len()))) - }, - |data| Some((data.try_decode()?, data.encoded_len())), - ) - } - - /// Attempt to remove a call from storage, returning any deposit on it to the owner. - fn clear_call(hash: &[u8; 32]) { - if let Some((_, who, deposit)) = Calls::::take(hash) { - T::Currency::unreserve(&who, deposit); - } - } - /// The current `Timepoint`. pub fn timepoint() -> Timepoint { Timepoint { diff --git a/frame/multisig/src/migrations.rs b/frame/multisig/src/migrations.rs new file mode 100644 index 0000000000000..5085297cde433 --- /dev/null +++ b/frame/multisig/src/migrations.rs @@ -0,0 +1,86 @@ +// This file is part of Substrate. + +// Copyright (C) 2019-2022 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. + +// Migrations for Multisig Pallet + +use super::*; +use frame_support::{ + dispatch::GetStorageVersion, + traits::{OnRuntimeUpgrade, WrapperKeepOpaque}, + Identity, +}; + +#[cfg(feature = "try-runtime")] +use frame_support::ensure; + +pub mod v1 { + use super::*; + + type OpaqueCall = WrapperKeepOpaque<::RuntimeCall>; + + #[frame_support::storage_alias] + type Calls = StorageMap< + Pallet, + Identity, + [u8; 32], + (OpaqueCall, ::AccountId, BalanceOf), + >; + + pub struct MigrateToV1(sp_std::marker::PhantomData); + impl OnRuntimeUpgrade for MigrateToV1 { + #[cfg(feature = "try-runtime")] + fn pre_upgrade() -> Result, &'static str> { + let onchain = Pallet::::on_chain_storage_version(); + + ensure!(onchain < 1, "this migration can be deleted"); + + log!(info, "Number of calls to refund and delete: {}", Calls::::iter().count()); + + Ok(Vec::new()) + } + + fn on_runtime_upgrade() -> Weight { + let current = Pallet::::current_storage_version(); + let onchain = Pallet::::on_chain_storage_version(); + + if onchain > 0 { + log!(info, "MigrateToV1 should be removed"); + return T::DbWeight::get().reads(1) + } + + Calls::::drain().for_each(|(_call_hash, (_data, caller, deposit))| { + T::Currency::unreserve(&caller, deposit); + }); + + current.put::>(); + + ::BlockWeights::get().max_block + } + + #[cfg(feature = "try-runtime")] + fn post_upgrade(_state: Vec) -> Result<(), &'static str> { + let onchain = Pallet::::on_chain_storage_version(); + ensure!(onchain < 2, "this migration needs to be removed"); + ensure!(onchain == 1, "this migration needs to be run"); + ensure!( + Calls::::iter().count() == 0, + "there are some dangling calls that need to be destroyed and refunded" + ); + Ok(()) + } + } +} diff --git a/frame/multisig/src/tests.rs b/frame/multisig/src/tests.rs index b24a06f454368..879f9dc6794a2 100644 --- a/frame/multisig/src/tests.rs +++ b/frame/multisig/src/tests.rs @@ -34,7 +34,6 @@ use sp_runtime::{ type UncheckedExtrinsic = frame_system::mocking::MockUncheckedExtrinsic; type Block = frame_system::mocking::MockBlock; -type OpaqueCall = super::OpaqueCall; frame_support::construct_runtime!( pub enum Test where @@ -130,8 +129,8 @@ fn now() -> Timepoint { Multisig::timepoint() } -fn call_transfer(dest: u64, value: u64) -> RuntimeCall { - RuntimeCall::Balances(BalancesCall::transfer { dest, value }) +fn call_transfer(dest: u64, value: u64) -> Box { + Box::new(Call::Balances(BalancesCall::transfer { dest, value })) } #[test] @@ -144,14 +143,12 @@ fn multisig_deposit_is_taken_and_returned() { let call = call_transfer(6, 15); let call_weight = call.get_dispatch_info().weight; - let data = call.encode(); assert_ok!(Multisig::as_multi( RuntimeOrigin::signed(1), 2, vec![2, 3], None, - OpaqueCall::from_encoded(data.clone()), - false, + call.clone(), Weight::zero() )); assert_eq!(Balances::free_balance(1), 2); @@ -162,8 +159,7 @@ fn multisig_deposit_is_taken_and_returned() { 2, vec![1, 3], Some(now()), - OpaqueCall::from_encoded(data), - false, + call, call_weight )); assert_eq!(Balances::free_balance(1), 5); @@ -171,96 +167,6 @@ fn multisig_deposit_is_taken_and_returned() { }); } -#[test] -fn multisig_deposit_is_taken_and_returned_with_call_storage() { - new_test_ext().execute_with(|| { - let multi = Multisig::multi_account_id(&[1, 2, 3][..], 2); - assert_ok!(Balances::transfer(RuntimeOrigin::signed(1), multi, 5)); - assert_ok!(Balances::transfer(RuntimeOrigin::signed(2), multi, 5)); - assert_ok!(Balances::transfer(RuntimeOrigin::signed(3), multi, 5)); - - let call = call_transfer(6, 15); - let call_weight = call.get_dispatch_info().weight; - let data = call.encode(); - let hash = blake2_256(&data); - assert_ok!(Multisig::as_multi( - RuntimeOrigin::signed(1), - 2, - vec![2, 3], - None, - OpaqueCall::from_encoded(data), - true, - Weight::zero() - )); - assert_eq!(Balances::free_balance(1), 0); - assert_eq!(Balances::reserved_balance(1), 5); - - assert_ok!(Multisig::approve_as_multi( - RuntimeOrigin::signed(2), - 2, - vec![1, 3], - Some(now()), - hash, - call_weight - )); - assert_eq!(Balances::free_balance(1), 5); - assert_eq!(Balances::reserved_balance(1), 0); - }); -} - -#[test] -fn multisig_deposit_is_taken_and_returned_with_alt_call_storage() { - new_test_ext().execute_with(|| { - let multi = Multisig::multi_account_id(&[1, 2, 3][..], 3); - assert_ok!(Balances::transfer(RuntimeOrigin::signed(1), multi, 5)); - assert_ok!(Balances::transfer(RuntimeOrigin::signed(2), multi, 5)); - assert_ok!(Balances::transfer(RuntimeOrigin::signed(3), multi, 5)); - - let call = call_transfer(6, 15); - let call_weight = call.get_dispatch_info().weight; - let data = call.encode(); - let hash = blake2_256(&data); - - assert_ok!(Multisig::approve_as_multi( - RuntimeOrigin::signed(1), - 3, - vec![2, 3], - None, - hash, - Weight::zero() - )); - assert_eq!(Balances::free_balance(1), 1); - assert_eq!(Balances::reserved_balance(1), 4); - - assert_ok!(Multisig::as_multi( - RuntimeOrigin::signed(2), - 3, - vec![1, 3], - Some(now()), - OpaqueCall::from_encoded(data), - true, - Weight::zero() - )); - assert_eq!(Balances::free_balance(2), 3); - assert_eq!(Balances::reserved_balance(2), 2); - assert_eq!(Balances::free_balance(1), 1); - assert_eq!(Balances::reserved_balance(1), 4); - - assert_ok!(Multisig::approve_as_multi( - RuntimeOrigin::signed(3), - 3, - vec![1, 2], - Some(now()), - hash, - call_weight - )); - assert_eq!(Balances::free_balance(1), 5); - assert_eq!(Balances::reserved_balance(1), 0); - assert_eq!(Balances::free_balance(2), 5); - assert_eq!(Balances::reserved_balance(2), 0); - }); -} - #[test] fn cancel_multisig_returns_deposit() { new_test_ext().execute_with(|| { @@ -298,8 +204,8 @@ fn timepoint_checking_works() { assert_ok!(Balances::transfer(RuntimeOrigin::signed(2), multi, 5)); assert_ok!(Balances::transfer(RuntimeOrigin::signed(3), multi, 5)); - let call = call_transfer(6, 15).encode(); - let hash = blake2_256(&call); + let call = call_transfer(6, 15); + let hash = blake2_256(&call.encode()); assert_noop!( Multisig::approve_as_multi( @@ -328,63 +234,19 @@ fn timepoint_checking_works() { 2, vec![1, 3], None, - OpaqueCall::from_encoded(call.clone()), - false, + call.clone(), Weight::zero() ), Error::::NoTimepoint, ); let later = Timepoint { index: 1, ..now() }; assert_noop!( - Multisig::as_multi( - RuntimeOrigin::signed(2), - 2, - vec![1, 3], - Some(later), - OpaqueCall::from_encoded(call), - false, - Weight::zero() - ), + Multisig::as_multi(RuntimeOrigin::signed(2), 2, vec![1, 3], Some(later), call, Weight::zero()), Error::::WrongTimepoint, ); }); } -#[test] -fn multisig_2_of_3_works_with_call_storing() { - new_test_ext().execute_with(|| { - let multi = Multisig::multi_account_id(&[1, 2, 3][..], 2); - assert_ok!(Balances::transfer(RuntimeOrigin::signed(1), multi, 5)); - assert_ok!(Balances::transfer(RuntimeOrigin::signed(2), multi, 5)); - assert_ok!(Balances::transfer(RuntimeOrigin::signed(3), multi, 5)); - - let call = call_transfer(6, 15); - let call_weight = call.get_dispatch_info().weight; - let data = call.encode(); - let hash = blake2_256(&data); - assert_ok!(Multisig::as_multi( - RuntimeOrigin::signed(1), - 2, - vec![2, 3], - None, - OpaqueCall::from_encoded(data), - true, - Weight::zero() - )); - assert_eq!(Balances::free_balance(6), 0); - - assert_ok!(Multisig::approve_as_multi( - RuntimeOrigin::signed(2), - 2, - vec![1, 3], - Some(now()), - hash, - call_weight - )); - assert_eq!(Balances::free_balance(6), 15); - }); -} - #[test] fn multisig_2_of_3_works() { new_test_ext().execute_with(|| { @@ -395,8 +257,7 @@ fn multisig_2_of_3_works() { let call = call_transfer(6, 15); let call_weight = call.get_dispatch_info().weight; - let data = call.encode(); - let hash = blake2_256(&data); + let hash = blake2_256(&call.encode()); assert_ok!(Multisig::approve_as_multi( RuntimeOrigin::signed(1), 2, @@ -412,8 +273,7 @@ fn multisig_2_of_3_works() { 2, vec![1, 3], Some(now()), - OpaqueCall::from_encoded(data), - false, + call, call_weight )); assert_eq!(Balances::free_balance(6), 15); @@ -430,8 +290,7 @@ fn multisig_3_of_3_works() { let call = call_transfer(6, 15); let call_weight = call.get_dispatch_info().weight; - let data = call.encode(); - let hash = blake2_256(&data); + let hash = blake2_256(&call.encode()); assert_ok!(Multisig::approve_as_multi( RuntimeOrigin::signed(1), 3, @@ -455,8 +314,7 @@ fn multisig_3_of_3_works() { 3, vec![1, 2], Some(now()), - OpaqueCall::from_encoded(data), - false, + call, call_weight )); assert_eq!(Balances::free_balance(6), 15); @@ -492,68 +350,6 @@ fn cancel_multisig_works() { }); } -#[test] -fn cancel_multisig_with_call_storage_works() { - new_test_ext().execute_with(|| { - let call = call_transfer(6, 15).encode(); - let hash = blake2_256(&call); - assert_ok!(Multisig::as_multi( - RuntimeOrigin::signed(1), - 3, - vec![2, 3], - None, - OpaqueCall::from_encoded(call), - true, - Weight::zero() - )); - assert_eq!(Balances::free_balance(1), 4); - assert_ok!(Multisig::approve_as_multi( - RuntimeOrigin::signed(2), - 3, - vec![1, 3], - Some(now()), - hash, - Weight::zero() - )); - assert_noop!( - Multisig::cancel_as_multi(RuntimeOrigin::signed(2), 3, vec![1, 3], now(), hash), - Error::::NotOwner, - ); - assert_ok!(Multisig::cancel_as_multi(RuntimeOrigin::signed(1), 3, vec![2, 3], now(), hash),); - assert_eq!(Balances::free_balance(1), 10); - }); -} - -#[test] -fn cancel_multisig_with_alt_call_storage_works() { - new_test_ext().execute_with(|| { - let call = call_transfer(6, 15).encode(); - let hash = blake2_256(&call); - assert_ok!(Multisig::approve_as_multi( - RuntimeOrigin::signed(1), - 3, - vec![2, 3], - None, - hash, - Weight::zero() - )); - assert_eq!(Balances::free_balance(1), 6); - assert_ok!(Multisig::as_multi( - RuntimeOrigin::signed(2), - 3, - vec![1, 3], - Some(now()), - OpaqueCall::from_encoded(call), - true, - Weight::zero() - )); - assert_eq!(Balances::free_balance(2), 8); - assert_ok!(Multisig::cancel_as_multi(RuntimeOrigin::signed(1), 3, vec![2, 3], now(), hash)); - assert_eq!(Balances::free_balance(1), 10); - assert_eq!(Balances::free_balance(2), 10); - }); -} - #[test] fn multisig_2_of_3_as_multi_works() { new_test_ext().execute_with(|| { @@ -564,14 +360,12 @@ fn multisig_2_of_3_as_multi_works() { let call = call_transfer(6, 15); let call_weight = call.get_dispatch_info().weight; - let data = call.encode(); assert_ok!(Multisig::as_multi( RuntimeOrigin::signed(1), 2, vec![2, 3], None, - OpaqueCall::from_encoded(data.clone()), - false, + call.clone(), Weight::zero() )); assert_eq!(Balances::free_balance(6), 0); @@ -581,8 +375,7 @@ fn multisig_2_of_3_as_multi_works() { 2, vec![1, 3], Some(now()), - OpaqueCall::from_encoded(data), - false, + call, call_weight )); assert_eq!(Balances::free_balance(6), 15); @@ -599,18 +392,15 @@ fn multisig_2_of_3_as_multi_with_many_calls_works() { let call1 = call_transfer(6, 10); let call1_weight = call1.get_dispatch_info().weight; - let data1 = call1.encode(); let call2 = call_transfer(7, 5); let call2_weight = call2.get_dispatch_info().weight; - let data2 = call2.encode(); assert_ok!(Multisig::as_multi( RuntimeOrigin::signed(1), 2, vec![2, 3], None, - OpaqueCall::from_encoded(data1.clone()), - false, + call1.clone(), Weight::zero() )); assert_ok!(Multisig::as_multi( @@ -618,8 +408,7 @@ fn multisig_2_of_3_as_multi_with_many_calls_works() { 2, vec![1, 3], None, - OpaqueCall::from_encoded(data2.clone()), - false, + call2.clone(), Weight::zero() )); assert_ok!(Multisig::as_multi( @@ -627,8 +416,7 @@ fn multisig_2_of_3_as_multi_with_many_calls_works() { 2, vec![1, 2], Some(now()), - OpaqueCall::from_encoded(data1), - false, + call1, call1_weight )); assert_ok!(Multisig::as_multi( @@ -636,8 +424,7 @@ fn multisig_2_of_3_as_multi_with_many_calls_works() { 2, vec![1, 2], Some(now()), - OpaqueCall::from_encoded(data2), - false, + call2, call2_weight )); @@ -656,15 +443,13 @@ fn multisig_2_of_3_cannot_reissue_same_call() { let call = call_transfer(6, 10); let call_weight = call.get_dispatch_info().weight; - let data = call.encode(); - let hash = blake2_256(&data); + let hash = blake2_256(&call.encode()); assert_ok!(Multisig::as_multi( RuntimeOrigin::signed(1), 2, vec![2, 3], None, - OpaqueCall::from_encoded(data.clone()), - false, + call.clone(), Weight::zero() )); assert_ok!(Multisig::as_multi( @@ -672,8 +457,7 @@ fn multisig_2_of_3_cannot_reissue_same_call() { 2, vec![1, 3], Some(now()), - OpaqueCall::from_encoded(data.clone()), - false, + call.clone(), call_weight )); assert_eq!(Balances::free_balance(multi), 5); @@ -683,8 +467,7 @@ fn multisig_2_of_3_cannot_reissue_same_call() { 2, vec![2, 3], None, - OpaqueCall::from_encoded(data.clone()), - false, + call.clone(), Weight::zero() )); assert_ok!(Multisig::as_multi( @@ -692,8 +475,7 @@ fn multisig_2_of_3_cannot_reissue_same_call() { 2, vec![1, 2], Some(now()), - OpaqueCall::from_encoded(data), - false, + call.clone(), call_weight )); @@ -714,29 +496,13 @@ fn multisig_2_of_3_cannot_reissue_same_call() { #[test] fn minimum_threshold_check_works() { new_test_ext().execute_with(|| { - let call = call_transfer(6, 15).encode(); + let call = call_transfer(6, 15); assert_noop!( - Multisig::as_multi( - RuntimeOrigin::signed(1), - 0, - vec![2], - None, - OpaqueCall::from_encoded(call.clone()), - false, - Weight::zero() - ), + Multisig::as_multi(RuntimeOrigin::signed(1), 0, vec![2], None, call.clone(), Weight::zero()), Error::::MinimumThreshold, ); assert_noop!( - Multisig::as_multi( - RuntimeOrigin::signed(1), - 1, - vec![2], - None, - OpaqueCall::from_encoded(call.clone()), - false, - Weight::zero() - ), + Multisig::as_multi(RuntimeOrigin::signed(1), 1, vec![2], None, call.clone(), Weight::zero()), Error::::MinimumThreshold, ); }); @@ -745,15 +511,14 @@ fn minimum_threshold_check_works() { #[test] fn too_many_signatories_fails() { new_test_ext().execute_with(|| { - let call = call_transfer(6, 15).encode(); + let call = call_transfer(6, 15); assert_noop!( Multisig::as_multi( RuntimeOrigin::signed(1), 2, vec![2, 3, 4], None, - OpaqueCall::from_encoded(call), - false, + call.clone(), Weight::zero() ), Error::::TooManySignatories, @@ -815,8 +580,8 @@ fn multisig_1_of_3_works() { assert_ok!(Balances::transfer(RuntimeOrigin::signed(2), multi, 5)); assert_ok!(Balances::transfer(RuntimeOrigin::signed(3), multi, 5)); - let call = call_transfer(6, 15).encode(); - let hash = blake2_256(&call); + let call = call_transfer(6, 15); + let hash = blake2_256(&call.encode()); assert_noop!( Multisig::approve_as_multi( RuntimeOrigin::signed(1), @@ -834,8 +599,7 @@ fn multisig_1_of_3_works() { 1, vec![2, 3], None, - OpaqueCall::from_encoded(call), - false, + call.clone(), Weight::zero() ), Error::::MinimumThreshold, @@ -871,28 +635,18 @@ fn weight_check_works() { assert_ok!(Balances::transfer(RuntimeOrigin::signed(3), multi, 5)); let call = call_transfer(6, 15); - let data = call.encode(); assert_ok!(Multisig::as_multi( RuntimeOrigin::signed(1), 2, vec![2, 3], None, - OpaqueCall::from_encoded(data.clone()), - false, + call.clone(), Weight::zero() )); assert_eq!(Balances::free_balance(6), 0); assert_noop!( - Multisig::as_multi( - RuntimeOrigin::signed(2), - 2, - vec![1, 3], - Some(now()), - OpaqueCall::from_encoded(data), - false, - Weight::zero() - ), + Multisig::as_multi(RuntimeOrigin::signed(2), 2, vec![1, 3], Some(now()), call, Weight::zero()), Error::::MaxWeightTooLow, ); }); @@ -911,8 +665,7 @@ fn multisig_handles_no_preimage_after_all_approve() { let call = call_transfer(6, 15); let call_weight = call.get_dispatch_info().weight; - let data = call.encode(); - let hash = blake2_256(&data); + let hash = blake2_256(&call.encode()); assert_ok!(Multisig::approve_as_multi( RuntimeOrigin::signed(1), 3, @@ -944,8 +697,7 @@ fn multisig_handles_no_preimage_after_all_approve() { 3, vec![1, 2], Some(now()), - OpaqueCall::from_encoded(data), - false, + call, call_weight )); assert_eq!(Balances::free_balance(6), 15);