Migrate pallet-collective to the new pallet attribute macro (#9115)

* Migrate pallet-collective to the new pallet attribute macro

Signed-off-by: koushiro <koushiro.cqx@gmail.com>

* Add migrations

Signed-off-by: koushiro <koushiro.cqx@gmail.com>

* some nits

Signed-off-by: koushiro <koushiro.cqx@gmail.com>

* fix some indent

Signed-off-by: koushiro <koushiro.cqx@gmail.com>

* Fix

Signed-off-by: koushiro <koushiro.cqx@gmail.com>

* Fix

Signed-off-by: koushiro <koushiro.cqx@gmail.com>

* fmt

* fix migration

* fix migration

* fmt

* finally fix migration

* keep the storages public as they were

* Some nits

Signed-off-by: koushiro <koushiro.cqx@gmail.com>

* Fix migration

Signed-off-by: koushiro <koushiro.cqx@gmail.com>

* Fix migration and Add test

Signed-off-by: koushiro <koushiro.cqx@gmail.com>

* Some nits

Signed-off-by: koushiro <koushiro.cqx@gmail.com>

* improve test

Signed-off-by: koushiro <koushiro.cqx@gmail.com>

* improve test

Signed-off-by: koushiro <koushiro.cqx@gmail.com>

* Revert the changes of membership

Signed-off-by: koushiro <koushiro.cqx@gmail.com>

* Some nits

Signed-off-by: koushiro <koushiro.cqx@gmail.com>

* Fix test

Signed-off-by: koushiro <koushiro.cqx@gmail.com>

* Fix test

Signed-off-by: koushiro <koushiro.cqx@gmail.com>

* Some nits

Signed-off-by: koushiro <koushiro.cqx@gmail.com>

* don't assert that there is something at the old prefix in the pre-migrate

Signed-off-by: koushiro <koushiro.cqx@gmail.com>

* cargo fmt

Co-authored-by: thiolliere <gui.thiolliere@gmail.com>
This commit is contained in:
Qinxuan Chen
2021-09-14 19:42:26 +08:00
committed by GitHub
parent e4a8d60015
commit 902739f96c
6 changed files with 655 additions and 361 deletions
+12 -11
View File
@@ -13,34 +13,35 @@ readme = "README.md"
targets = ["x86_64-unknown-linux-gnu"]
[dependencies]
codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false, features = [
"derive",
] }
codec = { package = "parity-scale-codec", version = "2.0.0", default-features = false, features = ["derive"] }
log = { version = "0.4.14", default-features = false }
sp-core = { version = "4.0.0-dev", default-features = false, path = "../../primitives/core" }
sp-std = { version = "4.0.0-dev", default-features = false, path = "../../primitives/std" }
sp-io = { version = "4.0.0-dev", default-features = false, path = "../../primitives/io" }
sp-runtime = { version = "4.0.0-dev", default-features = false, path = "../../primitives/runtime" }
sp-std = { version = "4.0.0-dev", default-features = false, path = "../../primitives/std" }
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" }
log = { version = "0.4.14", default-features = false }
[features]
default = ["std"]
std = [
"codec/std",
"sp-core/std",
"sp-std/std",
"sp-io/std",
"frame-support/std",
"sp-runtime/std",
"frame-system/std",
"log/std",
"sp-core/std",
"sp-io/std",
"sp-runtime/std",
"sp-std/std",
"frame-benchmarking/std",
"frame-support/std",
"frame-system/std",
]
runtime-benchmarks = [
"frame-benchmarking",
"sp-runtime/runtime-benchmarks",
"frame-support/runtime-benchmarks",
"frame-system/runtime-benchmarks",
]
try-runtime = ["frame-support/try-runtime"]
+62 -63
View File
@@ -18,26 +18,25 @@
//! Staking pallet benchmarking.
use super::*;
use crate::Pallet as Collective;
use frame_benchmarking::{
account, benchmarks_instance, impl_benchmark_test_suite, whitelisted_caller,
};
use frame_system::RawOrigin as SystemOrigin;
use sp_runtime::traits::Bounded;
use sp_std::mem::size_of;
use crate::Module as Collective;
use frame_system::{Call as SystemCall, Pallet as System};
use frame_benchmarking::{
account, benchmarks_instance_pallet, impl_benchmark_test_suite, whitelisted_caller,
};
use frame_system::{Call as SystemCall, Pallet as System, RawOrigin as SystemOrigin};
const SEED: u32 = 0;
const MAX_BYTES: u32 = 1_024;
fn assert_last_event<T: Config<I>, I: Instance>(generic_event: <T as Config<I>>::Event) {
fn assert_last_event<T: Config<I>, I: 'static>(generic_event: <T as Config<I>>::Event) {
frame_system::Pallet::<T>::assert_last_event(generic_event.into());
}
benchmarks_instance! {
benchmarks_instance_pallet! {
set_members {
let m in 1 .. T::MaxMembers::get();
let n in 1 .. T::MaxMembers::get();
@@ -53,7 +52,7 @@ benchmarks_instance! {
}
let old_members_count = old_members.len() as u32;
Collective::<T, _>::set_members(
Collective::<T, I>::set_members(
SystemOrigin::Root.into(),
old_members.clone(),
Some(last_old_member.clone()),
@@ -67,7 +66,7 @@ benchmarks_instance! {
for i in 0 .. p {
// Proposals should be different so that different proposal hashes are generated
let proposal: T::Proposal = SystemCall::<T>::remark(vec![i as u8; length]).into();
Collective::<T, _>::propose(
Collective::<T, I>::propose(
SystemOrigin::Signed(last_old_member.clone()).into(),
threshold,
Box::new(proposal.clone()),
@@ -80,7 +79,7 @@ benchmarks_instance! {
for j in 2 .. m - 1 {
let voter = &old_members[j as usize];
let approve = true;
Collective::<T, _>::vote(
Collective::<T, I>::vote(
SystemOrigin::Signed(voter.clone()).into(),
hash,
i,
@@ -101,7 +100,7 @@ benchmarks_instance! {
}: _(SystemOrigin::Root, new_members.clone(), Some(last_member), T::MaxMembers::get())
verify {
new_members.sort();
assert_eq!(Collective::<T, _>::members(), new_members);
assert_eq!(Collective::<T, I>::members(), new_members);
}
execute {
@@ -120,7 +119,7 @@ benchmarks_instance! {
let caller: T::AccountId = whitelisted_caller();
members.push(caller.clone());
Collective::<T, _>::set_members(SystemOrigin::Root.into(), members, None, T::MaxMembers::get())?;
Collective::<T, I>::set_members(SystemOrigin::Root.into(), members, None, T::MaxMembers::get())?;
let proposal: T::Proposal = SystemCall::<T>::remark(vec![1; b as usize]).into();
@@ -129,7 +128,7 @@ benchmarks_instance! {
let proposal_hash = T::Hashing::hash_of(&proposal);
// Note that execution fails due to mis-matched origin
assert_last_event::<T, I>(
RawEvent::MemberExecuted(proposal_hash, Err(DispatchError::BadOrigin)).into()
Event::MemberExecuted(proposal_hash, Err(DispatchError::BadOrigin)).into()
);
}
@@ -150,7 +149,7 @@ benchmarks_instance! {
let caller: T::AccountId = whitelisted_caller();
members.push(caller.clone());
Collective::<T, _>::set_members(SystemOrigin::Root.into(), members, None, T::MaxMembers::get())?;
Collective::<T, I>::set_members(SystemOrigin::Root.into(), members, None, T::MaxMembers::get())?;
let proposal: T::Proposal = SystemCall::<T>::remark(vec![1; b as usize]).into();
let threshold = 1;
@@ -160,7 +159,7 @@ benchmarks_instance! {
let proposal_hash = T::Hashing::hash_of(&proposal);
// Note that execution fails due to mis-matched origin
assert_last_event::<T, I>(
RawEvent::Executed(proposal_hash, Err(DispatchError::BadOrigin)).into()
Event::Executed(proposal_hash, Err(DispatchError::BadOrigin)).into()
);
}
@@ -180,14 +179,14 @@ benchmarks_instance! {
}
let caller: T::AccountId = whitelisted_caller();
members.push(caller.clone());
Collective::<T, _>::set_members(SystemOrigin::Root.into(), members, None, T::MaxMembers::get())?;
Collective::<T, I>::set_members(SystemOrigin::Root.into(), members, None, T::MaxMembers::get())?;
let threshold = m;
// Add previous proposals.
for i in 0 .. p - 1 {
// Proposals should be different so that different proposal hashes are generated
let proposal: T::Proposal = SystemCall::<T>::remark(vec![i as u8; b as usize]).into();
Collective::<T, _>::propose(
Collective::<T, I>::propose(
SystemOrigin::Signed(caller.clone()).into(),
threshold,
Box::new(proposal),
@@ -195,16 +194,16 @@ benchmarks_instance! {
)?;
}
assert_eq!(Collective::<T, _>::proposals().len(), (p - 1) as usize);
assert_eq!(Collective::<T, I>::proposals().len(), (p - 1) as usize);
let proposal: T::Proposal = SystemCall::<T>::remark(vec![p as u8; b as usize]).into();
}: propose(SystemOrigin::Signed(caller.clone()), threshold, Box::new(proposal.clone()), bytes_in_storage)
verify {
// New proposal is recorded
assert_eq!(Collective::<T, _>::proposals().len(), p as usize);
assert_eq!(Collective::<T, I>::proposals().len(), p as usize);
let proposal_hash = T::Hashing::hash_of(&proposal);
assert_last_event::<T, I>(RawEvent::Proposed(caller, p - 1, proposal_hash, threshold).into());
assert_last_event::<T, I>(Event::Proposed(caller, p - 1, proposal_hash, threshold).into());
}
vote {
@@ -225,7 +224,7 @@ benchmarks_instance! {
}
let voter: T::AccountId = account("voter", 0, SEED);
members.push(voter.clone());
Collective::<T, _>::set_members(SystemOrigin::Root.into(), members.clone(), None, T::MaxMembers::get())?;
Collective::<T, I>::set_members(SystemOrigin::Root.into(), members.clone(), None, T::MaxMembers::get())?;
// Threshold is 1 less than the number of members so that one person can vote nay
let threshold = m - 1;
@@ -235,7 +234,7 @@ benchmarks_instance! {
for i in 0 .. p {
// Proposals should be different so that different proposal hashes are generated
let proposal: T::Proposal = SystemCall::<T>::remark(vec![i as u8; b as usize]).into();
Collective::<T, _>::propose(
Collective::<T, I>::propose(
SystemOrigin::Signed(proposer.clone()).into(),
threshold,
Box::new(proposal.clone()),
@@ -249,7 +248,7 @@ benchmarks_instance! {
for j in 0 .. m - 3 {
let voter = &members[j as usize];
let approve = true;
Collective::<T, _>::vote(
Collective::<T, I>::vote(
SystemOrigin::Signed(voter.clone()).into(),
last_hash.clone(),
index,
@@ -258,14 +257,14 @@ benchmarks_instance! {
}
// Voter votes aye without resolving the vote.
let approve = true;
Collective::<T, _>::vote(
Collective::<T, I>::vote(
SystemOrigin::Signed(voter.clone()).into(),
last_hash.clone(),
index,
approve,
)?;
assert_eq!(Collective::<T, _>::proposals().len(), p as usize);
assert_eq!(Collective::<T, I>::proposals().len(), p as usize);
// Voter switches vote to nay, but does not kill the vote, just updates + inserts
let approve = false;
@@ -276,8 +275,8 @@ benchmarks_instance! {
}: _(SystemOrigin::Signed(voter), last_hash.clone(), index, approve)
verify {
// All proposals exist and the last proposal has just been updated.
assert_eq!(Collective::<T, _>::proposals().len(), p as usize);
let voting = Collective::<T, _>::voting(&last_hash).ok_or("Proposal Missing")?;
assert_eq!(Collective::<T, I>::proposals().len(), p as usize);
let voting = Collective::<T, I>::voting(&last_hash).ok_or("Proposal Missing")?;
assert_eq!(voting.ayes.len(), (m - 3) as usize);
assert_eq!(voting.nays.len(), 1);
}
@@ -300,7 +299,7 @@ benchmarks_instance! {
}
let voter: T::AccountId = account("voter", 0, SEED);
members.push(voter.clone());
Collective::<T, _>::set_members(SystemOrigin::Root.into(), members.clone(), None, T::MaxMembers::get())?;
Collective::<T, I>::set_members(SystemOrigin::Root.into(), members.clone(), None, T::MaxMembers::get())?;
// Threshold is total members so that one nay will disapprove the vote
let threshold = m;
@@ -310,7 +309,7 @@ benchmarks_instance! {
for i in 0 .. p {
// Proposals should be different so that different proposal hashes are generated
let proposal: T::Proposal = SystemCall::<T>::remark(vec![i as u8; bytes as usize]).into();
Collective::<T, _>::propose(
Collective::<T, I>::propose(
SystemOrigin::Signed(proposer.clone()).into(),
threshold,
Box::new(proposal.clone()),
@@ -324,7 +323,7 @@ benchmarks_instance! {
for j in 0 .. m - 2 {
let voter = &members[j as usize];
let approve = true;
Collective::<T, _>::vote(
Collective::<T, I>::vote(
SystemOrigin::Signed(voter.clone()).into(),
last_hash.clone(),
index,
@@ -333,18 +332,18 @@ benchmarks_instance! {
}
// Voter votes aye without resolving the vote.
let approve = true;
Collective::<T, _>::vote(
Collective::<T, I>::vote(
SystemOrigin::Signed(voter.clone()).into(),
last_hash.clone(),
index,
approve,
)?;
assert_eq!(Collective::<T, _>::proposals().len(), p as usize);
assert_eq!(Collective::<T, I>::proposals().len(), p as usize);
// Voter switches vote to nay, which kills the vote
let approve = false;
Collective::<T, _>::vote(
Collective::<T, I>::vote(
SystemOrigin::Signed(voter.clone()).into(),
last_hash.clone(),
index,
@@ -357,8 +356,8 @@ benchmarks_instance! {
}: close(SystemOrigin::Signed(voter), last_hash.clone(), index, Weight::max_value(), bytes_in_storage)
verify {
// The last proposal is removed.
assert_eq!(Collective::<T, _>::proposals().len(), (p - 1) as usize);
assert_last_event::<T, I>(RawEvent::Disapproved(last_hash).into());
assert_eq!(Collective::<T, I>::proposals().len(), (p - 1) as usize);
assert_last_event::<T, I>(Event::Disapproved(last_hash).into());
}
close_early_approved {
@@ -377,7 +376,7 @@ benchmarks_instance! {
}
let caller: T::AccountId = whitelisted_caller();
members.push(caller.clone());
Collective::<T, _>::set_members(SystemOrigin::Root.into(), members.clone(), None, T::MaxMembers::get())?;
Collective::<T, I>::set_members(SystemOrigin::Root.into(), members.clone(), None, T::MaxMembers::get())?;
// Threshold is 2 so any two ayes will approve the vote
let threshold = 2;
@@ -387,7 +386,7 @@ benchmarks_instance! {
for i in 0 .. p {
// Proposals should be different so that different proposal hashes are generated
let proposal: T::Proposal = SystemCall::<T>::remark(vec![i as u8; b as usize]).into();
Collective::<T, _>::propose(
Collective::<T, I>::propose(
SystemOrigin::Signed(caller.clone()).into(),
threshold,
Box::new(proposal.clone()),
@@ -397,7 +396,7 @@ benchmarks_instance! {
}
// Caller switches vote to nay on their own proposal, allowing them to be the deciding approval vote
Collective::<T, _>::vote(
Collective::<T, I>::vote(
SystemOrigin::Signed(caller.clone()).into(),
last_hash.clone(),
p - 1,
@@ -408,7 +407,7 @@ benchmarks_instance! {
for j in 2 .. m - 1 {
let voter = &members[j as usize];
let approve = false;
Collective::<T, _>::vote(
Collective::<T, I>::vote(
SystemOrigin::Signed(voter.clone()).into(),
last_hash.clone(),
p - 1,
@@ -417,19 +416,19 @@ benchmarks_instance! {
}
// Member zero is the first aye
Collective::<T, _>::vote(
Collective::<T, I>::vote(
SystemOrigin::Signed(members[0].clone()).into(),
last_hash.clone(),
p - 1,
true,
)?;
assert_eq!(Collective::<T, _>::proposals().len(), p as usize);
assert_eq!(Collective::<T, I>::proposals().len(), p as usize);
// Caller switches vote to aye, which passes the vote
let index = p - 1;
let approve = true;
Collective::<T, _>::vote(
Collective::<T, I>::vote(
SystemOrigin::Signed(caller.clone()).into(),
last_hash.clone(),
index, approve,
@@ -438,8 +437,8 @@ benchmarks_instance! {
}: close(SystemOrigin::Signed(caller), last_hash.clone(), index, Weight::max_value(), bytes_in_storage)
verify {
// The last proposal is removed.
assert_eq!(Collective::<T, _>::proposals().len(), (p - 1) as usize);
assert_last_event::<T, I>(RawEvent::Executed(last_hash, Err(DispatchError::BadOrigin)).into());
assert_eq!(Collective::<T, I>::proposals().len(), (p - 1) as usize);
assert_last_event::<T, I>(Event::Executed(last_hash, Err(DispatchError::BadOrigin)).into());
}
close_disapproved {
@@ -458,7 +457,7 @@ benchmarks_instance! {
}
let caller: T::AccountId = whitelisted_caller();
members.push(caller.clone());
Collective::<T, _>::set_members(
Collective::<T, I>::set_members(
SystemOrigin::Root.into(),
members.clone(),
Some(caller.clone()),
@@ -473,7 +472,7 @@ benchmarks_instance! {
for i in 0 .. p {
// Proposals should be different so that different proposal hashes are generated
let proposal: T::Proposal = SystemCall::<T>::remark(vec![i as u8; bytes as usize]).into();
Collective::<T, _>::propose(
Collective::<T, I>::propose(
SystemOrigin::Signed(caller.clone()).into(),
threshold,
Box::new(proposal.clone()),
@@ -488,7 +487,7 @@ benchmarks_instance! {
for j in 2 .. m - 1 {
let voter = &members[j as usize];
let approve = true;
Collective::<T, _>::vote(
Collective::<T, I>::vote(
SystemOrigin::Signed(voter.clone()).into(),
last_hash.clone(),
index,
@@ -497,7 +496,7 @@ benchmarks_instance! {
}
// caller is prime, prime votes nay
Collective::<T, _>::vote(
Collective::<T, I>::vote(
SystemOrigin::Signed(caller.clone()).into(),
last_hash.clone(),
index,
@@ -505,13 +504,13 @@ benchmarks_instance! {
)?;
System::<T>::set_block_number(T::BlockNumber::max_value());
assert_eq!(Collective::<T, _>::proposals().len(), p as usize);
assert_eq!(Collective::<T, I>::proposals().len(), p as usize);
// Prime nay will close it as disapproved
}: close(SystemOrigin::Signed(caller), last_hash, index, Weight::max_value(), bytes_in_storage)
verify {
assert_eq!(Collective::<T, _>::proposals().len(), (p - 1) as usize);
assert_last_event::<T, I>(RawEvent::Disapproved(last_hash).into());
assert_eq!(Collective::<T, I>::proposals().len(), (p - 1) as usize);
assert_last_event::<T, I>(Event::Disapproved(last_hash).into());
}
close_approved {
@@ -530,7 +529,7 @@ benchmarks_instance! {
}
let caller: T::AccountId = whitelisted_caller();
members.push(caller.clone());
Collective::<T, _>::set_members(
Collective::<T, I>::set_members(
SystemOrigin::Root.into(),
members.clone(),
Some(caller.clone()),
@@ -545,7 +544,7 @@ benchmarks_instance! {
for i in 0 .. p {
// Proposals should be different so that different proposal hashes are generated
let proposal: T::Proposal = SystemCall::<T>::remark(vec![i as u8; b as usize]).into();
Collective::<T, _>::propose(
Collective::<T, I>::propose(
SystemOrigin::Signed(caller.clone()).into(),
threshold,
Box::new(proposal.clone()),
@@ -567,7 +566,7 @@ benchmarks_instance! {
for j in 2 .. m - 1 {
let voter = &members[j as usize];
let approve = false;
Collective::<T, _>::vote(
Collective::<T, I>::vote(
SystemOrigin::Signed(voter.clone()).into(),
last_hash.clone(),
p - 1,
@@ -577,13 +576,13 @@ benchmarks_instance! {
// caller is prime, prime already votes aye by creating the proposal
System::<T>::set_block_number(T::BlockNumber::max_value());
assert_eq!(Collective::<T, _>::proposals().len(), p as usize);
assert_eq!(Collective::<T, I>::proposals().len(), p as usize);
// Prime aye will close it as approved
}: close(SystemOrigin::Signed(caller), last_hash, p - 1, Weight::max_value(), bytes_in_storage)
verify {
assert_eq!(Collective::<T, _>::proposals().len(), (p - 1) as usize);
assert_last_event::<T, I>(RawEvent::Executed(last_hash, Err(DispatchError::BadOrigin)).into());
assert_eq!(Collective::<T, I>::proposals().len(), (p - 1) as usize);
assert_last_event::<T, I>(Event::Executed(last_hash, Err(DispatchError::BadOrigin)).into());
}
disapprove_proposal {
@@ -601,7 +600,7 @@ benchmarks_instance! {
}
let caller: T::AccountId = account("caller", 0, SEED);
members.push(caller.clone());
Collective::<T, _>::set_members(
Collective::<T, I>::set_members(
SystemOrigin::Root.into(),
members.clone(),
Some(caller.clone()),
@@ -616,7 +615,7 @@ benchmarks_instance! {
for i in 0 .. p {
// Proposals should be different so that different proposal hashes are generated
let proposal: T::Proposal = SystemCall::<T>::remark(vec![i as u8; b as usize]).into();
Collective::<T, _>::propose(
Collective::<T, I>::propose(
SystemOrigin::Signed(caller.clone()).into(),
threshold,
Box::new(proposal.clone()),
@@ -626,12 +625,12 @@ benchmarks_instance! {
}
System::<T>::set_block_number(T::BlockNumber::max_value());
assert_eq!(Collective::<T, _>::proposals().len(), p as usize);
assert_eq!(Collective::<T, I>::proposals().len(), p as usize);
}: _(SystemOrigin::Root, last_hash)
verify {
assert_eq!(Collective::<T, _>::proposals().len(), (p - 1) as usize);
assert_last_event::<T, I>(RawEvent::Disapproved(last_hash).into());
assert_eq!(Collective::<T, I>::proposals().len(), (p - 1) as usize);
assert_last_event::<T, I>(Event::Disapproved(last_hash).into());
}
}
+281 -216
View File
@@ -45,29 +45,27 @@
use sp_core::u32_trait::Value as U32;
use sp_io::storage;
use sp_runtime::{traits::Hash, RuntimeDebug};
use sp_std::{prelude::*, result};
use sp_std::{marker::PhantomData, prelude::*, result};
use frame_support::{
codec::{Decode, Encode},
decl_error, decl_event, decl_module, decl_storage,
dispatch::{
DispatchError, DispatchResult, DispatchResultWithPostInfo, Dispatchable, Parameter,
PostDispatchInfo,
},
dispatch::{DispatchError, DispatchResultWithPostInfo, Dispatchable, PostDispatchInfo},
ensure,
traits::{Backing, ChangeMembers, EnsureOrigin, Get, GetBacking, InitializeMembers},
weights::{DispatchClass, GetDispatchInfo, Pays, Weight},
BoundedVec,
traits::{
Backing, ChangeMembers, EnsureOrigin, Get, GetBacking, InitializeMembers, StorageVersion,
},
weights::{GetDispatchInfo, Weight},
};
use frame_system::{self as system, ensure_root, ensure_signed};
#[cfg(test)]
mod tests;
#[cfg(feature = "runtime-benchmarks")]
mod benchmarking;
pub mod migrations;
pub mod weights;
pub use pallet::*;
pub use weights::WeightInfo;
/// Simple index type for proposal counting.
@@ -125,39 +123,6 @@ impl DefaultVote for MoreThanMajorityThenPrimeDefaultVote {
}
}
pub trait Config<I: Instance = DefaultInstance>: frame_system::Config {
/// The outer origin type.
type Origin: From<RawOrigin<Self::AccountId, I>>;
/// The outer call dispatch type.
type Proposal: Parameter
+ Dispatchable<Origin = <Self as Config<I>>::Origin, PostInfo = PostDispatchInfo>
+ From<frame_system::Call<Self>>
+ GetDispatchInfo;
/// The outer event type.
type Event: From<Event<Self, I>> + Into<<Self as frame_system::Config>::Event>;
/// The time-out for council motions.
type MotionDuration: Get<Self::BlockNumber>;
/// Maximum number of proposals allowed to be active in parallel.
type MaxProposals: Get<ProposalIndex>;
/// The maximum number of members supported by the pallet. Used for weight estimation.
///
/// NOTE:
/// + Benchmarks will need to be re-run and weights adjusted if this changes.
/// + This pallet assumes that dependents keep to the limit without enforcing it.
type MaxMembers: Get<MemberCount>;
/// Default vote strategy of this collective.
type DefaultVote: DefaultVote;
/// Weight information for extrinsics in this pallet.
type WeightInfo: WeightInfo;
}
/// Origin for the collective module.
#[derive(PartialEq, Eq, Clone, RuntimeDebug, Encode, Decode)]
pub enum RawOrigin<AccountId, I> {
@@ -166,7 +131,7 @@ pub enum RawOrigin<AccountId, I> {
/// It has been condoned by a single member of the collective.
Member(AccountId),
/// Dummy to manage the fact we have instancing.
_Phantom(sp_std::marker::PhantomData<I>),
_Phantom(PhantomData<I>),
}
impl<AccountId, I> GetBacking for RawOrigin<AccountId, I> {
@@ -178,11 +143,8 @@ impl<AccountId, I> GetBacking for RawOrigin<AccountId, I> {
}
}
/// Origin for the collective module.
pub type Origin<T, I = DefaultInstance> = RawOrigin<<T as frame_system::Config>::AccountId, I>;
#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug)]
/// Info for keeping track of a motion being voted on.
#[derive(PartialEq, Eq, Clone, Encode, Decode, RuntimeDebug)]
pub struct Votes<AccountId, BlockNumber> {
/// The proposal's unique index.
index: ProposalIndex,
@@ -196,69 +158,155 @@ pub struct Votes<AccountId, BlockNumber> {
end: BlockNumber,
}
decl_storage! {
trait Store for Module<T: Config<I>, I: Instance=DefaultInstance> as Collective {
/// The hashes of the active proposals.
pub Proposals get(fn proposals): BoundedVec<T::Hash, T::MaxProposals>;
/// Actual proposal for a given hash, if it's current.
pub ProposalOf get(fn proposal_of):
map hasher(identity) T::Hash => Option<<T as Config<I>>::Proposal>;
/// Votes on a given proposal, if it is ongoing.
pub Voting get(fn voting):
map hasher(identity) T::Hash => Option<Votes<T::AccountId, T::BlockNumber>>;
/// Proposals so far.
pub ProposalCount get(fn proposal_count): u32;
/// The current members of the collective. This is stored sorted (just by value).
pub Members get(fn members): Vec<T::AccountId>;
/// The prime member that helps determine the default vote behavior in case of absentations.
pub Prime get(fn prime): Option<T::AccountId>;
#[frame_support::pallet]
pub mod pallet {
use super::*;
use frame_support::pallet_prelude::*;
use frame_system::pallet_prelude::*;
/// The current storage version.
const STORAGE_VERSION: StorageVersion = StorageVersion::new(4);
#[pallet::pallet]
#[pallet::generate_store(pub(super) trait Store)]
#[pallet::storage_version(STORAGE_VERSION)]
pub struct Pallet<T, I = ()>(PhantomData<(T, I)>);
#[pallet::config]
pub trait Config<I: 'static = ()>: frame_system::Config {
/// The outer origin type.
type Origin: From<RawOrigin<Self::AccountId, I>>;
/// The outer call dispatch type.
type Proposal: Parameter
+ Dispatchable<Origin = <Self as Config<I>>::Origin, PostInfo = PostDispatchInfo>
+ From<frame_system::Call<Self>>
+ GetDispatchInfo;
/// The outer event type.
type Event: From<Event<Self, I>> + IsType<<Self as frame_system::Config>::Event>;
/// The time-out for council motions.
type MotionDuration: Get<Self::BlockNumber>;
/// Maximum number of proposals allowed to be active in parallel.
type MaxProposals: Get<ProposalIndex>;
/// The maximum number of members supported by the pallet. Used for weight estimation.
///
/// NOTE:
/// + Benchmarks will need to be re-run and weights adjusted if this changes.
/// + This pallet assumes that dependents keep to the limit without enforcing it.
type MaxMembers: Get<MemberCount>;
/// Default vote strategy of this collective.
type DefaultVote: DefaultVote;
/// Weight information for extrinsics in this pallet.
type WeightInfo: WeightInfo;
}
add_extra_genesis {
config(phantom): sp_std::marker::PhantomData<I>;
config(members): Vec<T::AccountId>;
build(|config| {
#[pallet::genesis_config]
pub struct GenesisConfig<T: Config<I>, I: 'static = ()> {
pub phantom: PhantomData<I>,
pub members: Vec<T::AccountId>,
}
#[cfg(feature = "std")]
impl<T: Config<I>, I: 'static> Default for GenesisConfig<T, I> {
fn default() -> Self {
Self { phantom: Default::default(), members: Default::default() }
}
}
#[pallet::genesis_build]
impl<T: Config<I>, I: 'static> GenesisBuild<T, I> for GenesisConfig<T, I> {
fn build(&self) {
use sp_std::collections::btree_set::BTreeSet;
let members_set: BTreeSet<_> = config.members.iter().collect();
assert!(members_set.len() == config.members.len(), "Members cannot contain duplicate accounts.");
let members_set: BTreeSet<_> = self.members.iter().collect();
assert_eq!(
members_set.len(),
self.members.len(),
"Members cannot contain duplicate accounts."
);
Module::<T, I>::initialize_members(&config.members)
});
Pallet::<T, I>::initialize_members(&self.members)
}
}
}
decl_event! {
pub enum Event<T, I=DefaultInstance> where
<T as frame_system::Config>::Hash,
<T as frame_system::Config>::AccountId,
{
/// Origin for the collective pallet.
#[pallet::origin]
pub type Origin<T, I = ()> = RawOrigin<<T as frame_system::Config>::AccountId, I>;
/// The hashes of the active proposals.
#[pallet::storage]
#[pallet::getter(fn proposals)]
pub type Proposals<T: Config<I>, I: 'static = ()> =
StorageValue<_, BoundedVec<T::Hash, T::MaxProposals>, ValueQuery>;
/// Actual proposal for a given hash, if it's current.
#[pallet::storage]
#[pallet::getter(fn proposal_of)]
pub type ProposalOf<T: Config<I>, I: 'static = ()> =
StorageMap<_, Identity, T::Hash, <T as Config<I>>::Proposal, OptionQuery>;
/// Votes on a given proposal, if it is ongoing.
#[pallet::storage]
#[pallet::getter(fn voting)]
pub type Voting<T: Config<I>, I: 'static = ()> =
StorageMap<_, Identity, T::Hash, Votes<T::AccountId, T::BlockNumber>, OptionQuery>;
/// Proposals so far.
#[pallet::storage]
#[pallet::getter(fn proposal_count)]
pub type ProposalCount<T: Config<I>, I: 'static = ()> = StorageValue<_, u32, ValueQuery>;
/// The current members of the collective. This is stored sorted (just by value).
#[pallet::storage]
#[pallet::getter(fn members)]
pub type Members<T: Config<I>, I: 'static = ()> =
StorageValue<_, Vec<T::AccountId>, ValueQuery>;
/// The prime member that helps determine the default vote behavior in case of absentations.
#[pallet::storage]
#[pallet::getter(fn prime)]
pub type Prime<T: Config<I>, I: 'static = ()> = StorageValue<_, T::AccountId, OptionQuery>;
#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
#[pallet::metadata(T::AccountId = "AccountId", T::Hash = "Hash")]
pub enum Event<T: Config<I>, I: 'static = ()> {
/// A motion (given hash) has been proposed (by given account) with a threshold (given
/// `MemberCount`).
/// \[account, proposal_index, proposal_hash, threshold\]
Proposed(AccountId, ProposalIndex, Hash, MemberCount),
Proposed(T::AccountId, ProposalIndex, T::Hash, MemberCount),
/// A motion (given hash) has been voted on by given account, leaving
/// a tally (yes votes and no votes given respectively as `MemberCount`).
/// \[account, proposal_hash, voted, yes, no\]
Voted(AccountId, Hash, bool, MemberCount, MemberCount),
Voted(T::AccountId, T::Hash, bool, MemberCount, MemberCount),
/// A motion was approved by the required threshold.
/// \[proposal_hash\]
Approved(Hash),
Approved(T::Hash),
/// A motion was not approved by the required threshold.
/// \[proposal_hash\]
Disapproved(Hash),
Disapproved(T::Hash),
/// A motion was executed; result will be `Ok` if it returned without error.
/// \[proposal_hash, result\]
Executed(Hash, DispatchResult),
Executed(T::Hash, DispatchResult),
/// A single member did some action; result will be `Ok` if it returned without error.
/// \[proposal_hash, result\]
MemberExecuted(Hash, DispatchResult),
MemberExecuted(T::Hash, DispatchResult),
/// A proposal was closed because its threshold was reached or after its duration was up.
/// \[proposal_hash, yes, no\]
Closed(Hash, MemberCount, MemberCount),
Closed(T::Hash, MemberCount, MemberCount),
}
}
decl_error! {
pub enum Error for Module<T: Config<I>, I: Instance> {
/// Old name generated by `decl_event`.
#[deprecated(note = "use `Event` instead")]
pub type RawEvent<T, I = ()> = Event<T, I>;
#[pallet::error]
pub enum Error<T, I = ()> {
/// Account is not a member
NotMember,
/// Duplicate proposals not allowed
@@ -280,31 +328,16 @@ decl_error! {
/// The given length bound for the proposal was too low.
WrongProposalLength,
}
}
/// Return the weight of a dispatch call result as an `Option`.
///
/// Will return the weight regardless of what the state of the result is.
fn get_result_weight(result: DispatchResultWithPostInfo) -> Option<Weight> {
match result {
Ok(post_info) => post_info.actual_weight,
Err(err) => err.post_info.actual_weight,
}
}
// Note that councillor operations are assigned to the operational class.
decl_module! {
pub struct Module<T: Config<I>, I: Instance=DefaultInstance> for enum Call where origin: <T as frame_system::Config>::Origin {
type Error = Error<T, I>;
fn deposit_event() = default;
// Note that councillor operations are assigned to the operational class.
#[pallet::call]
impl<T: Config<I>, I: 'static> Pallet<T, I> {
/// Set the collective's membership.
///
/// - `new_members`: The new member list. Be nice to the chain and provide it sorted.
/// - `prime`: The prime member whose vote sets the default.
/// - `old_count`: The upper bound for the previous number of members in storage.
/// Used for weight estimation.
/// - `old_count`: The upper bound for the previous number of members in storage. Used for
/// weight estimation.
///
/// Requires root origin.
///
@@ -318,20 +351,22 @@ decl_module! {
/// - `N` new-members-count (code- and governance-bounded)
/// - `P` proposals-count (code-bounded)
/// - DB:
/// - 1 storage mutation (codec `O(M)` read, `O(N)` write) for reading and writing the members
/// - 1 storage mutation (codec `O(M)` read, `O(N)` write) for reading and writing the
/// members
/// - 1 storage read (codec `O(P)`) for reading the proposals
/// - `P` storage mutations (codec `O(M)`) for updating the votes for each proposal
/// - 1 storage write (codec `O(1)`) for deleting the old `prime` and setting the new one
/// # </weight>
#[weight = (
#[pallet::weight((
T::WeightInfo::set_members(
*old_count, // M
new_members.len() as u32, // N
T::MaxProposals::get() // P
),
DispatchClass::Operational
)]
fn set_members(origin,
))]
pub fn set_members(
origin: OriginFor<T>,
new_members: Vec<T::AccountId>,
prime: Option<T::AccountId>,
old_count: MemberCount,
@@ -361,10 +396,11 @@ decl_module! {
Prime::<T, I>::set(prime);
Ok(Some(T::WeightInfo::set_members(
old.len() as u32, // M
old.len() as u32, // M
new_members.len() as u32, // N
T::MaxProposals::get(), // P
)).into())
T::MaxProposals::get(), // P
))
.into())
}
/// Dispatch a proposal from a member using the `Member` origin.
@@ -373,20 +409,22 @@ decl_module! {
///
/// # <weight>
/// ## Weight
/// - `O(M + P)` where `M` members-count (code-bounded) and `P` complexity of dispatching `proposal`
/// - `O(M + P)` where `M` members-count (code-bounded) and `P` complexity of dispatching
/// `proposal`
/// - DB: 1 read (codec `O(M)`) + DB access of `proposal`
/// - 1 event
/// # </weight>
#[weight = (
#[pallet::weight((
T::WeightInfo::execute(
*length_bound, // B
T::MaxMembers::get(), // M
).saturating_add(proposal.get_dispatch_info().weight), // P
DispatchClass::Operational
)]
fn execute(origin,
))]
pub fn execute(
origin: OriginFor<T>,
proposal: Box<<T as Config<I>>::Proposal>,
#[compact] length_bound: u32,
#[pallet::compact] length_bound: u32,
) -> DispatchResultWithPostInfo {
let who = ensure_signed(origin)?;
let members = Self::members();
@@ -396,16 +434,20 @@ decl_module! {
let proposal_hash = T::Hashing::hash_of(&proposal);
let result = proposal.dispatch(RawOrigin::Member(who).into());
Self::deposit_event(
RawEvent::MemberExecuted(proposal_hash, result.map(|_| ()).map_err(|e| e.error))
);
Self::deposit_event(Event::MemberExecuted(
proposal_hash,
result.map(|_| ()).map_err(|e| e.error),
));
Ok(get_result_weight(result).map(|w| {
T::WeightInfo::execute(
proposal_len as u32, // B
members.len() as u32, // M
).saturating_add(w) // P
}).into())
Ok(get_result_weight(result)
.map(|w| {
T::WeightInfo::execute(
proposal_len as u32, // B
members.len() as u32, // M
)
.saturating_add(w) // P
})
.into())
}
/// Add a new proposal to either be voted on or executed directly.
@@ -435,7 +477,7 @@ decl_module! {
/// - 1 storage write `Voting` (codec `O(M)`)
/// - 1 event
/// # </weight>
#[weight = (
#[pallet::weight((
if *threshold < 2 {
T::WeightInfo::propose_execute(
*length_bound, // B
@@ -449,11 +491,12 @@ decl_module! {
)
},
DispatchClass::Operational
)]
fn propose(origin,
#[compact] threshold: MemberCount,
))]
pub fn propose(
origin: OriginFor<T>,
#[pallet::compact] threshold: MemberCount,
proposal: Box<<T as Config<I>>::Proposal>,
#[compact] length_bound: u32
#[pallet::compact] length_bound: u32,
) -> DispatchResultWithPostInfo {
let who = ensure_signed(origin)?;
let members = Self::members();
@@ -462,43 +505,53 @@ decl_module! {
let proposal_len = proposal.using_encoded(|x| x.len());
ensure!(proposal_len <= length_bound as usize, Error::<T, I>::WrongProposalLength);
let proposal_hash = T::Hashing::hash_of(&proposal);
ensure!(!<ProposalOf<T, I>>::contains_key(proposal_hash), Error::<T, I>::DuplicateProposal);
ensure!(
!<ProposalOf<T, I>>::contains_key(proposal_hash),
Error::<T, I>::DuplicateProposal
);
if threshold < 2 {
let seats = Self::members().len() as MemberCount;
let result = proposal.dispatch(RawOrigin::Members(1, seats).into());
Self::deposit_event(
RawEvent::Executed(proposal_hash, result.map(|_| ()).map_err(|e| e.error))
);
Self::deposit_event(Event::Executed(
proposal_hash,
result.map(|_| ()).map_err(|e| e.error),
));
Ok(get_result_weight(result).map(|w| {
T::WeightInfo::propose_execute(
proposal_len as u32, // B
members.len() as u32, // M
).saturating_add(w) // P1
}).into())
Ok(get_result_weight(result)
.map(|w| {
T::WeightInfo::propose_execute(
proposal_len as u32, // B
members.len() as u32, // M
)
.saturating_add(w) // P1
})
.into())
} else {
let active_proposals =
<Proposals<T, I>>::try_mutate(|proposals| -> Result<usize, DispatchError> {
proposals.try_push(proposal_hash).map_err(|_| Error::<T, I>::TooManyProposals)?;
proposals
.try_push(proposal_hash)
.map_err(|_| Error::<T, I>::TooManyProposals)?;
Ok(proposals.len())
})?;
let index = Self::proposal_count();
<ProposalCount<I>>::mutate(|i| *i += 1);
<ProposalCount<T, I>>::mutate(|i| *i += 1);
<ProposalOf<T, I>>::insert(proposal_hash, *proposal);
let votes = {
let end = system::Pallet::<T>::block_number() + T::MotionDuration::get();
let end = frame_system::Pallet::<T>::block_number() + T::MotionDuration::get();
Votes { index, threshold, ayes: vec![], nays: vec![], end }
};
<Voting<T, I>>::insert(proposal_hash, votes);
Self::deposit_event(RawEvent::Proposed(who, index, proposal_hash, threshold));
Self::deposit_event(Event::Proposed(who, index, proposal_hash, threshold));
Ok(Some(T::WeightInfo::propose_proposed(
proposal_len as u32, // B
members.len() as u32, // M
proposal_len as u32, // B
members.len() as u32, // M
active_proposals as u32, // P2
)).into())
))
.into())
}
}
@@ -507,7 +560,8 @@ decl_module! {
/// Requires the sender to be a member.
///
/// Transaction fees will be waived if the member is voting on any particular proposal
/// for the first time and the call is successful. Subsequent vote changes will charge a fee.
/// for the first time and the call is successful. Subsequent vote changes will charge a
/// fee.
/// # <weight>
/// ## Weight
/// - `O(M)` where `M` is members-count (code- and governance-bounded)
@@ -516,13 +570,11 @@ decl_module! {
/// - 1 storage mutation `Voting` (codec `O(M)`)
/// - 1 event
/// # </weight>
#[weight = (
T::WeightInfo::vote(T::MaxMembers::get()),
DispatchClass::Operational
)]
fn vote(origin,
#[pallet::weight((T::WeightInfo::vote(T::MaxMembers::get()), DispatchClass::Operational))]
pub fn vote(
origin: OriginFor<T>,
proposal: T::Hash,
#[compact] index: ProposalIndex,
#[pallet::compact] index: ProposalIndex,
approve: bool,
) -> DispatchResultWithPostInfo {
let who = ensure_signed(origin)?;
@@ -542,7 +594,7 @@ decl_module! {
if position_yes.is_none() {
voting.ayes.push(who.clone());
} else {
Err(Error::<T, I>::DuplicateVote)?
return Err(Error::<T, I>::DuplicateVote.into())
}
if let Some(pos) = position_no {
voting.nays.swap_remove(pos);
@@ -551,7 +603,7 @@ decl_module! {
if position_no.is_none() {
voting.nays.push(who.clone());
} else {
Err(Error::<T, I>::DuplicateVote)?
return Err(Error::<T, I>::DuplicateVote.into())
}
if let Some(pos) = position_yes {
voting.ayes.swap_remove(pos);
@@ -560,20 +612,14 @@ decl_module! {
let yes_votes = voting.ayes.len() as MemberCount;
let no_votes = voting.nays.len() as MemberCount;
Self::deposit_event(RawEvent::Voted(who, proposal, approve, yes_votes, no_votes));
Self::deposit_event(Event::Voted(who, proposal, approve, yes_votes, no_votes));
Voting::<T, I>::insert(&proposal, voting);
if is_account_voting_first_time {
Ok((
Some(T::WeightInfo::vote(members.len() as u32)),
Pays::No,
).into())
Ok((Some(T::WeightInfo::vote(members.len() as u32)), Pays::No).into())
} else {
Ok((
Some(T::WeightInfo::vote(members.len() as u32)),
Pays::Yes,
).into())
Ok((Some(T::WeightInfo::vote(members.len() as u32)), Pays::Yes).into())
}
}
@@ -590,9 +636,10 @@ decl_module! {
/// If the close operation completes successfully with disapproval, the transaction fee will
/// be waived. Otherwise execution of the approved operation will be charged to the caller.
///
/// + `proposal_weight_bound`: The maximum amount of weight consumed by executing the closed proposal.
/// + `proposal_weight_bound`: The maximum amount of weight consumed by executing the closed
/// proposal.
/// + `length_bound`: The upper bound for the length of the proposal in storage. Checked via
/// `storage::read` so it is `size_of::<u32>() == 4` larger than the pure length.
/// `storage::read` so it is `size_of::<u32>() == 4` larger than the pure length.
///
/// # <weight>
/// ## Weight
@@ -603,11 +650,12 @@ decl_module! {
/// - `P2` is proposal-count (code-bounded)
/// - DB:
/// - 2 storage reads (`Members`: codec `O(M)`, `Prime`: codec `O(1)`)
/// - 3 mutations (`Voting`: codec `O(M)`, `ProposalOf`: codec `O(B)`, `Proposals`: codec `O(P2)`)
/// - 3 mutations (`Voting`: codec `O(M)`, `ProposalOf`: codec `O(B)`, `Proposals`: codec
/// `O(P2)`)
/// - any mutations done while executing `proposal` (`P1`)
/// - up to 3 events
/// # </weight>
#[weight = (
#[pallet::weight((
{
let b = *length_bound;
let m = T::MaxMembers::get();
@@ -620,12 +668,13 @@ decl_module! {
.saturating_add(p1)
},
DispatchClass::Operational
)]
fn close(origin,
))]
pub fn close(
origin: OriginFor<T>,
proposal_hash: T::Hash,
#[compact] index: ProposalIndex,
#[compact] proposal_weight_bound: Weight,
#[compact] length_bound: u32
#[pallet::compact] index: ProposalIndex,
#[pallet::compact] proposal_weight_bound: Weight,
#[pallet::compact] length_bound: u32,
) -> DispatchResultWithPostInfo {
let _ = ensure_signed(origin)?;
@@ -644,26 +693,32 @@ decl_module! {
length_bound,
proposal_weight_bound,
)?;
Self::deposit_event(RawEvent::Closed(proposal_hash, yes_votes, no_votes));
Self::deposit_event(Event::Closed(proposal_hash, yes_votes, no_votes));
let (proposal_weight, proposal_count) =
Self::do_approve_proposal(seats, yes_votes, proposal_hash, proposal);
return Ok((
Some(T::WeightInfo::close_early_approved(len as u32, seats, proposal_count)
.saturating_add(proposal_weight)),
Some(
T::WeightInfo::close_early_approved(len as u32, seats, proposal_count)
.saturating_add(proposal_weight),
),
Pays::Yes,
).into());
)
.into())
} else if disapproved {
Self::deposit_event(RawEvent::Closed(proposal_hash, yes_votes, no_votes));
Self::deposit_event(Event::Closed(proposal_hash, yes_votes, no_votes));
let proposal_count = Self::do_disapprove_proposal(proposal_hash);
return Ok((
Some(T::WeightInfo::close_early_disapproved(seats, proposal_count)),
Pays::No,
).into());
)
.into())
}
// Only allow actual closing of the proposal after the voting period has ended.
ensure!(system::Pallet::<T>::block_number() >= voting.end, Error::<T, I>::TooEarly);
ensure!(
frame_system::Pallet::<T>::block_number() >= voting.end,
Error::<T, I>::TooEarly
);
let prime_vote = Self::prime().map(|who| voting.ayes.iter().any(|a| a == &who));
@@ -683,25 +738,26 @@ decl_module! {
length_bound,
proposal_weight_bound,
)?;
Self::deposit_event(RawEvent::Closed(proposal_hash, yes_votes, no_votes));
Self::deposit_event(Event::Closed(proposal_hash, yes_votes, no_votes));
let (proposal_weight, proposal_count) =
Self::do_approve_proposal(seats, yes_votes, proposal_hash, proposal);
return Ok((
Some(T::WeightInfo::close_approved(len as u32, seats, proposal_count)
.saturating_add(proposal_weight)),
Ok((
Some(
T::WeightInfo::close_approved(len as u32, seats, proposal_count)
.saturating_add(proposal_weight),
),
Pays::Yes,
).into());
)
.into())
} else {
Self::deposit_event(RawEvent::Closed(proposal_hash, yes_votes, no_votes));
Self::deposit_event(Event::Closed(proposal_hash, yes_votes, no_votes));
let proposal_count = Self::do_disapprove_proposal(proposal_hash);
return Ok((
Some(T::WeightInfo::close_disapproved(seats, proposal_count)),
Pays::No,
).into());
Ok((Some(T::WeightInfo::close_disapproved(seats, proposal_count)), Pays::No).into())
}
}
/// Disapprove a proposal, close, and remove it from the system, regardless of its current state.
/// Disapprove a proposal, close, and remove it from the system, regardless of its current
/// state.
///
/// Must be called by the Root origin.
///
@@ -714,8 +770,11 @@ decl_module! {
/// * Reads: Proposals
/// * Writes: Voting, Proposals, ProposalOf
/// # </weight>
#[weight = T::WeightInfo::disapprove_proposal(T::MaxProposals::get())]
fn disapprove_proposal(origin, proposal_hash: T::Hash) -> DispatchResultWithPostInfo {
#[pallet::weight(T::WeightInfo::disapprove_proposal(T::MaxProposals::get()))]
pub fn disapprove_proposal(
origin: OriginFor<T>,
proposal_hash: T::Hash,
) -> DispatchResultWithPostInfo {
ensure_root(origin)?;
let proposal_count = Self::do_disapprove_proposal(proposal_hash);
Ok(Some(T::WeightInfo::disapprove_proposal(proposal_count)).into())
@@ -723,7 +782,17 @@ decl_module! {
}
}
impl<T: Config<I>, I: Instance> Module<T, I> {
/// Return the weight of a dispatch call result as an `Option`.
///
/// Will return the weight regardless of what the state of the result is.
fn get_result_weight(result: DispatchResultWithPostInfo) -> Option<Weight> {
match result {
Ok(post_info) => post_info.actual_weight,
Err(err) => err.post_info.actual_weight,
}
}
impl<T: Config<I>, I: 'static> Pallet<T, I> {
/// Check whether `who` is a member of the collective.
pub fn is_member(who: &T::AccountId) -> bool {
// Note: The dispatchables *do not* use this to check membership so make sure
@@ -771,12 +840,12 @@ impl<T: Config<I>, I: Instance> Module<T, I> {
proposal_hash: T::Hash,
proposal: <T as Config<I>>::Proposal,
) -> (Weight, u32) {
Self::deposit_event(RawEvent::Approved(proposal_hash));
Self::deposit_event(Event::Approved(proposal_hash));
let dispatch_weight = proposal.get_dispatch_info().weight;
let origin = RawOrigin::Members(yes_votes, seats).into();
let result = proposal.dispatch(origin);
Self::deposit_event(RawEvent::Executed(
Self::deposit_event(Event::Executed(
proposal_hash,
result.map(|_| ()).map_err(|e| e.error),
));
@@ -789,7 +858,7 @@ impl<T: Config<I>, I: Instance> Module<T, I> {
fn do_disapprove_proposal(proposal_hash: T::Hash) -> u32 {
// disapproved
Self::deposit_event(RawEvent::Disapproved(proposal_hash));
Self::deposit_event(Event::Disapproved(proposal_hash));
Self::remove_proposal(proposal_hash)
}
@@ -806,7 +875,7 @@ impl<T: Config<I>, I: Instance> Module<T, I> {
}
}
impl<T: Config<I>, I: Instance> ChangeMembers<T::AccountId> for Module<T, I> {
impl<T: Config<I>, I: 'static> ChangeMembers<T::AccountId> for Pallet<T, I> {
/// Update the members of the collective. Votes are updated and the prime is reset.
///
/// NOTE: Does not enforce the expected `MaxMembers` limit on the amount of members, but
@@ -870,7 +939,7 @@ impl<T: Config<I>, I: Instance> ChangeMembers<T::AccountId> for Module<T, I> {
}
}
impl<T: Config<I>, I: Instance> InitializeMembers<T::AccountId> for Module<T, I> {
impl<T: Config<I>, I: 'static> InitializeMembers<T::AccountId> for Pallet<T, I> {
fn initialize_members(members: &[T::AccountId]) {
if !members.is_empty() {
assert!(<Members<T, I>>::get().is_empty(), "Members are already initialized!");
@@ -894,9 +963,7 @@ where
}
}
pub struct EnsureMember<AccountId, I = DefaultInstance>(
sp_std::marker::PhantomData<(AccountId, I)>,
);
pub struct EnsureMember<AccountId, I: 'static>(PhantomData<(AccountId, I)>);
impl<
O: Into<Result<RawOrigin<AccountId, I>, O>> + From<RawOrigin<AccountId, I>>,
AccountId: Default,
@@ -917,9 +984,7 @@ impl<
}
}
pub struct EnsureMembers<N: U32, AccountId, I = DefaultInstance>(
sp_std::marker::PhantomData<(N, AccountId, I)>,
);
pub struct EnsureMembers<N: U32, AccountId, I: 'static>(PhantomData<(N, AccountId, I)>);
impl<
O: Into<Result<RawOrigin<AccountId, I>, O>> + From<RawOrigin<AccountId, I>>,
N: U32,
@@ -941,8 +1006,8 @@ impl<
}
}
pub struct EnsureProportionMoreThan<N: U32, D: U32, AccountId, I = DefaultInstance>(
sp_std::marker::PhantomData<(N, D, AccountId, I)>,
pub struct EnsureProportionMoreThan<N: U32, D: U32, AccountId, I: 'static>(
PhantomData<(N, D, AccountId, I)>,
);
impl<
O: Into<Result<RawOrigin<AccountId, I>, O>> + From<RawOrigin<AccountId, I>>,
@@ -966,8 +1031,8 @@ impl<
}
}
pub struct EnsureProportionAtLeast<N: U32, D: U32, AccountId, I = DefaultInstance>(
sp_std::marker::PhantomData<(N, D, AccountId, I)>,
pub struct EnsureProportionAtLeast<N: U32, D: U32, AccountId, I: 'static>(
PhantomData<(N, D, AccountId, I)>,
);
impl<
O: Into<Result<RawOrigin<AccountId, I>, O>> + From<RawOrigin<AccountId, I>>,
@@ -0,0 +1,19 @@
// This file is part of Substrate.
// Copyright (C) 2019-2021 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/// Version 4.
pub mod v4;
@@ -0,0 +1,147 @@
// This file is part of Substrate.
// Copyright (C) 2019-2021 Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use sp_io::hashing::twox_128;
use frame_support::{
traits::{
Get, GetStorageVersion, PalletInfoAccess, StorageVersion,
STORAGE_VERSION_STORAGE_KEY_POSTFIX,
},
weights::Weight,
};
/// Migrate the entire storage of this pallet to a new prefix.
///
/// This new prefix must be the same as the one set in construct_runtime. For safety, use
/// `PalletInfo` to get it, as:
/// `<Runtime as frame_system::Config>::PalletInfo::name::<CollectivePallet>`.
///
/// The migration will look into the storage version in order not to trigger a migration on an up
/// to date storage. Thus the on chain storage version must be less than 4 in order to trigger the
/// migration.
pub fn migrate<T: frame_system::Config, P: GetStorageVersion + PalletInfoAccess, N: AsRef<str>>(
old_pallet_name: N,
) -> Weight {
let old_pallet_name = old_pallet_name.as_ref();
let new_pallet_name = <P as PalletInfoAccess>::name();
if new_pallet_name == old_pallet_name {
log::info!(
target: "runtime::collective",
"New pallet name is equal to the old pallet name. No migration needs to be done.",
);
return 0
}
let on_chain_storage_version = <P as GetStorageVersion>::on_chain_storage_version();
log::info!(
target: "runtime::collective",
"Running migration to v4 for collective with storage version {:?}",
on_chain_storage_version,
);
if on_chain_storage_version < 4 {
frame_support::storage::migration::move_pallet(
old_pallet_name.as_bytes(),
new_pallet_name.as_bytes(),
);
log_migration("migration", old_pallet_name, new_pallet_name);
StorageVersion::new(4).put::<P>();
<T as frame_system::Config>::BlockWeights::get().max_block
} else {
log::warn!(
target: "runtime::collective",
"Attempted to apply migration to v4 but failed because storage version is {:?}",
on_chain_storage_version,
);
0
}
}
/// Some checks prior to migration. This can be linked to
/// [`frame_support::traits::OnRuntimeUpgrade::pre_upgrade`] for further testing.
///
/// Panics if anything goes wrong.
pub fn pre_migrate<P: GetStorageVersion + PalletInfoAccess, N: AsRef<str>>(old_pallet_name: N) {
let old_pallet_name = old_pallet_name.as_ref();
let new_pallet_name = <P as PalletInfoAccess>::name();
log_migration("pre-migration", old_pallet_name, new_pallet_name);
if new_pallet_name == old_pallet_name {
return
}
let new_pallet_prefix = twox_128(new_pallet_name.as_bytes());
let storage_version_key = twox_128(STORAGE_VERSION_STORAGE_KEY_POSTFIX);
let mut new_pallet_prefix_iter = frame_support::storage::KeyPrefixIterator::new(
new_pallet_prefix.to_vec(),
new_pallet_prefix.to_vec(),
|key| Ok(key.to_vec()),
);
// Ensure nothing except the storage_version_key is stored in the new prefix.
assert!(new_pallet_prefix_iter.all(|key| key == storage_version_key));
assert!(<P as GetStorageVersion>::on_chain_storage_version() < 4);
}
/// Some checks for after migration. This can be linked to
/// [`frame_support::traits::OnRuntimeUpgrade::post_upgrade`] for further testing.
///
/// Panics if anything goes wrong.
pub fn post_migrate<P: GetStorageVersion + PalletInfoAccess, N: AsRef<str>>(old_pallet_name: N) {
let old_pallet_name = old_pallet_name.as_ref();
let new_pallet_name = <P as PalletInfoAccess>::name();
log_migration("post-migration", old_pallet_name, new_pallet_name);
if new_pallet_name == old_pallet_name {
return
}
// Assert that nothing remains at the old prefix.
let old_pallet_prefix = twox_128(old_pallet_name.as_bytes());
let old_pallet_prefix_iter = frame_support::storage::KeyPrefixIterator::new(
old_pallet_prefix.to_vec(),
old_pallet_prefix.to_vec(),
|_| Ok(()),
);
assert_eq!(old_pallet_prefix_iter.count(), 0);
// NOTE: storage_version_key is already in the new prefix.
let new_pallet_prefix = twox_128(new_pallet_name.as_bytes());
let new_pallet_prefix_iter = frame_support::storage::KeyPrefixIterator::new(
new_pallet_prefix.to_vec(),
new_pallet_prefix.to_vec(),
|_| Ok(()),
);
assert!(new_pallet_prefix_iter.count() >= 1);
assert_eq!(<P as GetStorageVersion>::on_chain_storage_version(), 4);
}
fn log_migration(stage: &str, old_pallet_name: &str, new_pallet_name: &str) {
log::info!(
target: "runtime::collective",
"{}, prefix: '{}' ==> '{}'",
stage,
old_pallet_name,
new_pallet_name,
);
}
+134 -71
View File
@@ -15,10 +15,12 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use super::*;
use crate as collective;
use frame_support::{assert_noop, assert_ok, parameter_types, Hashable};
use frame_system::{self as system, EventRecord, Phase};
use super::{Event as CollectiveEvent, *};
use crate as pallet_collective;
use frame_support::{
assert_noop, assert_ok, parameter_types, traits::GenesisBuild, weights::Pays, Hashable,
};
use frame_system::{EventRecord, Phase};
use sp_core::{
u32_trait::{_3, _4},
H256,
@@ -38,10 +40,10 @@ frame_support::construct_runtime!(
NodeBlock = Block,
UncheckedExtrinsic = UncheckedExtrinsic
{
System: system::{Pallet, Call, Event<T>},
Collective: collective::<Instance1>::{Pallet, Call, Event<T>, Origin<T>, Config<T>},
CollectiveMajority: collective::<Instance2>::{Pallet, Call, Event<T>, Origin<T>, Config<T>},
DefaultCollective: collective::{Pallet, Call, Event<T>, Origin<T>, Config<T>},
System: frame_system::{Pallet, Call, Event<T>},
Collective: pallet_collective::<Instance1>::{Pallet, Call, Event<T>, Origin<T>, Config<T>},
CollectiveMajority: pallet_collective::<Instance2>::{Pallet, Call, Event<T>, Origin<T>, Config<T>},
DefaultCollective: pallet_collective::{Pallet, Call, Event<T>, Origin<T>, Config<T>},
Democracy: mock_democracy::{Pallet, Call, Event<T>},
}
);
@@ -152,11 +154,11 @@ impl Config for Test {
pub fn new_test_ext() -> sp_io::TestExternalities {
let mut ext: sp_io::TestExternalities = GenesisConfig {
collective: collective::GenesisConfig {
collective: pallet_collective::GenesisConfig {
members: vec![1, 2, 3],
phantom: Default::default(),
},
collective_majority: collective::GenesisConfig {
collective_majority: pallet_collective::GenesisConfig {
members: vec![1, 2, 3, 4, 5],
phantom: Default::default(),
},
@@ -214,11 +216,11 @@ fn close_works() {
assert_eq!(
System::events(),
vec![
record(Event::Collective(RawEvent::Proposed(1, 0, hash, 3))),
record(Event::Collective(RawEvent::Voted(1, hash, true, 1, 0))),
record(Event::Collective(RawEvent::Voted(2, hash, true, 2, 0))),
record(Event::Collective(RawEvent::Closed(hash, 2, 1))),
record(Event::Collective(RawEvent::Disapproved(hash)))
record(Event::Collective(CollectiveEvent::Proposed(1, 0, hash, 3))),
record(Event::Collective(CollectiveEvent::Voted(1, hash, true, 1, 0))),
record(Event::Collective(CollectiveEvent::Voted(2, hash, true, 2, 0))),
record(Event::Collective(CollectiveEvent::Closed(hash, 2, 1))),
record(Event::Collective(CollectiveEvent::Disapproved(hash)))
]
);
});
@@ -307,11 +309,11 @@ fn close_with_prime_works() {
assert_eq!(
System::events(),
vec![
record(Event::Collective(RawEvent::Proposed(1, 0, hash, 3))),
record(Event::Collective(RawEvent::Voted(1, hash, true, 1, 0))),
record(Event::Collective(RawEvent::Voted(2, hash, true, 2, 0))),
record(Event::Collective(RawEvent::Closed(hash, 2, 1))),
record(Event::Collective(RawEvent::Disapproved(hash)))
record(Event::Collective(CollectiveEvent::Proposed(1, 0, hash, 3))),
record(Event::Collective(CollectiveEvent::Voted(1, hash, true, 1, 0))),
record(Event::Collective(CollectiveEvent::Voted(2, hash, true, 2, 0))),
record(Event::Collective(CollectiveEvent::Closed(hash, 2, 1))),
record(Event::Collective(CollectiveEvent::Disapproved(hash)))
]
);
});
@@ -346,12 +348,15 @@ fn close_with_voting_prime_works() {
assert_eq!(
System::events(),
vec![
record(Event::Collective(RawEvent::Proposed(1, 0, hash, 3))),
record(Event::Collective(RawEvent::Voted(1, hash, true, 1, 0))),
record(Event::Collective(RawEvent::Voted(2, hash, true, 2, 0))),
record(Event::Collective(RawEvent::Closed(hash, 3, 0))),
record(Event::Collective(RawEvent::Approved(hash))),
record(Event::Collective(RawEvent::Executed(hash, Err(DispatchError::BadOrigin))))
record(Event::Collective(CollectiveEvent::Proposed(1, 0, hash, 3))),
record(Event::Collective(CollectiveEvent::Voted(1, hash, true, 1, 0))),
record(Event::Collective(CollectiveEvent::Voted(2, hash, true, 2, 0))),
record(Event::Collective(CollectiveEvent::Closed(hash, 3, 0))),
record(Event::Collective(CollectiveEvent::Approved(hash))),
record(Event::Collective(CollectiveEvent::Executed(
hash,
Err(DispatchError::BadOrigin)
)))
]
);
});
@@ -393,13 +398,13 @@ fn close_with_no_prime_but_majority_works() {
assert_eq!(
System::events(),
vec![
record(Event::CollectiveMajority(RawEvent::Proposed(1, 0, hash, 5))),
record(Event::CollectiveMajority(RawEvent::Voted(1, hash, true, 1, 0))),
record(Event::CollectiveMajority(RawEvent::Voted(2, hash, true, 2, 0))),
record(Event::CollectiveMajority(RawEvent::Voted(3, hash, true, 3, 0))),
record(Event::CollectiveMajority(RawEvent::Closed(hash, 5, 0))),
record(Event::CollectiveMajority(RawEvent::Approved(hash))),
record(Event::CollectiveMajority(RawEvent::Executed(
record(Event::CollectiveMajority(CollectiveEvent::Proposed(1, 0, hash, 5))),
record(Event::CollectiveMajority(CollectiveEvent::Voted(1, hash, true, 1, 0))),
record(Event::CollectiveMajority(CollectiveEvent::Voted(2, hash, true, 2, 0))),
record(Event::CollectiveMajority(CollectiveEvent::Voted(3, hash, true, 3, 0))),
record(Event::CollectiveMajority(CollectiveEvent::Closed(hash, 5, 0))),
record(Event::CollectiveMajority(CollectiveEvent::Approved(hash))),
record(Event::CollectiveMajority(CollectiveEvent::Executed(
hash,
Err(DispatchError::BadOrigin)
)))
@@ -526,7 +531,7 @@ fn propose_works() {
assert_eq!(
System::events(),
vec![record(Event::Collective(RawEvent::Proposed(1, 0, hash, 3)))]
vec![record(Event::Collective(CollectiveEvent::Proposed(1, 0, hash, 3)))]
);
});
}
@@ -682,9 +687,9 @@ fn motions_vote_after_works() {
assert_eq!(
System::events(),
vec![
record(Event::Collective(RawEvent::Proposed(1, 0, hash, 2))),
record(Event::Collective(RawEvent::Voted(1, hash, true, 1, 0))),
record(Event::Collective(RawEvent::Voted(1, hash, false, 0, 1))),
record(Event::Collective(CollectiveEvent::Proposed(1, 0, hash, 2))),
record(Event::Collective(CollectiveEvent::Voted(1, hash, true, 1, 0))),
record(Event::Collective(CollectiveEvent::Voted(1, hash, false, 0, 1))),
]
);
});
@@ -798,12 +803,15 @@ fn motions_approval_with_enough_votes_and_lower_voting_threshold_works() {
assert_eq!(
System::events(),
vec![
record(Event::Collective(RawEvent::Proposed(1, 0, hash, 2))),
record(Event::Collective(RawEvent::Voted(1, hash, true, 1, 0))),
record(Event::Collective(RawEvent::Voted(2, hash, true, 2, 0))),
record(Event::Collective(RawEvent::Closed(hash, 2, 0))),
record(Event::Collective(RawEvent::Approved(hash))),
record(Event::Collective(RawEvent::Executed(hash, Err(DispatchError::BadOrigin)))),
record(Event::Collective(CollectiveEvent::Proposed(1, 0, hash, 2))),
record(Event::Collective(CollectiveEvent::Voted(1, hash, true, 1, 0))),
record(Event::Collective(CollectiveEvent::Voted(2, hash, true, 2, 0))),
record(Event::Collective(CollectiveEvent::Closed(hash, 2, 0))),
record(Event::Collective(CollectiveEvent::Approved(hash))),
record(Event::Collective(CollectiveEvent::Executed(
hash,
Err(DispatchError::BadOrigin)
))),
]
);
@@ -823,14 +831,14 @@ fn motions_approval_with_enough_votes_and_lower_voting_threshold_works() {
assert_eq!(
System::events(),
vec![
record(Event::Collective(RawEvent::Proposed(1, 1, hash, 2))),
record(Event::Collective(RawEvent::Voted(1, hash, true, 1, 0))),
record(Event::Collective(RawEvent::Voted(2, hash, true, 2, 0))),
record(Event::Collective(RawEvent::Voted(3, hash, true, 3, 0))),
record(Event::Collective(RawEvent::Closed(hash, 3, 0))),
record(Event::Collective(RawEvent::Approved(hash))),
record(Event::Collective(CollectiveEvent::Proposed(1, 1, hash, 2))),
record(Event::Collective(CollectiveEvent::Voted(1, hash, true, 1, 0))),
record(Event::Collective(CollectiveEvent::Voted(2, hash, true, 2, 0))),
record(Event::Collective(CollectiveEvent::Voted(3, hash, true, 3, 0))),
record(Event::Collective(CollectiveEvent::Closed(hash, 3, 0))),
record(Event::Collective(CollectiveEvent::Approved(hash))),
record(Event::Democracy(mock_democracy::pallet::Event::<Test>::ExternalProposed)),
record(Event::Collective(RawEvent::Executed(hash, Ok(())))),
record(Event::Collective(CollectiveEvent::Executed(hash, Ok(())))),
]
);
});
@@ -856,11 +864,11 @@ fn motions_disapproval_works() {
assert_eq!(
System::events(),
vec![
record(Event::Collective(RawEvent::Proposed(1, 0, hash, 3))),
record(Event::Collective(RawEvent::Voted(1, hash, true, 1, 0))),
record(Event::Collective(RawEvent::Voted(2, hash, false, 1, 1))),
record(Event::Collective(RawEvent::Closed(hash, 1, 1))),
record(Event::Collective(RawEvent::Disapproved(hash))),
record(Event::Collective(CollectiveEvent::Proposed(1, 0, hash, 3))),
record(Event::Collective(CollectiveEvent::Voted(1, hash, true, 1, 0))),
record(Event::Collective(CollectiveEvent::Voted(2, hash, false, 1, 1))),
record(Event::Collective(CollectiveEvent::Closed(hash, 1, 1))),
record(Event::Collective(CollectiveEvent::Disapproved(hash))),
]
);
});
@@ -886,12 +894,15 @@ fn motions_approval_works() {
assert_eq!(
System::events(),
vec![
record(Event::Collective(RawEvent::Proposed(1, 0, hash, 2))),
record(Event::Collective(RawEvent::Voted(1, hash, true, 1, 0))),
record(Event::Collective(RawEvent::Voted(2, hash, true, 2, 0))),
record(Event::Collective(RawEvent::Closed(hash, 2, 0))),
record(Event::Collective(RawEvent::Approved(hash))),
record(Event::Collective(RawEvent::Executed(hash, Err(DispatchError::BadOrigin)))),
record(Event::Collective(CollectiveEvent::Proposed(1, 0, hash, 2))),
record(Event::Collective(CollectiveEvent::Voted(1, hash, true, 1, 0))),
record(Event::Collective(CollectiveEvent::Voted(2, hash, true, 2, 0))),
record(Event::Collective(CollectiveEvent::Closed(hash, 2, 0))),
record(Event::Collective(CollectiveEvent::Approved(hash))),
record(Event::Collective(CollectiveEvent::Executed(
hash,
Err(DispatchError::BadOrigin)
))),
]
);
});
@@ -912,7 +923,7 @@ fn motion_with_no_votes_closes_with_disapproval() {
));
assert_eq!(
System::events()[0],
record(Event::Collective(RawEvent::Proposed(1, 0, hash, 3)))
record(Event::Collective(CollectiveEvent::Proposed(1, 0, hash, 3)))
);
// Closing the motion too early is not possible because it has neither
@@ -929,8 +940,14 @@ fn motion_with_no_votes_closes_with_disapproval() {
assert_ok!(Collective::close(Origin::signed(2), hash, 0, proposal_weight, proposal_len));
// Events show that the close ended in a disapproval.
assert_eq!(System::events()[1], record(Event::Collective(RawEvent::Closed(hash, 0, 3))));
assert_eq!(System::events()[2], record(Event::Collective(RawEvent::Disapproved(hash))));
assert_eq!(
System::events()[1],
record(Event::Collective(CollectiveEvent::Closed(hash, 0, 3)))
);
assert_eq!(
System::events()[2],
record(Event::Collective(CollectiveEvent::Disapproved(hash)))
);
})
}
@@ -989,10 +1006,10 @@ fn disapprove_proposal_works() {
assert_eq!(
System::events(),
vec![
record(Event::Collective(RawEvent::Proposed(1, 0, hash, 2))),
record(Event::Collective(RawEvent::Voted(1, hash, true, 1, 0))),
record(Event::Collective(RawEvent::Voted(2, hash, true, 2, 0))),
record(Event::Collective(RawEvent::Disapproved(hash))),
record(Event::Collective(CollectiveEvent::Proposed(1, 0, hash, 2))),
record(Event::Collective(CollectiveEvent::Voted(1, hash, true, 1, 0))),
record(Event::Collective(CollectiveEvent::Voted(2, hash, true, 2, 0))),
record(Event::Collective(CollectiveEvent::Disapproved(hash))),
]
);
})
@@ -1001,7 +1018,53 @@ fn disapprove_proposal_works() {
#[test]
#[should_panic(expected = "Members cannot contain duplicate accounts.")]
fn genesis_build_panics_with_duplicate_members() {
collective::GenesisConfig::<Test> { members: vec![1, 2, 3, 1], phantom: Default::default() }
.build_storage()
.unwrap();
pallet_collective::GenesisConfig::<Test> {
members: vec![1, 2, 3, 1],
phantom: Default::default(),
}
.build_storage()
.unwrap();
}
#[test]
fn migration_v4() {
new_test_ext().execute_with(|| {
use frame_support::traits::PalletInfoAccess;
let old_pallet = "OldCollective";
let new_pallet = <Collective as PalletInfoAccess>::name();
frame_support::storage::migration::move_pallet(
new_pallet.as_bytes(),
old_pallet.as_bytes(),
);
StorageVersion::new(0).put::<Collective>();
crate::migrations::v4::pre_migrate::<Collective, _>(old_pallet);
crate::migrations::v4::migrate::<Test, Collective, _>(old_pallet);
crate::migrations::v4::post_migrate::<Collective, _>(old_pallet);
let old_pallet = "OldCollectiveMajority";
let new_pallet = <CollectiveMajority as PalletInfoAccess>::name();
frame_support::storage::migration::move_pallet(
new_pallet.as_bytes(),
old_pallet.as_bytes(),
);
StorageVersion::new(0).put::<CollectiveMajority>();
crate::migrations::v4::pre_migrate::<CollectiveMajority, _>(old_pallet);
crate::migrations::v4::migrate::<Test, CollectiveMajority, _>(old_pallet);
crate::migrations::v4::post_migrate::<CollectiveMajority, _>(old_pallet);
let old_pallet = "OldDefaultCollective";
let new_pallet = <DefaultCollective as PalletInfoAccess>::name();
frame_support::storage::migration::move_pallet(
new_pallet.as_bytes(),
old_pallet.as_bytes(),
);
StorageVersion::new(0).put::<DefaultCollective>();
crate::migrations::v4::pre_migrate::<DefaultCollective, _>(old_pallet);
crate::migrations::v4::migrate::<Test, DefaultCollective, _>(old_pallet);
crate::migrations::v4::post_migrate::<DefaultCollective, _>(old_pallet);
});
}