mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-30 10:31:03 +00:00
Introduce polling pallet (#1391)
* Initial draft * Initial build * Integrate into runtime * Fix warnings. * Initial tests * Fixes and tests * Update runtime/polkadot/src/poll.rs Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com> * Expose End constant * Docs * Update runtime/polkadot/src/lib.rs Co-authored-by: kaichao <kaichaosuna@gmail.com> * Update runtime/polkadot/src/poll.rs Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com> * Clean up filters * Warnings Co-authored-by: Bastian Köcher <bkchr@users.noreply.github.com> Co-authored-by: kaichao <kaichaosuna@gmail.com> Co-authored-by: Kian Paimani <5588131+kianenigma@users.noreply.github.com>
This commit is contained in:
Generated
+1
@@ -4716,6 +4716,7 @@ dependencies = [
|
|||||||
"sp-consensus-babe",
|
"sp-consensus-babe",
|
||||||
"sp-core",
|
"sp-core",
|
||||||
"sp-inherents",
|
"sp-inherents",
|
||||||
|
"sp-io",
|
||||||
"sp-keyring",
|
"sp-keyring",
|
||||||
"sp-offchain",
|
"sp-offchain",
|
||||||
"sp-runtime",
|
"sp-runtime",
|
||||||
|
|||||||
@@ -20,7 +20,8 @@ babe-primitives = { package = "sp-consensus-babe", git = "https://github.com/par
|
|||||||
sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
sp-api = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
||||||
inherents = { package = "sp-inherents", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
inherents = { package = "sp-inherents", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
||||||
offchain-primitives = { package = "sp-offchain", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
offchain-primitives = { package = "sp-offchain", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
||||||
sp-std = { package = "sp-std", git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
sp-std = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
||||||
|
sp-io = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
||||||
sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
sp-runtime = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
||||||
sp-staking = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
sp-staking = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
||||||
sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
sp-core = { git = "https://github.com/paritytech/substrate", branch = "master", default-features = false }
|
||||||
|
|||||||
@@ -79,6 +79,7 @@ pub use parachains::Call as ParachainsCall;
|
|||||||
|
|
||||||
/// Constant values used within the runtime.
|
/// Constant values used within the runtime.
|
||||||
pub mod constants;
|
pub mod constants;
|
||||||
|
pub mod poll;
|
||||||
use constants::{time::*, currency::*, fee::*};
|
use constants::{time::*, currency::*, fee::*};
|
||||||
use frame_support::traits::InstanceFilter;
|
use frame_support::traits::InstanceFilter;
|
||||||
|
|
||||||
@@ -114,12 +115,7 @@ pub struct BaseFilter;
|
|||||||
impl Filter<Call> for BaseFilter {
|
impl Filter<Call> for BaseFilter {
|
||||||
fn filter(call: &Call) -> bool {
|
fn filter(call: &Call) -> bool {
|
||||||
match call {
|
match call {
|
||||||
Call::Parachains(parachains::Call::set_heads(..))
|
Call::Parachains(parachains::Call::set_heads(..)) => true,
|
||||||
| Call::Democracy(democracy::Call::vote(..))
|
|
||||||
| Call::Democracy(democracy::Call::remove_vote(..))
|
|
||||||
| Call::Democracy(democracy::Call::delegate(..))
|
|
||||||
| Call::Democracy(democracy::Call::undelegate(..))
|
|
||||||
=> true,
|
|
||||||
|
|
||||||
// Governance stuff
|
// Governance stuff
|
||||||
Call::Democracy(_) | Call::Council(_) | Call::TechnicalCommittee(_) |
|
Call::Democracy(_) | Call::Council(_) | Call::TechnicalCommittee(_) |
|
||||||
@@ -138,7 +134,7 @@ impl Filter<Call> for BaseFilter {
|
|||||||
Call::Session(_) | Call::FinalityTracker(_) | Call::Grandpa(_) | Call::ImOnline(_) |
|
Call::Session(_) | Call::FinalityTracker(_) | Call::Grandpa(_) | Call::ImOnline(_) |
|
||||||
Call::AuthorityDiscovery(_) |
|
Call::AuthorityDiscovery(_) |
|
||||||
Call::Utility(_) | Call::Claims(_) | Call::Vesting(_) | Call::Sudo(_) |
|
Call::Utility(_) | Call::Claims(_) | Call::Vesting(_) | Call::Sudo(_) |
|
||||||
Call::Identity(_) | Call::Proxy(_) | Call::Multisig(_) =>
|
Call::Identity(_) | Call::Proxy(_) | Call::Multisig(_) | Call::Poll(_) =>
|
||||||
true,
|
true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -893,6 +889,7 @@ impl InstanceFilter<Call> for ProxyType {
|
|||||||
ProxyType::Governance => matches!(c,
|
ProxyType::Governance => matches!(c,
|
||||||
Call::Democracy(..) | Call::Council(..) | Call::TechnicalCommittee(..)
|
Call::Democracy(..) | Call::Council(..) | Call::TechnicalCommittee(..)
|
||||||
| Call::ElectionsPhragmen(..) | Call::Treasury(..) | Call::Utility(..)
|
| Call::ElectionsPhragmen(..) | Call::Treasury(..) | Call::Utility(..)
|
||||||
|
| Call::Poll(..)
|
||||||
),
|
),
|
||||||
ProxyType::Staking => matches!(c,
|
ProxyType::Staking => matches!(c,
|
||||||
Call::Staking(..) | Call::Utility(utility::Call::batch(..)) | Call::Utility(..)
|
Call::Staking(..) | Call::Utility(utility::Call::batch(..)) | Call::Utility(..)
|
||||||
@@ -941,6 +938,16 @@ impl frame_support::traits::OnRuntimeUpgrade for CustomOnRuntimeUpgrade {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
parameter_types! {
|
||||||
|
pub const PollEnd: BlockNumber = 100_000;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl poll::Trait for Runtime {
|
||||||
|
type Event = Event;
|
||||||
|
type Currency = Balances;
|
||||||
|
type End = PollEnd;
|
||||||
|
}
|
||||||
|
|
||||||
construct_runtime! {
|
construct_runtime! {
|
||||||
pub enum Runtime where
|
pub enum Runtime where
|
||||||
Block = Block,
|
Block = Block,
|
||||||
@@ -1006,6 +1013,9 @@ construct_runtime! {
|
|||||||
|
|
||||||
// Multisig dispatch. Late addition.
|
// Multisig dispatch. Late addition.
|
||||||
Multisig: multisig::{Module, Call, Storage, Event<T>},
|
Multisig: multisig::{Module, Call, Storage, Event<T>},
|
||||||
|
|
||||||
|
// Poll module.
|
||||||
|
Poll: poll::{Module, Call, Storage, Event<T>},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,233 @@
|
|||||||
|
// Copyright 2020 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Polkadot.
|
||||||
|
|
||||||
|
// Polkadot is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// (at your option) any later version.
|
||||||
|
|
||||||
|
// Polkadot is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Polkadot. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! # Simple polling module
|
||||||
|
//!
|
||||||
|
//! Note: This implementation assumes that all accounts are locked, and thus that no account balance
|
||||||
|
//! may ever reduce.
|
||||||
|
|
||||||
|
use frame_support::{
|
||||||
|
decl_module, decl_storage, decl_event, decl_error, ensure, traits::{Currency, Get},
|
||||||
|
};
|
||||||
|
use system::{self as frame_system, ensure_signed};
|
||||||
|
use sp_runtime::traits::Saturating;
|
||||||
|
|
||||||
|
pub type BalanceOf<T> =
|
||||||
|
<<T as Trait>::Currency as Currency<<T as system::Trait>::AccountId>>::Balance;
|
||||||
|
|
||||||
|
pub trait Trait: system::Trait {
|
||||||
|
/// The overarching event type.
|
||||||
|
type Event: From<Event<Self>> + Into<<Self as system::Trait>::Event>;
|
||||||
|
|
||||||
|
/// The currency type used.
|
||||||
|
type Currency: Currency<Self::AccountId>;
|
||||||
|
|
||||||
|
/// The block number only before which voting is possible.
|
||||||
|
type End: Get<Self::BlockNumber>;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The options someone has approved.
|
||||||
|
pub type Approvals = [bool; 4];
|
||||||
|
|
||||||
|
decl_storage! {
|
||||||
|
trait Store for Module<T: Trait> as Poll {
|
||||||
|
/// Votes, so far.
|
||||||
|
pub VoteOf: map hasher(twox_64_concat) T::AccountId => (Approvals, BalanceOf<T>);
|
||||||
|
|
||||||
|
/// The total balances voting for each option.
|
||||||
|
pub Totals: [BalanceOf<T>; 4];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
decl_event! {
|
||||||
|
pub enum Event<T> where
|
||||||
|
<T as system::Trait>::AccountId,
|
||||||
|
Balance = BalanceOf<T>,
|
||||||
|
{
|
||||||
|
Voted(AccountId, Balance, Approvals),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
decl_error! {
|
||||||
|
pub enum Error for Module<T: Trait> {
|
||||||
|
/// Vote attempted after the end of voting.
|
||||||
|
TooLate,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
decl_module! {
|
||||||
|
pub struct Module<T: Trait> for enum Call where origin: T::Origin {
|
||||||
|
type Error = Error<T>;
|
||||||
|
|
||||||
|
fn deposit_event() = default;
|
||||||
|
|
||||||
|
/// The End config param.
|
||||||
|
const End: T::BlockNumber = T::End::get();
|
||||||
|
|
||||||
|
/// Cast a vote on the poll.
|
||||||
|
#[weight = 100_000_000]
|
||||||
|
fn vote(origin, approvals: Approvals) {
|
||||||
|
let who = ensure_signed(origin)?;
|
||||||
|
ensure!(system::Module::<T>::block_number() < T::End::get(), Error::<T>::TooLate);
|
||||||
|
let balance = T::Currency::total_balance(&who);
|
||||||
|
Totals::<T>::mutate(|ref mut totals| {
|
||||||
|
VoteOf::<T>::mutate(&who, |(ref mut who_approvals, ref mut who_balance)| {
|
||||||
|
for i in 0..approvals.len() {
|
||||||
|
if who_approvals[i] {
|
||||||
|
totals[i] = totals[i].saturating_sub(*who_balance);
|
||||||
|
}
|
||||||
|
if approvals[i] {
|
||||||
|
totals[i] = totals[i].saturating_add(balance);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*who_approvals = approvals;
|
||||||
|
*who_balance = balance;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
Self::deposit_event(RawEvent::Voted(who, balance, approvals));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
use frame_support::{assert_ok, assert_noop, impl_outer_origin, parameter_types, weights::Weight};
|
||||||
|
use sp_core::H256;
|
||||||
|
use sp_runtime::{Perbill, testing::Header, traits::{BlakeTwo256, IdentityLookup}};
|
||||||
|
|
||||||
|
impl_outer_origin! {
|
||||||
|
pub enum Origin for Test where system = frame_system {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// For testing the pallet, we construct most of a mock runtime. This means
|
||||||
|
// first constructing a configuration type (`Test`) which `impl`s each of the
|
||||||
|
// configuration traits of pallets we want to use.
|
||||||
|
#[derive(Clone, Eq, PartialEq)]
|
||||||
|
pub struct Test;
|
||||||
|
parameter_types! {
|
||||||
|
pub const BlockHashCount: u64 = 250;
|
||||||
|
pub const MaximumBlockWeight: Weight = 1024;
|
||||||
|
pub const MaximumBlockLength: u32 = 2 * 1024;
|
||||||
|
pub const AvailableBlockRatio: Perbill = Perbill::one();
|
||||||
|
}
|
||||||
|
impl frame_system::Trait for Test {
|
||||||
|
type BaseCallFilter = ();
|
||||||
|
type Origin = Origin;
|
||||||
|
type Index = u64;
|
||||||
|
type BlockNumber = u64;
|
||||||
|
type Hash = H256;
|
||||||
|
type Call = ();
|
||||||
|
type Hashing = BlakeTwo256;
|
||||||
|
type AccountId = u64;
|
||||||
|
type Lookup = IdentityLookup<Self::AccountId>;
|
||||||
|
type Header = Header;
|
||||||
|
type Event = ();
|
||||||
|
type BlockHashCount = BlockHashCount;
|
||||||
|
type MaximumBlockWeight = MaximumBlockWeight;
|
||||||
|
type DbWeight = ();
|
||||||
|
type BlockExecutionWeight = ();
|
||||||
|
type ExtrinsicBaseWeight = ();
|
||||||
|
type MaximumExtrinsicWeight = MaximumBlockWeight;
|
||||||
|
type MaximumBlockLength = MaximumBlockLength;
|
||||||
|
type AvailableBlockRatio = AvailableBlockRatio;
|
||||||
|
type Version = ();
|
||||||
|
type ModuleToIndex = ();
|
||||||
|
type AccountData = balances::AccountData<u64>;
|
||||||
|
type OnNewAccount = ();
|
||||||
|
type OnKilledAccount = ();
|
||||||
|
type SystemWeightInfo = ();
|
||||||
|
}
|
||||||
|
parameter_types! {
|
||||||
|
pub const ExistentialDeposit: u64 = 1;
|
||||||
|
}
|
||||||
|
impl balances::Trait for Test {
|
||||||
|
type Balance = u64;
|
||||||
|
type Event = ();
|
||||||
|
type DustRemoval = ();
|
||||||
|
type ExistentialDeposit = ExistentialDeposit;
|
||||||
|
type AccountStore = System;
|
||||||
|
type WeightInfo = ();
|
||||||
|
}
|
||||||
|
parameter_types! {
|
||||||
|
pub const End: u64 = 1;
|
||||||
|
}
|
||||||
|
impl Trait for Test {
|
||||||
|
type Event = ();
|
||||||
|
type Currency = Balances;
|
||||||
|
type End = End;
|
||||||
|
}
|
||||||
|
type System = system::Module<Test>;
|
||||||
|
type Balances = balances::Module<Test>;
|
||||||
|
type Poll = Module<Test>;
|
||||||
|
|
||||||
|
// This function basically just builds a genesis storage key/value store according to
|
||||||
|
// our desired mockup.
|
||||||
|
pub fn new_test_ext() -> sp_io::TestExternalities {
|
||||||
|
let mut t = system::GenesisConfig::default().build_storage::<Test>().unwrap();
|
||||||
|
// We use default for brevity, but you can configure as desired if needed.
|
||||||
|
balances::GenesisConfig::<Test> {
|
||||||
|
balances: vec![
|
||||||
|
(1, 10),
|
||||||
|
(2, 20),
|
||||||
|
(3, 30),
|
||||||
|
(4, 40),
|
||||||
|
],
|
||||||
|
}.assimilate_storage(&mut t).unwrap();
|
||||||
|
t.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn basic_setup_works() {
|
||||||
|
new_test_ext().execute_with(|| {
|
||||||
|
assert_eq!(System::block_number(), 0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn totaling_works() {
|
||||||
|
new_test_ext().execute_with(|| {
|
||||||
|
assert_ok!(Poll::vote(Origin::signed(1), [true, true, false, false]));
|
||||||
|
assert_ok!(Poll::vote(Origin::signed(2), [false, true, true, false]));
|
||||||
|
assert_ok!(Poll::vote(Origin::signed(3), [false, false, true, true]));
|
||||||
|
assert_ok!(Poll::vote(Origin::signed(4), [true, false, false, true]));
|
||||||
|
assert_eq!(Totals::<Test>::get(), [50, 30, 50, 70]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn revoting_works() {
|
||||||
|
new_test_ext().execute_with(|| {
|
||||||
|
assert_ok!(Poll::vote(Origin::signed(1), [true, false, false, false]));
|
||||||
|
assert_eq!(Totals::<Test>::get(), [10, 0, 0, 0]);
|
||||||
|
assert_ok!(Poll::vote(Origin::signed(1), [false, true, false, false]));
|
||||||
|
assert_eq!(Totals::<Test>::get(), [0, 10, 0, 0]);
|
||||||
|
assert_ok!(Poll::vote(Origin::signed(1), [false, false, true, true]));
|
||||||
|
assert_eq!(Totals::<Test>::get(), [0, 0, 10, 10]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn vote_end_works() {
|
||||||
|
new_test_ext().execute_with(|| {
|
||||||
|
assert_ok!(Poll::vote(Origin::signed(1), [true, false, false, false]));
|
||||||
|
assert_eq!(Totals::<Test>::get(), [10, 0, 0, 0]);
|
||||||
|
system::Module::<Test>::set_block_number(1);
|
||||||
|
assert_noop!(Poll::vote(Origin::signed(1), [false, true, false, false]), Error::<Test>::TooLate);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user