mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-14 04:01:10 +00:00
pallet alliance (#11112)
* Add pallet-alliance Signed-off-by: koushiro <koushiro.cqx@gmail.com> * Update multihash/cid and format Signed-off-by: koushiro <koushiro.cqx@gmail.com> * Remove useless deps Signed-off-by: koushiro <koushiro.cqx@gmail.com> * Add pallet-alliance into node runtime Signed-off-by: koushiro <koushiro.cqx@gmail.com> * Fix has_identity Signed-off-by: koushiro <koushiro.cqx@gmail.com> * Add TooMantBlacklist Error Signed-off-by: koushiro <koushiro.cqx@gmail.com> * Add TooLongWebsiteUrlLength Error Signed-off-by: koushiro <koushiro.cqx@gmail.com> * Add remove_announcement Signed-off-by: koushiro <koushiro.cqx@gmail.com> * Derive pallet_identity::Config and Fix test/benchmarking Signed-off-by: koushiro <koushiro.cqx@gmail.com> * Add part weight for call of pallet-alliance Signed-off-by: koushiro <koushiro.cqx@gmail.com> * Remove pallet_identity Config trait Signed-off-by: koushiro <koushiro.cqx@gmail.com> * Fix propose arguments Signed-off-by: koushiro <koushiro.cqx@gmail.com> * Some nits Signed-off-by: koushiro <koushiro.cqx@gmail.com> * cargo fmt Signed-off-by: koushiro <koushiro.cqx@gmail.com> * Fix benchmarking of add_blacklist/remove_blacklist Signed-off-by: koushiro <koushiro.cqx@gmail.com> * Some nits Signed-off-by: koushiro <koushiro.cqx@gmail.com> * Add benchmarking for init_members Signed-off-by: koushiro <koushiro.cqx@gmail.com> * Add benchmarking for propose/vote/veto Signed-off-by: koushiro <koushiro.cqx@gmail.com> * Fix benchmarking Signed-off-by: koushiro <koushiro.cqx@gmail.com> * Remove some useless Signed-off-by: koushiro <koushiro.cqx@gmail.com> * fix some compile issue * more fix * all checks * refactor * refactor event * refactor * cleanup * cleanup * fix benchmarking * fix warning * fmt * fix benchmarks * with storage info * fmt * add new config * fix benchmarks * fix * fmt * Apply suggestions from code review Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * improvements * fix * update docs * rename events, errors, and functions * make kicking events clearer * fix * fix tests * fix tests * fix runtime * fix build * remove unneeded change * remove Candidate role from Alliance * fmt grumbles * update lock * update recursion limit * benchmarks * convert-try * benchmark assertions * fmt , * cargo lock * Update frame/alliance/src/benchmarking.rs Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * add docs to public interfaces * Apply suggestions from code review Co-authored-by: Squirrel <gilescope@gmail.com> * fix build (node) * review * use EitherOfDiverse * update cargo toml * make all dispatch class Normal * change blacklist to unscrupulous * formatting * fmt oops * rename benchmarking unscrupulous account creator Co-authored-by: koushiro <koushiro.cqx@gmail.com> Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> Co-authored-by: joepetrowski <joe@parity.io> Co-authored-by: joe petrowski <25483142+joepetrowski@users.noreply.github.com> Co-authored-by: Squirrel <gilescope@gmail.com>
This commit is contained in:
@@ -51,8 +51,10 @@ use frame_support::{
|
||||
codec::{Decode, Encode},
|
||||
dispatch::{DispatchError, DispatchResultWithPostInfo, Dispatchable, PostDispatchInfo},
|
||||
ensure,
|
||||
traits::{Backing, ChangeMembers, EnsureOrigin, Get, GetBacking, InitializeMembers},
|
||||
weights::{GetDispatchInfo, Weight},
|
||||
traits::{
|
||||
Backing, ChangeMembers, EnsureOrigin, Get, GetBacking, InitializeMembers, StorageVersion,
|
||||
},
|
||||
weights::{GetDispatchInfo, Pays, Weight},
|
||||
};
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -435,7 +437,7 @@ pub mod pallet {
|
||||
let who = ensure_signed(origin)?;
|
||||
let members = Self::members();
|
||||
ensure!(members.contains(&who), Error::<T, I>::NotMember);
|
||||
let proposal_len = proposal.using_encoded(|x| x.len());
|
||||
let proposal_len = proposal.encoded_size();
|
||||
ensure!(proposal_len <= length_bound as usize, Error::<T, I>::WrongProposalLength);
|
||||
|
||||
let proposal_hash = T::Hashing::hash_of(&proposal);
|
||||
@@ -508,21 +510,8 @@ pub mod pallet {
|
||||
let members = Self::members();
|
||||
ensure!(members.contains(&who), Error::<T, I>::NotMember);
|
||||
|
||||
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
|
||||
);
|
||||
|
||||
if threshold < 2 {
|
||||
let seats = Self::members().len() as MemberCount;
|
||||
let result = proposal.dispatch(RawOrigin::Members(1, seats).into());
|
||||
Self::deposit_event(Event::Executed {
|
||||
proposal_hash,
|
||||
result: result.map(|_| ()).map_err(|e| e.error),
|
||||
});
|
||||
let (proposal_len, result) = Self::do_propose_execute(proposal, length_bound)?;
|
||||
|
||||
Ok(get_result_weight(result)
|
||||
.map(|w| {
|
||||
@@ -534,33 +523,13 @@ pub mod pallet {
|
||||
})
|
||||
.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)?;
|
||||
Ok(proposals.len())
|
||||
})?;
|
||||
let index = Self::proposal_count();
|
||||
<ProposalCount<T, I>>::mutate(|i| *i += 1);
|
||||
<ProposalOf<T, I>>::insert(proposal_hash, *proposal);
|
||||
let votes = {
|
||||
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(Event::Proposed {
|
||||
account: who,
|
||||
proposal_index: index,
|
||||
proposal_hash,
|
||||
threshold,
|
||||
});
|
||||
let (proposal_len, active_proposals) =
|
||||
Self::do_propose_proposed(who, threshold, proposal, length_bound)?;
|
||||
|
||||
Ok(Some(T::WeightInfo::propose_proposed(
|
||||
proposal_len as u32, // B
|
||||
members.len() as u32, // M
|
||||
active_proposals as u32, // P2
|
||||
proposal_len as u32, // B
|
||||
members.len() as u32, // M
|
||||
active_proposals, // P2
|
||||
))
|
||||
.into())
|
||||
}
|
||||
@@ -592,46 +561,8 @@ pub mod pallet {
|
||||
let members = Self::members();
|
||||
ensure!(members.contains(&who), Error::<T, I>::NotMember);
|
||||
|
||||
let mut voting = Self::voting(&proposal).ok_or(Error::<T, I>::ProposalMissing)?;
|
||||
ensure!(voting.index == index, Error::<T, I>::WrongIndex);
|
||||
|
||||
let position_yes = voting.ayes.iter().position(|a| a == &who);
|
||||
let position_no = voting.nays.iter().position(|a| a == &who);
|
||||
|
||||
// Detects first vote of the member in the motion
|
||||
let is_account_voting_first_time = position_yes.is_none() && position_no.is_none();
|
||||
|
||||
if approve {
|
||||
if position_yes.is_none() {
|
||||
voting.ayes.push(who.clone());
|
||||
} else {
|
||||
return Err(Error::<T, I>::DuplicateVote.into())
|
||||
}
|
||||
if let Some(pos) = position_no {
|
||||
voting.nays.swap_remove(pos);
|
||||
}
|
||||
} else {
|
||||
if position_no.is_none() {
|
||||
voting.nays.push(who.clone());
|
||||
} else {
|
||||
return Err(Error::<T, I>::DuplicateVote.into())
|
||||
}
|
||||
if let Some(pos) = position_yes {
|
||||
voting.ayes.swap_remove(pos);
|
||||
}
|
||||
}
|
||||
|
||||
let yes_votes = voting.ayes.len() as MemberCount;
|
||||
let no_votes = voting.nays.len() as MemberCount;
|
||||
Self::deposit_event(Event::Voted {
|
||||
account: who,
|
||||
proposal_hash: proposal,
|
||||
voted: approve,
|
||||
yes: yes_votes,
|
||||
no: no_votes,
|
||||
});
|
||||
|
||||
Voting::<T, I>::insert(&proposal, voting);
|
||||
let is_account_voting_first_time = Self::do_vote(who, proposal, index, approve)?;
|
||||
|
||||
if is_account_voting_first_time {
|
||||
Ok((Some(T::WeightInfo::vote(members.len() as u32)), Pays::No).into())
|
||||
@@ -695,82 +626,7 @@ pub mod pallet {
|
||||
) -> DispatchResultWithPostInfo {
|
||||
let _ = ensure_signed(origin)?;
|
||||
|
||||
let voting = Self::voting(&proposal_hash).ok_or(Error::<T, I>::ProposalMissing)?;
|
||||
ensure!(voting.index == index, Error::<T, I>::WrongIndex);
|
||||
|
||||
let mut no_votes = voting.nays.len() as MemberCount;
|
||||
let mut yes_votes = voting.ayes.len() as MemberCount;
|
||||
let seats = Self::members().len() as MemberCount;
|
||||
let approved = yes_votes >= voting.threshold;
|
||||
let disapproved = seats.saturating_sub(no_votes) < voting.threshold;
|
||||
// Allow (dis-)approving the proposal as soon as there are enough votes.
|
||||
if approved {
|
||||
let (proposal, len) = Self::validate_and_get_proposal(
|
||||
&proposal_hash,
|
||||
length_bound,
|
||||
proposal_weight_bound,
|
||||
)?;
|
||||
Self::deposit_event(Event::Closed { proposal_hash, yes: yes_votes, no: 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),
|
||||
),
|
||||
Pays::Yes,
|
||||
)
|
||||
.into())
|
||||
} else if disapproved {
|
||||
Self::deposit_event(Event::Closed { proposal_hash, yes: yes_votes, no: 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())
|
||||
}
|
||||
|
||||
// Only allow actual closing of the proposal after the voting period has ended.
|
||||
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));
|
||||
|
||||
// default voting strategy.
|
||||
let default = T::DefaultVote::default_vote(prime_vote, yes_votes, no_votes, seats);
|
||||
|
||||
let abstentions = seats - (yes_votes + no_votes);
|
||||
match default {
|
||||
true => yes_votes += abstentions,
|
||||
false => no_votes += abstentions,
|
||||
}
|
||||
let approved = yes_votes >= voting.threshold;
|
||||
|
||||
if approved {
|
||||
let (proposal, len) = Self::validate_and_get_proposal(
|
||||
&proposal_hash,
|
||||
length_bound,
|
||||
proposal_weight_bound,
|
||||
)?;
|
||||
Self::deposit_event(Event::Closed { proposal_hash, yes: yes_votes, no: no_votes });
|
||||
let (proposal_weight, proposal_count) =
|
||||
Self::do_approve_proposal(seats, yes_votes, proposal_hash, proposal);
|
||||
Ok((
|
||||
Some(
|
||||
T::WeightInfo::close_approved(len as u32, seats, proposal_count)
|
||||
.saturating_add(proposal_weight),
|
||||
),
|
||||
Pays::Yes,
|
||||
)
|
||||
.into())
|
||||
} else {
|
||||
Self::deposit_event(Event::Closed { proposal_hash, yes: yes_votes, no: no_votes });
|
||||
let proposal_count = Self::do_disapprove_proposal(proposal_hash);
|
||||
Ok((Some(T::WeightInfo::close_disapproved(seats, proposal_count)), Pays::No).into())
|
||||
}
|
||||
Self::do_close(proposal_hash, index, proposal_weight_bound, length_bound)
|
||||
}
|
||||
|
||||
/// Disapprove a proposal, close, and remove it from the system, regardless of its current
|
||||
@@ -817,6 +673,197 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
|
||||
Self::members().contains(who)
|
||||
}
|
||||
|
||||
/// Execute immediately when adding a new proposal.
|
||||
pub fn do_propose_execute(
|
||||
proposal: Box<<T as Config<I>>::Proposal>,
|
||||
length_bound: MemberCount,
|
||||
) -> Result<(u32, DispatchResultWithPostInfo), DispatchError> {
|
||||
let proposal_len = proposal.encoded_size();
|
||||
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);
|
||||
|
||||
let seats = Self::members().len() as MemberCount;
|
||||
let result = proposal.dispatch(RawOrigin::Members(1, seats).into());
|
||||
Self::deposit_event(Event::Executed {
|
||||
proposal_hash,
|
||||
result: result.map(|_| ()).map_err(|e| e.error),
|
||||
});
|
||||
Ok((proposal_len as u32, result))
|
||||
}
|
||||
|
||||
/// Add a new proposal to be voted.
|
||||
pub fn do_propose_proposed(
|
||||
who: T::AccountId,
|
||||
threshold: MemberCount,
|
||||
proposal: Box<<T as Config<I>>::Proposal>,
|
||||
length_bound: MemberCount,
|
||||
) -> Result<(u32, u32), DispatchError> {
|
||||
let proposal_len = proposal.encoded_size();
|
||||
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);
|
||||
|
||||
let active_proposals =
|
||||
<Proposals<T, I>>::try_mutate(|proposals| -> Result<usize, DispatchError> {
|
||||
proposals.try_push(proposal_hash).map_err(|_| Error::<T, I>::TooManyProposals)?;
|
||||
Ok(proposals.len())
|
||||
})?;
|
||||
|
||||
let index = Self::proposal_count();
|
||||
<ProposalCount<T, I>>::mutate(|i| *i += 1);
|
||||
<ProposalOf<T, I>>::insert(proposal_hash, proposal);
|
||||
let votes = {
|
||||
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(Event::Proposed {
|
||||
account: who,
|
||||
proposal_index: index,
|
||||
proposal_hash,
|
||||
threshold,
|
||||
});
|
||||
Ok((proposal_len as u32, active_proposals as u32))
|
||||
}
|
||||
|
||||
/// Add an aye or nay vote for the member to the given proposal, returns true if it's the first
|
||||
/// vote of the member in the motion
|
||||
pub fn do_vote(
|
||||
who: T::AccountId,
|
||||
proposal: T::Hash,
|
||||
index: ProposalIndex,
|
||||
approve: bool,
|
||||
) -> Result<bool, DispatchError> {
|
||||
let mut voting = Self::voting(&proposal).ok_or(Error::<T, I>::ProposalMissing)?;
|
||||
ensure!(voting.index == index, Error::<T, I>::WrongIndex);
|
||||
|
||||
let position_yes = voting.ayes.iter().position(|a| a == &who);
|
||||
let position_no = voting.nays.iter().position(|a| a == &who);
|
||||
|
||||
// Detects first vote of the member in the motion
|
||||
let is_account_voting_first_time = position_yes.is_none() && position_no.is_none();
|
||||
|
||||
if approve {
|
||||
if position_yes.is_none() {
|
||||
voting.ayes.push(who.clone());
|
||||
} else {
|
||||
return Err(Error::<T, I>::DuplicateVote.into())
|
||||
}
|
||||
if let Some(pos) = position_no {
|
||||
voting.nays.swap_remove(pos);
|
||||
}
|
||||
} else {
|
||||
if position_no.is_none() {
|
||||
voting.nays.push(who.clone());
|
||||
} else {
|
||||
return Err(Error::<T, I>::DuplicateVote.into())
|
||||
}
|
||||
if let Some(pos) = position_yes {
|
||||
voting.ayes.swap_remove(pos);
|
||||
}
|
||||
}
|
||||
|
||||
let yes_votes = voting.ayes.len() as MemberCount;
|
||||
let no_votes = voting.nays.len() as MemberCount;
|
||||
Self::deposit_event(Event::Voted {
|
||||
account: who,
|
||||
proposal_hash: proposal,
|
||||
voted: approve,
|
||||
yes: yes_votes,
|
||||
no: no_votes,
|
||||
});
|
||||
|
||||
Voting::<T, I>::insert(&proposal, voting);
|
||||
|
||||
Ok(is_account_voting_first_time)
|
||||
}
|
||||
|
||||
/// Close a vote that is either approved, disapproved or whose voting period has ended.
|
||||
pub fn do_close(
|
||||
proposal_hash: T::Hash,
|
||||
index: ProposalIndex,
|
||||
proposal_weight_bound: Weight,
|
||||
length_bound: u32,
|
||||
) -> DispatchResultWithPostInfo {
|
||||
let voting = Self::voting(&proposal_hash).ok_or(Error::<T, I>::ProposalMissing)?;
|
||||
ensure!(voting.index == index, Error::<T, I>::WrongIndex);
|
||||
|
||||
let mut no_votes = voting.nays.len() as MemberCount;
|
||||
let mut yes_votes = voting.ayes.len() as MemberCount;
|
||||
let seats = Self::members().len() as MemberCount;
|
||||
let approved = yes_votes >= voting.threshold;
|
||||
let disapproved = seats.saturating_sub(no_votes) < voting.threshold;
|
||||
// Allow (dis-)approving the proposal as soon as there are enough votes.
|
||||
if approved {
|
||||
let (proposal, len) = Self::validate_and_get_proposal(
|
||||
&proposal_hash,
|
||||
length_bound,
|
||||
proposal_weight_bound,
|
||||
)?;
|
||||
Self::deposit_event(Event::Closed { proposal_hash, yes: yes_votes, no: 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),
|
||||
),
|
||||
Pays::Yes,
|
||||
)
|
||||
.into())
|
||||
} else if disapproved {
|
||||
Self::deposit_event(Event::Closed { proposal_hash, yes: yes_votes, no: 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())
|
||||
}
|
||||
|
||||
// Only allow actual closing of the proposal after the voting period has ended.
|
||||
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));
|
||||
|
||||
// default voting strategy.
|
||||
let default = T::DefaultVote::default_vote(prime_vote, yes_votes, no_votes, seats);
|
||||
|
||||
let abstentions = seats - (yes_votes + no_votes);
|
||||
match default {
|
||||
true => yes_votes += abstentions,
|
||||
false => no_votes += abstentions,
|
||||
}
|
||||
let approved = yes_votes >= voting.threshold;
|
||||
|
||||
if approved {
|
||||
let (proposal, len) = Self::validate_and_get_proposal(
|
||||
&proposal_hash,
|
||||
length_bound,
|
||||
proposal_weight_bound,
|
||||
)?;
|
||||
Self::deposit_event(Event::Closed { proposal_hash, yes: yes_votes, no: no_votes });
|
||||
let (proposal_weight, proposal_count) =
|
||||
Self::do_approve_proposal(seats, yes_votes, proposal_hash, proposal);
|
||||
Ok((
|
||||
Some(
|
||||
T::WeightInfo::close_approved(len as u32, seats, proposal_count)
|
||||
.saturating_add(proposal_weight),
|
||||
),
|
||||
Pays::Yes,
|
||||
)
|
||||
.into())
|
||||
} else {
|
||||
Self::deposit_event(Event::Closed { proposal_hash, yes: yes_votes, no: no_votes });
|
||||
let proposal_count = Self::do_disapprove_proposal(proposal_hash);
|
||||
Ok((Some(T::WeightInfo::close_disapproved(seats, proposal_count)), Pays::No).into())
|
||||
}
|
||||
}
|
||||
|
||||
/// Ensure that the right proposal bounds were passed and get the proposal from storage.
|
||||
///
|
||||
/// Checks the length in storage via `storage::read` which adds an extra `size_of::<u32>() == 4`
|
||||
@@ -873,7 +920,8 @@ impl<T: Config<I>, I: 'static> Pallet<T, I> {
|
||||
(proposal_weight, proposal_count)
|
||||
}
|
||||
|
||||
fn do_disapprove_proposal(proposal_hash: T::Hash) -> u32 {
|
||||
/// Removes a proposal from the pallet, and deposit the `Disapproved` event.
|
||||
pub fn do_disapprove_proposal(proposal_hash: T::Hash) -> u32 {
|
||||
// disapproved
|
||||
Self::deposit_event(Event::Disapproved { proposal_hash });
|
||||
Self::remove_proposal(proposal_hash)
|
||||
|
||||
Reference in New Issue
Block a user