--- a/node/src/chain_spec.rs +++ b/node/src/chain_spec.rs @@ -147,12 +147,24 @@ pallet_aura: Some(AuraConfig { authorities: initial_authorities.iter().map(|x| (x.0.clone())).collect(), }), - pallet_grandpa: Some(GrandpaConfig { + pallet_collective_Instance1: Some(CouncilConfig { + members: vec![], + phantom: Default::default(), + }), + pallet_collective_Instance2: Some(TechnicalCommitteeConfig { + members: vec![], + phantom: Default::default(), + }), + pallet_democracy: Some(DemocracyConfig::default()), + pallet_grandpa: Some(GrandpaConfig { authorities: initial_authorities .iter() .map(|x| (x.1.clone(), 1)) .collect(), - }), + }), + pallet_elections_phragmen: Some(Default::default()), + pallet_membership: Some(Default::default()), + pallet_treasury: Some(Default::default()), pallet_sudo: Some(SudoConfig { key: root_key }), pallet_nft: Some(NftConfig { collection: vec![( --- a/runtime/Cargo.toml +++ b/runtime/Cargo.toml @@ -57,6 +57,13 @@ sp-transaction-pool = { default-features = false, version = '2.0.0' , git = 'https://github.com/usetech-llc/substrate.git', branch = 'v2.0.0_release' } sp-version = { default-features = false, version = '2.0.0' , git = 'https://github.com/usetech-llc/substrate.git', branch = 'v2.0.0_release' } +pallet-membership = { version = "2.0.0", default-features = false, git = 'https://github.com/usetech-llc/substrate.git', branch = 'v2.0.0_release' } +pallet-collective = { default-features = false, version = '2.0.0' , git = 'https://github.com/usetech-llc/substrate.git', branch = 'v2.0.0_release' } +pallet-democracy = { default-features = false, version = '2.0.0' , git = 'https://github.com/usetech-llc/substrate.git', branch = 'v2.0.0_release' } +pallet-elections-phragmen = { version = "2.0.0", default-features = false, git = 'https://github.com/usetech-llc/substrate.git', branch = 'v2.0.0_release' } +pallet-treasury = { version = "2.0.0", default-features = false, git = 'https://github.com/usetech-llc/substrate.git', branch = 'v2.0.0_release' } +pallet-scheduler = { version = "2.0.0", default-features = false, git = 'https://github.com/usetech-llc/substrate.git', branch = 'v2.0.0_release' } + [features] default = ['std'] runtime-benchmarks = [ @@ -100,4 +107,11 @@ 'sp-std/std', 'sp-transaction-pool/std', 'sp-version/std', + + 'pallet-collective/std', + 'pallet-democracy/std', + 'pallet-elections-phragmen/std', + 'pallet-membership/std', + 'pallet-treasury/std', + 'pallet-scheduler/std', ] --- a/runtime/src/lib.rs +++ b/runtime/src/lib.rs @@ -13,12 +13,16 @@ use pallet_grandpa::{AuthorityId as GrandpaId, AuthorityList as GrandpaAuthorityList}; use sp_api::impl_runtime_apis; use sp_consensus_aura::sr25519::AuthorityId as AuraId; -use sp_core::{crypto::KeyTypeId, OpaqueMetadata}; +use sp_core::{ + crypto::KeyTypeId, + OpaqueMetadata, + u32_trait::{_1, _2, _3, _4}, +}; use sp_runtime::{ create_runtime_str, generic, impl_opaque_keys, traits::{ - BlakeTwo256, Block as BlockT, IdentifyAccount, IdentityLookup, NumberFor, Saturating, - Verify, + Convert, BlakeTwo256, Block as BlockT, IdentifyAccount, + IdentityLookup, NumberFor, Saturating, Verify, }, transaction_validity::{TransactionSource, TransactionValidity}, ApplyExtrinsicResult, MultiSignature, @@ -37,7 +41,7 @@ parameter_types, traits::{ Currency, ExistenceRequirement, Get, KeyOwnerProofSystem, OnUnbalanced, Randomness, - WithdrawReason, + WithdrawReason, LockIdentifier, }, weights::{ constants::{BlockExecutionWeight, ExtrinsicBaseWeight, RocksDbWeight, WEIGHT_PER_SECOND}, @@ -48,11 +52,33 @@ }; #[cfg(any(feature = "std", test))] pub use sp_runtime::BuildStorage; -use sp_runtime::Perbill; -use frame_system::{self as system}; +use sp_runtime:: { Perbill, Permill, Percent, ModuleId }; +use frame_system::{self as system, EnsureRoot, EnsureOneOf }; pub use pallet_timestamp::Call as TimestampCall; +/// Struct that handles the conversion of Balance -> `u64`. This is used for +/// staking's election calculation. +pub struct CurrencyToVoteHandler; + +impl CurrencyToVoteHandler { + fn factor() -> Balance { + (Balances::total_issuance() / u64::max_value() as Balance).max(1) + } +} + +impl Convert for CurrencyToVoteHandler { + fn convert(x: Balance) -> u64 { + (x / Self::factor()) as u64 + } +} + +impl Convert for CurrencyToVoteHandler { + fn convert(x: u128) -> Balance { + x * Self::factor() + } +} + /// Re-export a nft pallet /// TODO: Check this re-export. Is this safe and good style? extern crate pallet_nft; @@ -303,6 +329,213 @@ type FeeMultiplierUpdate = (); } +parameter_types! { + pub MaximumSchedulerWeight: Weight = Perbill::from_percent(80) * MaximumBlockWeight::get(); + pub const MaxScheduledPerBlock: u32 = 50; +} + +impl pallet_scheduler::Trait for Runtime { + type Event = Event; + type Origin = Origin; + type PalletsOrigin = OriginCaller; + type Call = Call; + type MaximumWeight = MaximumSchedulerWeight; + type ScheduleOrigin = EnsureRoot; + type MaxScheduledPerBlock = MaxScheduledPerBlock; + type WeightInfo = (); +} + +parameter_types! { + pub const ProposalBond: Permill = Permill::from_percent(5); + pub const ProposalBondMinimum: Balance = 1 * DOLLARS; + pub const SpendPeriod: BlockNumber = 1 * DAYS; + pub const Burn: Permill = Permill::from_percent(50); + pub const TipCountdown: BlockNumber = 1 * DAYS; + pub const TipFindersFee: Percent = Percent::from_percent(20); + pub const TipReportDepositBase: Balance = 1 * DOLLARS; + pub const DataDepositPerByte: Balance = 1 * CENTS; + pub const BountyDepositBase: Balance = 1 * DOLLARS; + pub const BountyDepositPayoutDelay: BlockNumber = 1 * DAYS; + pub const TreasuryModuleId: ModuleId = ModuleId(*b"py/trsry"); + pub const BountyUpdatePeriod: BlockNumber = 14 * DAYS; + pub const MaximumReasonLength: u32 = 16384; + pub const BountyCuratorDeposit: Permill = Permill::from_percent(50); + pub const BountyValueMinimum: Balance = 5 * DOLLARS; +} + +impl pallet_treasury::Trait for Runtime { + type ModuleId = TreasuryModuleId; + type Currency = Balances; + type ApproveOrigin = + EnsureOneOf< + AccountId, + EnsureRoot, + pallet_collective::EnsureMembers<_4, AccountId, CouncilCollective> + >; + type RejectOrigin = + EnsureOneOf< + AccountId, + EnsureRoot, + pallet_collective::EnsureMembers<_2, AccountId, CouncilCollective> + >; + type Tippers = Elections; + type TipCountdown = TipCountdown; + type TipFindersFee = TipFindersFee; + type TipReportDepositBase = TipReportDepositBase; + type DataDepositPerByte = DataDepositPerByte; + type Event = Event; + type OnSlash = (); + type ProposalBond = ProposalBond; + type ProposalBondMinimum = ProposalBondMinimum; + type SpendPeriod = SpendPeriod; + type Burn = Burn; + type BountyDepositBase = BountyDepositBase; + type BountyDepositPayoutDelay = BountyDepositPayoutDelay; + type BountyUpdatePeriod = BountyUpdatePeriod; + type BountyCuratorDeposit = BountyCuratorDeposit; + type BountyValueMinimum = BountyValueMinimum; + type MaximumReasonLength = MaximumReasonLength; + type BurnDestination = (); + type WeightInfo = (); +} + +parameter_types! { + pub const TechnicalMotionDuration: BlockNumber = 5 * DAYS; + pub const TechnicalMaxProposals: u32 = 100; + pub const TechnicalMaxMembers: u32 = 100; +} + +type TechnicalCollective = pallet_collective::Instance2; +impl pallet_collective::Trait for Runtime { + type Origin = Origin; + type Proposal = Call; + type Event = Event; + type MotionDuration = TechnicalMotionDuration; + type MaxProposals = TechnicalMaxProposals; + type MaxMembers = TechnicalMaxMembers; + type DefaultVote = pallet_collective::PrimeDefaultVote; + type WeightInfo = (); +} + +parameter_types! { + pub const LaunchPeriod: BlockNumber = 28 * 24 * 60 * MINUTES; + pub const VotingPeriod: BlockNumber = 28 * 24 * 60 * MINUTES; + pub const FastTrackVotingPeriod: BlockNumber = 3 * 24 * 60 * MINUTES; + pub const InstantAllowed: bool = true; + pub const MinimumDeposit: Balance = 100 * DOLLARS; + pub const EnactmentPeriod: BlockNumber = 30 * 24 * 60 * MINUTES; + pub const CooloffPeriod: BlockNumber = 28 * 24 * 60 * MINUTES; + // One cent: $10,000 / MB + pub const PreimageByteDeposit: Balance = 1 * CENTS; + pub const MaxVotes: u32 = 100; +} + +impl pallet_democracy::Trait for Runtime { + type Proposal = Call; + type Event = Event; + type Currency = Balances; + type EnactmentPeriod = EnactmentPeriod; + type LaunchPeriod = LaunchPeriod; + type VotingPeriod = VotingPeriod; + type MinimumDeposit = MinimumDeposit; + /// A straight majority of the council can decide what their next motion is. + type ExternalOrigin = pallet_collective::EnsureProportionAtLeast<_1, _2, AccountId, CouncilCollective>; + /// A super-majority can have the next scheduled referendum be a straight majority-carries vote. + type ExternalMajorityOrigin = pallet_collective::EnsureProportionAtLeast<_3, _4, AccountId, CouncilCollective>; + /// A unanimous council can have the next scheduled referendum be a straight default-carries + /// (NTB) vote. + type ExternalDefaultOrigin = pallet_collective::EnsureProportionAtLeast<_1, _1, AccountId, CouncilCollective>; + /// Two thirds of the technical committee can have an ExternalMajority/ExternalDefault vote + /// be tabled immediately and with a shorter voting/enactment period. + type FastTrackOrigin = pallet_collective::EnsureProportionAtLeast<_2, _3, AccountId, TechnicalCollective>; + type InstantOrigin = pallet_collective::EnsureProportionAtLeast<_1, _1, AccountId, TechnicalCollective>; + type InstantAllowed = InstantAllowed; + type FastTrackVotingPeriod = FastTrackVotingPeriod; + // To cancel a proposal which has been passed, 2/3 of the council must agree to it. + type CancellationOrigin = EnsureOneOf< + AccountId, + EnsureRoot, + pallet_collective::EnsureProportionAtLeast<_2, _3, AccountId, CouncilCollective>, + >; + // Any single technical committee member may veto a coming council proposal, however they can + // only do it once and it lasts only for the cooloff period. + type VetoOrigin = pallet_collective::EnsureMember; + type CooloffPeriod = CooloffPeriod; + type PreimageByteDeposit = PreimageByteDeposit; + type OperationalPreimageOrigin = pallet_collective::EnsureMember; + type Slash = Treasury; + type Scheduler = Scheduler; + type PalletsOrigin = OriginCaller; + type MaxVotes = MaxVotes; + type WeightInfo = (); //weights::pallet_democracy::WeightInfo; +} + +parameter_types! { + pub const CouncilMotionDuration: BlockNumber = 5 * DAYS; + pub const CouncilMaxProposals: u32 = 100; + pub const CouncilMaxMembers: u32 = 100; +} + +type CouncilCollective = pallet_collective::Instance1; +impl pallet_collective::Trait for Runtime { + type Origin = Origin; + type Proposal = Call; + type Event = Event; + type MotionDuration = CouncilMotionDuration; + type MaxProposals = CouncilMaxProposals; + type MaxMembers = CouncilMaxMembers; + type DefaultVote = pallet_collective::PrimeDefaultVote; + type WeightInfo = (); //weights::pallet_collective::WeightInfo; +} + +parameter_types! { + pub const CandidacyBond: Balance = 10 * DOLLARS; + pub const VotingBond: Balance = 1 * DOLLARS; + pub const TermDuration: BlockNumber = 7 * DAYS; + pub const DesiredMembers: u32 = 13; + pub const DesiredRunnersUp: u32 = 7; + pub const ElectionsPhragmenModuleId: LockIdentifier = *b"phrelect"; +} + +// Make sure that there are no more than `MaxMembers` members elected via elections-phragmen. +// const_assert!(DesiredMembers::get() <= CouncilMaxMembers::get()); + +impl pallet_elections_phragmen::Trait for Runtime { + type Event = Event; + type ModuleId = ElectionsPhragmenModuleId; + type Currency = Balances; + type ChangeMembers = Council; + // NOTE: this implies that council's genesis members cannot be set directly and must come from + // this module. + type InitializeMembers = Council; + type CurrencyToVote = CurrencyToVoteHandler; + type CandidacyBond = CandidacyBond; + type VotingBond = VotingBond; + type LoserCandidate = (); + type BadReport = (); + type KickedMember = (); + type DesiredMembers = DesiredMembers; + type DesiredRunnersUp = DesiredRunnersUp; + type TermDuration = TermDuration; + type WeightInfo = (); //weights::pallet_elections_phragmen::WeightInfo; +} + +type EnsureRootOrHalfCouncil = EnsureOneOf< + AccountId, + EnsureRoot, + pallet_collective::EnsureProportionMoreThan<_1, _2, AccountId, CouncilCollective> +>; +impl pallet_membership::Trait for Runtime { + type Event = Event; + type AddOrigin = EnsureRootOrHalfCouncil; + type RemoveOrigin = EnsureRootOrHalfCouncil; + type SwapOrigin = EnsureRootOrHalfCouncil; + type ResetOrigin = EnsureRootOrHalfCouncil; + type PrimeOrigin = EnsureRootOrHalfCouncil; + type MembershipInitialized = TechnicalCommittee; + type MembershipChanged = TechnicalCommittee; +} + impl pallet_sudo::Trait for Runtime { type Event = Event; type Call = Call; @@ -330,6 +563,14 @@ TransactionPayment: pallet_transaction_payment::{Module, Storage}, Sudo: pallet_sudo::{Module, Call, Config, Storage, Event}, Nft: pallet_nft::{Module, Call, Config, Storage, Event}, + + Democracy: pallet_democracy::{Module, Call, Storage, Config, Event}, + Council: pallet_collective::::{Module, Call, Storage, Origin, Event, Config}, + TechnicalCommittee: pallet_collective::::{Module, Call, Storage, Origin, Event, Config}, + Elections: pallet_elections_phragmen::{Module, Call, Storage, Event, Config}, + TechnicalMembership: pallet_membership::{Module, Call, Storage, Event, Config}, + Treasury: pallet_treasury::{Module, Call, Storage, Config, Event}, + Scheduler: pallet_scheduler::{Module, Call, Storage, Event}, } );