diff --git a/Cargo.lock b/Cargo.lock index e99203bc92..e2e854fa63 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -136,6 +136,7 @@ dependencies = [ "frame-try-runtime", "hex", "hex-literal", + "lazy_static", "libsecp256k1", "log", "module-aggregated-dex", @@ -191,11 +192,13 @@ dependencies = [ "pallet-balances", "pallet-bounties", "pallet-collective", + "pallet-conviction-voting", "pallet-democracy", "pallet-membership", "pallet-multisig", "pallet-preimage", "pallet-proxy", + "pallet-referenda", "pallet-scheduler", "pallet-session", "pallet-staking", @@ -206,6 +209,7 @@ dependencies = [ "pallet-transaction-payment-rpc-runtime-api", "pallet-treasury", "pallet-utility", + "pallet-whitelist", "pallet-xcm", "parachain-info", "parity-scale-codec", @@ -232,6 +236,8 @@ dependencies = [ "staging-xcm", "staging-xcm-builder", "staging-xcm-executor", + "strum 0.24.1", + "strum_macros 0.24.3", "substrate-wasm-builder", ] @@ -5440,6 +5446,7 @@ dependencies = [ "frame-try-runtime", "hex", "hex-literal", + "lazy_static", "libsecp256k1", "log", "module-aggregated-dex", @@ -5495,11 +5502,13 @@ dependencies = [ "pallet-balances", "pallet-bounties", "pallet-collective", + "pallet-conviction-voting", "pallet-democracy", "pallet-membership", "pallet-multisig", "pallet-preimage", "pallet-proxy", + "pallet-referenda", "pallet-scheduler", "pallet-session", "pallet-staking", @@ -5510,6 +5519,7 @@ dependencies = [ "pallet-transaction-payment-rpc-runtime-api", "pallet-treasury", "pallet-utility", + "pallet-whitelist", "pallet-xcm", "parachain-info", "parity-scale-codec", @@ -5536,6 +5546,8 @@ dependencies = [ "staging-xcm", "staging-xcm-builder", "staging-xcm-executor", + "strum 0.24.1", + "strum_macros 0.24.3", "substrate-wasm-builder", ] @@ -6334,6 +6346,7 @@ dependencies = [ "frame-try-runtime", "hex", "hex-literal", + "lazy_static", "libsecp256k1", "log", "module-aggregated-dex", @@ -6392,6 +6405,7 @@ dependencies = [ "pallet-balances", "pallet-bounties", "pallet-collective", + "pallet-conviction-voting", "pallet-democracy", "pallet-elections-phragmen", "pallet-indices", @@ -6400,6 +6414,7 @@ dependencies = [ "pallet-preimage", "pallet-proxy", "pallet-recovery", + "pallet-referenda", "pallet-root-testing", "pallet-scheduler", "pallet-session", @@ -6410,6 +6425,7 @@ dependencies = [ "pallet-transaction-payment-rpc-runtime-api", "pallet-treasury", "pallet-utility", + "pallet-whitelist", "pallet-xcm", "parachain-info", "parity-scale-codec", @@ -6437,6 +6453,8 @@ dependencies = [ "staging-xcm", "staging-xcm-builder", "staging-xcm-executor", + "strum 0.24.1", + "strum_macros 0.24.3", "substrate-wasm-builder", ] diff --git a/runtime/acala/Cargo.toml b/runtime/acala/Cargo.toml index cc87888bc4..909bd10dde 100644 --- a/runtime/acala/Cargo.toml +++ b/runtime/acala/Cargo.toml @@ -13,6 +13,9 @@ scale-info = { version = "2.9.0", default-features = false, features = ["derive" serde_json = { version = "1.0.85", default-features = false, features = ["alloc"] } hex = { version = "0.4", default-features = false } hex-literal = { version = "0.4.1" } +strum = { version = "0.24", default-features = false, features = [ "derive" ] } +strum_macros = "0.24" +lazy_static = "1.4.0" # substrate frame-executive = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } @@ -53,6 +56,9 @@ sp-staking = { git = "https://github.com/paritytech/polkadot-sdk", branch = "rel sp-std = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } sp-transaction-pool = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } sp-version = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } +pallet-conviction-voting = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } +pallet-referenda = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } +pallet-whitelist = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } # cumulus cumulus-pallet-aura-ext = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } @@ -149,6 +155,7 @@ std = [ "hex/std", "scale-info/std", "serde_json/std", + "strum/std", "frame-benchmarking/std", "frame-executive/std", @@ -191,6 +198,9 @@ std = [ "sp-transaction-pool/std", "sp-version/std", "substrate-wasm-builder", + "pallet-conviction-voting/std", + "pallet-referenda/std", + "pallet-whitelist/std", "cumulus-pallet-aura-ext/std", "cumulus-pallet-dmp-queue/std", @@ -285,6 +295,9 @@ runtime-benchmarks = [ "pallet-utility/runtime-benchmarks", "sp-runtime/runtime-benchmarks", "sp-staking/runtime-benchmarks", + "pallet-conviction-voting/runtime-benchmarks", + "pallet-referenda/runtime-benchmarks", + "pallet-whitelist/runtime-benchmarks", "cumulus-pallet-parachain-system/runtime-benchmarks", "cumulus-pallet-xcmp-queue/runtime-benchmarks", @@ -348,6 +361,9 @@ try-runtime = [ "pallet-treasury/try-runtime", "pallet-utility/try-runtime", "pallet-bags-list/try-runtime", + "pallet-conviction-voting/try-runtime", + "pallet-referenda/try-runtime", + "pallet-whitelist/try-runtime", "cumulus-pallet-aura-ext/try-runtime", "cumulus-pallet-dmp-queue/try-runtime", diff --git a/runtime/acala/src/governance/councils.rs b/runtime/acala/src/governance/councils.rs new file mode 100644 index 0000000000..31bc170ce2 --- /dev/null +++ b/runtime/acala/src/governance/councils.rs @@ -0,0 +1,144 @@ +// This file is part of Acala. + +// Copyright (C) 2020-2023 Acala Foundation. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +//! Councils for Gov1 and Gov2 + +use super::*; + +parameter_types! { + pub const GeneralCouncilMotionDuration: BlockNumber = 3 * DAYS; + pub const CouncilDefaultMaxProposals: u32 = 20; + pub const CouncilDefaultMaxMembers: u32 = 30; + pub MaxProposalWeight: Weight = Perbill::from_percent(50) * RuntimeBlockWeights::get().max_block; +} + +impl pallet_collective::Config for Runtime { + type RuntimeOrigin = RuntimeOrigin; + type Proposal = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type MotionDuration = GeneralCouncilMotionDuration; + type MaxProposals = CouncilDefaultMaxProposals; + type MaxMembers = CouncilDefaultMaxMembers; + type DefaultVote = pallet_collective::PrimeDefaultVote; + type SetMembersOrigin = EnsureRoot; + type WeightInfo = (); + type MaxProposalWeight = MaxProposalWeight; +} + +impl pallet_membership::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type AddOrigin = EnsureRootOrThreeFourthsGeneralCouncil; + type RemoveOrigin = EnsureRootOrThreeFourthsGeneralCouncil; + type SwapOrigin = EnsureRootOrThreeFourthsGeneralCouncil; + type ResetOrigin = EnsureRootOrThreeFourthsGeneralCouncil; + type PrimeOrigin = EnsureRootOrThreeFourthsGeneralCouncil; + type MembershipInitialized = GeneralCouncil; + type MembershipChanged = GeneralCouncil; + type MaxMembers = CouncilDefaultMaxMembers; + type WeightInfo = (); +} + +parameter_types! { + pub const FinancialCouncilMotionDuration: BlockNumber = 3 * DAYS; +} + +impl pallet_collective::Config for Runtime { + type RuntimeOrigin = RuntimeOrigin; + type Proposal = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type MotionDuration = FinancialCouncilMotionDuration; + type MaxProposals = CouncilDefaultMaxProposals; + type MaxMembers = CouncilDefaultMaxMembers; + type DefaultVote = pallet_collective::PrimeDefaultVote; + type SetMembersOrigin = EnsureRoot; + type WeightInfo = (); + type MaxProposalWeight = MaxProposalWeight; +} + +impl pallet_membership::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type AddOrigin = EnsureRootOrTwoThirdsGeneralCouncil; + type RemoveOrigin = EnsureRootOrTwoThirdsGeneralCouncil; + type SwapOrigin = EnsureRootOrTwoThirdsGeneralCouncil; + type ResetOrigin = EnsureRootOrTwoThirdsGeneralCouncil; + type PrimeOrigin = EnsureRootOrTwoThirdsGeneralCouncil; + type MembershipInitialized = FinancialCouncil; + type MembershipChanged = FinancialCouncil; + type MaxMembers = CouncilDefaultMaxMembers; + type WeightInfo = (); +} + +parameter_types! { + pub const HomaCouncilMotionDuration: BlockNumber = 3 * DAYS; +} + +impl pallet_collective::Config for Runtime { + type RuntimeOrigin = RuntimeOrigin; + type Proposal = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type MotionDuration = HomaCouncilMotionDuration; + type MaxProposals = CouncilDefaultMaxProposals; + type MaxMembers = CouncilDefaultMaxMembers; + type DefaultVote = pallet_collective::PrimeDefaultVote; + type SetMembersOrigin = EnsureRoot; + type WeightInfo = (); + type MaxProposalWeight = MaxProposalWeight; +} + +impl pallet_membership::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type AddOrigin = EnsureRootOrTwoThirdsGeneralCouncil; + type RemoveOrigin = EnsureRootOrTwoThirdsGeneralCouncil; + type SwapOrigin = EnsureRootOrTwoThirdsGeneralCouncil; + type ResetOrigin = EnsureRootOrTwoThirdsGeneralCouncil; + type PrimeOrigin = EnsureRootOrTwoThirdsGeneralCouncil; + type MembershipInitialized = HomaCouncil; + type MembershipChanged = HomaCouncil; + type MaxMembers = CouncilDefaultMaxMembers; + type WeightInfo = (); +} + +parameter_types! { + pub const TechnicalCommitteeMotionDuration: BlockNumber = 3 * DAYS; +} + +impl pallet_collective::Config for Runtime { + type RuntimeOrigin = RuntimeOrigin; + type Proposal = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type MotionDuration = TechnicalCommitteeMotionDuration; + type MaxProposals = CouncilDefaultMaxProposals; + type MaxMembers = CouncilDefaultMaxMembers; + type DefaultVote = pallet_collective::PrimeDefaultVote; + type SetMembersOrigin = EnsureRoot; + type WeightInfo = (); + type MaxProposalWeight = MaxProposalWeight; +} + +impl pallet_membership::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type AddOrigin = EnsureRootOrTwoThirdsGeneralCouncil; + type RemoveOrigin = EnsureRootOrTwoThirdsGeneralCouncil; + type SwapOrigin = EnsureRootOrTwoThirdsGeneralCouncil; + type ResetOrigin = EnsureRootOrTwoThirdsGeneralCouncil; + type PrimeOrigin = EnsureRootOrTwoThirdsGeneralCouncil; + type MembershipInitialized = TechnicalCommittee; + type MembershipChanged = TechnicalCommittee; + type MaxMembers = CouncilDefaultMaxMembers; + type WeightInfo = (); +} diff --git a/runtime/acala/src/governance/democracy.rs b/runtime/acala/src/governance/democracy.rs new file mode 100644 index 0000000000..4ec5df6265 --- /dev/null +++ b/runtime/acala/src/governance/democracy.rs @@ -0,0 +1,74 @@ +// This file is part of Acala. + +// Copyright (C) 2020-2023 Acala Foundation. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +//! Democracy config for Gov1 + +use crate::*; + +parameter_types! { + pub const LaunchPeriod: BlockNumber = 5 * DAYS; + pub const VotingPeriod: BlockNumber = 5 * DAYS; + pub const FastTrackVotingPeriod: BlockNumber = 3 * HOURS; + pub MinimumDeposit: Balance = 1000 * dollar(ACA); + pub const EnactmentPeriod: BlockNumber = 2 * DAYS; + pub const VoteLockingPeriod: BlockNumber = 14 * DAYS; + pub const CooloffPeriod: BlockNumber = 7 * DAYS; +} + +impl pallet_democracy::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Currency = Balances; + type EnactmentPeriod = EnactmentPeriod; + type LaunchPeriod = LaunchPeriod; + type VotingPeriod = VotingPeriod; + type VoteLockingPeriod = VoteLockingPeriod; + type MinimumDeposit = MinimumDeposit; + /// A straight majority of the council can decide what their next motion is. + type ExternalOrigin = EnsureRootOrHalfGeneralCouncil; + /// A majority can have the next scheduled referendum be a straight majority-carries vote. + type ExternalMajorityOrigin = EnsureRootOrHalfGeneralCouncil; + /// A unanimous council can have the next scheduled referendum be a straight default-carries + /// (NTB) vote. + type ExternalDefaultOrigin = EnsureRootOrAllGeneralCouncil; + /// Two thirds of the technical committee can have an ExternalMajority/ExternalDefault vote + /// be tabled immediately and with a shorter voting/enactment period. + type FastTrackOrigin = EnsureRootOrTwoThirdsTechnicalCommittee; + type InstantOrigin = EnsureRootOrAllTechnicalCommittee; + type InstantAllowed = ConstBool; + type FastTrackVotingPeriod = FastTrackVotingPeriod; + // To cancel a proposal which has been passed, 2/3 of the council must agree to it. + type CancellationOrigin = EnsureRootOrTwoThirdsGeneralCouncil; + type BlacklistOrigin = EnsureRoot; + // To cancel a proposal before it has been passed, the technical committee must be unanimous or + // Root must agree. + type CancelProposalOrigin = EnsureRootOrAllTechnicalCommittee; + // 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 Slash = Treasury; + type Scheduler = Scheduler; + type PalletsOrigin = OriginCaller; + type MaxVotes = ConstU32<100>; + type WeightInfo = pallet_democracy::weights::SubstrateWeight; + type MaxProposals = ConstU32<100>; + type Preimages = Preimage; + type MaxDeposits = ConstU32<100>; + type MaxBlacklisted = ConstU32<100>; + type SubmitOrigin = EnsureSigned; +} diff --git a/runtime/acala/src/governance/mod.rs b/runtime/acala/src/governance/mod.rs new file mode 100644 index 0000000000..9d8000ef01 --- /dev/null +++ b/runtime/acala/src/governance/mod.rs @@ -0,0 +1,30 @@ +// This file is part of Acala. + +// Copyright (C) 2020-2023 Acala Foundation. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +//! Governance configurations + +pub mod councils; +mod democracy; +pub mod referenda; + +use super::*; + +mod origins; +pub use origins::{custom_origins, GeneralAdmin, ReferendumCanceller, ReferendumKiller, WhitelistedCaller}; +mod tracks; +pub use tracks::TracksInfo; diff --git a/runtime/acala/src/governance/origins.rs b/runtime/acala/src/governance/origins.rs new file mode 100644 index 0000000000..7f7d7237a0 --- /dev/null +++ b/runtime/acala/src/governance/origins.rs @@ -0,0 +1,79 @@ +// This file is part of Acala. + +// Copyright (C) 2020-2023 Acala Foundation. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +//! Custom origins for governance interventions. + +pub use custom_origins::*; + +#[frame_support::pallet] +pub mod custom_origins { + use frame_support::pallet_prelude::*; + use strum_macros::EnumString; + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + + #[derive(PartialEq, Eq, Clone, MaxEncodedLen, Encode, Decode, TypeInfo, RuntimeDebug, EnumString)] + #[strum(serialize_all = "snake_case")] + #[pallet::origin] + pub enum Origin { + /// Origin able to dispatch a whitelisted call. + WhitelistedCaller, + /// General admin + GeneralAdmin, + /// Origin able to cancel referenda. + ReferendumCanceller, + /// Origin able to kill referenda. + ReferendumKiller, + } + + macro_rules! decl_unit_ensures { + ( $name:ident: $success_type:ty = $success:expr ) => { + pub struct $name; + impl> + From> + EnsureOrigin for $name + { + type Success = $success_type; + fn try_origin(o: O) -> Result { + o.into().and_then(|o| match o { + Origin::$name => Ok($success), + r => Err(O::from(r)), + }) + } + #[cfg(feature = "runtime-benchmarks")] + fn try_successful_origin() -> Result { + Ok(O::from(Origin::$name)) + } + } + }; + ( $name:ident ) => { decl_unit_ensures! { $name : () = () } }; + ( $name:ident: $success_type:ty = $success:expr, $( $rest:tt )* ) => { + decl_unit_ensures! { $name: $success_type = $success } + decl_unit_ensures! { $( $rest )* } + }; + ( $name:ident, $( $rest:tt )* ) => { + decl_unit_ensures! { $name } + decl_unit_ensures! { $( $rest )* } + }; + () => {} + } + decl_unit_ensures!(ReferendumCanceller, ReferendumKiller, WhitelistedCaller, GeneralAdmin); +} diff --git a/runtime/acala/src/governance/referenda.rs b/runtime/acala/src/governance/referenda.rs new file mode 100644 index 0000000000..cd46634046 --- /dev/null +++ b/runtime/acala/src/governance/referenda.rs @@ -0,0 +1,94 @@ +// This file is part of Acala. + +// Copyright (C) 2020-2023 Acala Foundation. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +//! # Gov2 config +//! Includes runtime configs for these substrate pallets: +//! 1. pallet-conviction-voting +//! 2. pallet-whitelist +//! 3. pallet-referenda + +use super::*; +use frame_support::traits::{EitherOf, MapSuccess}; +use frame_system::EnsureRootWithSuccess; +use sp_runtime::traits::Replace; + +parameter_types! { + pub const VoteLockingPeriod: BlockNumber = 1 * DAYS; +} + +impl pallet_conviction_voting::Config for Runtime { + type WeightInfo = (); + type RuntimeEvent = RuntimeEvent; + type Currency = Balances; + type Polls = Referenda; + type MaxTurnout = frame_support::traits::TotalIssuanceOf; + // Maximum number of concurrent votes an account may have + type MaxVotes = ConstU32<20>; + // Minimum period of vote locking + type VoteLockingPeriod = VoteLockingPeriod; +} + +// Origin for general admin or root +pub type GeneralAdminOrRoot = EitherOf, origins::GeneralAdmin>; + +impl custom_origins::Config for Runtime {} + +// The purpose of this pallet is to queue calls to be dispatched as by root later => the Dispatch +// origin corresponds to the Gov2 Whitelist track. +impl pallet_whitelist::Config for Runtime { + type WeightInfo = (); + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type WhitelistOrigin = EitherOf< + EnsureRootWithSuccess>, + MapSuccess< + pallet_collective::EnsureProportionAtLeast, + Replace>, + >, + >; + type DispatchWhitelistedOrigin = EitherOf, WhitelistedCaller>; + type Preimages = Preimage; +} + +pallet_referenda::impl_tracksinfo_get!(TracksInfo, Balance, BlockNumber); + +parameter_types! { + pub const AlarmInterval: BlockNumber = 1; + pub SubmissionDeposit: Balance = 10 * dollar(ACA); + pub const UndecidingTimeout: BlockNumber = 14 * DAYS; +} + +impl pallet_referenda::Config for Runtime { + type WeightInfo = (); + type RuntimeCall = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type Scheduler = Scheduler; + type Currency = Balances; + type SubmitOrigin = frame_system::EnsureSigned; + type CancelOrigin = EitherOf, ReferendumCanceller>; + type KillOrigin = EitherOf, ReferendumKiller>; + type Slash = Treasury; + type Votes = pallet_conviction_voting::VotesOf; + type Tally = pallet_conviction_voting::TallyOf; + type SubmissionDeposit = SubmissionDeposit; + type MaxQueued = ConstU32<100>; + type UndecidingTimeout = UndecidingTimeout; + type AlarmInterval = AlarmInterval; + type Tracks = TracksInfo; + type Preimages = Preimage; +} diff --git a/runtime/acala/src/governance/tracks.rs b/runtime/acala/src/governance/tracks.rs new file mode 100644 index 0000000000..ef053401e9 --- /dev/null +++ b/runtime/acala/src/governance/tracks.rs @@ -0,0 +1,178 @@ +// This file is part of Acala. + +// Copyright (C) 2020-2023 Acala Foundation. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +//! Track configurations for governance. + +use super::*; +use pallet_referenda::Curve; +use sp_std::str::FromStr; + +const fn percent(x: i32) -> sp_runtime::FixedI64 { + sp_runtime::FixedI64::from_rational(x as u128, 100) +} +const fn permill(x: i32) -> sp_runtime::FixedI64 { + sp_runtime::FixedI64::from_rational(x as u128, 1000) +} + +lazy_static::lazy_static! { +static ref TRACKS_DATA: [(u16, pallet_referenda::TrackInfo); 5] = [ + ( + 0, + pallet_referenda::TrackInfo { + // Name of this track. + name: "root", + // A limit for the number of referenda on this track that can be being decided at once. + // For Root origin this should generally be just one. + max_deciding: 5, + // Amount that must be placed on deposit before a decision can be made. + decision_deposit: 20 * 1000 * dollar(ACA), + // Amount of time this must be submitted for before a decision can be made. + prepare_period: 1 * DAYS, + // Amount of time that a decision may take to be approved prior to cancellation. + decision_period: 14 * DAYS, + // Amount of time that the approval criteria must hold before it can be approved. + confirm_period: 1 * DAYS, + // Minimum amount of time that an approved proposal must be in the dispatch queue. + min_enactment_period: 1 * DAYS, + // Minimum aye votes as percentage of overall conviction-weighted votes needed for + // approval as a function of time into decision period. + min_approval: Curve::make_reciprocal(4, 14, percent(80), percent(50), percent(100)), + // Minimum pre-conviction aye-votes ("support") as percentage of overall population that + // is needed for approval as a function of time into decision period. + min_support: Curve::make_linear(14, 14, permill(5), percent(25)), + }, + ), + ( + 1, + pallet_referenda::TrackInfo { + name: "whitelisted_caller", + max_deciding: 100, + decision_deposit: 2 * 1000 * dollar(ACA), + prepare_period: 10 * MINUTES, + decision_period: 14 * DAYS, + confirm_period: 10 * MINUTES, + min_enactment_period: 30 * MINUTES, + min_approval: Curve::make_reciprocal(1, 14, percent(96), percent(50), percent(100)), + min_support: Curve::make_reciprocal(1, 14 * 24, percent(1), percent(0), percent(2)), + }, + ), + ( + 2, + pallet_referenda::TrackInfo { + name: "general_admin", + max_deciding: 10, + decision_deposit: 1000 * dollar(ACA), + prepare_period: 1 * HOURS, + decision_period: 14 * DAYS, + confirm_period: 1 * DAYS, + min_enactment_period: 1 * DAYS, + min_approval: Curve::make_reciprocal(4, 14, percent(80), percent(50), percent(100)), + min_support: Curve::make_reciprocal(7, 14, percent(10), percent(0), percent(50)), + }, + ), + ( + 3, + pallet_referenda::TrackInfo { + name: "referendum_canceller", + max_deciding: 20, + decision_deposit: 2 * 1000 * dollar(ACA), + prepare_period: 1 * HOURS, + decision_period: 14 * DAYS, + confirm_period: 3 * HOURS, + min_enactment_period: 10 * MINUTES, + min_approval: Curve::make_reciprocal(1, 14, percent(96), percent(50), percent(100)), + min_support: Curve::make_reciprocal(1, 14, percent(1), percent(0), percent(10)), + }, + ), + ( + 4, + pallet_referenda::TrackInfo { + name: "referendum_killer", + max_deciding: 100, + decision_deposit: 4 * 1000 * dollar(ACA), + prepare_period: 1 * HOURS, + decision_period: 14 * DAYS, + confirm_period: 3 * HOURS, + min_enactment_period: 10 * MINUTES, + min_approval: Curve::make_reciprocal(1, 14, percent(96), percent(50), percent(100)), + min_support: Curve::make_reciprocal(1, 14, percent(1), percent(0), percent(10)), + }, + ), +]; +} + +pub struct TracksInfo; +impl pallet_referenda::TracksInfo for TracksInfo { + type Id = u16; + type RuntimeOrigin = ::PalletsOrigin; + fn tracks() -> &'static [(Self::Id, pallet_referenda::TrackInfo)] { + &TRACKS_DATA[..] + } + fn track_for(id: &Self::RuntimeOrigin) -> Result { + if let Ok(system_origin) = frame_system::RawOrigin::try_from(id.clone()) { + match system_origin { + frame_system::RawOrigin::Root => { + if let Some((track_id, _)) = Self::tracks().into_iter().find(|(_, track)| track.name == "root") { + Ok(*track_id) + } else { + Err(()) + } + } + _ => Err(()), + } + } else if let Ok(custom_origin) = custom_origins::Origin::try_from(id.clone()) { + if let Some((track_id, _)) = Self::tracks().into_iter().find(|(_, track)| { + if let Ok(track_custom_origin) = custom_origins::Origin::from_str(track.name) { + track_custom_origin == custom_origin + } else { + false + } + }) { + Ok(*track_id) + } else { + Err(()) + } + } else { + Err(()) + } + } +} + +#[test] +/// To ensure voters are always locked into their vote +fn vote_locking_always_longer_than_enactment_period() { + for (_, track) in (&TRACKS_DATA).iter() { + assert!( + ::VoteLockingPeriod::get() >= track.min_enactment_period, + "Track {} has enactment period {} < vote locking period {}", + track.name, + track.min_enactment_period, + ::VoteLockingPeriod::get(), + ); + } +} + +#[test] +fn all_tracks_have_origins() { + for (_, track) in (&TRACKS_DATA).iter() { + // check name.into() is successful either converts into "root" or custom origin + let track_is_root = track.name == "root"; + let track_has_custom_origin = custom_origins::Origin::from_str(track.name).is_ok(); + assert!(track_is_root || track_has_custom_origin); + } +} diff --git a/runtime/acala/src/lib.rs b/runtime/acala/src/lib.rs index 7c4e54c522..8b5ed30ec2 100644 --- a/runtime/acala/src/lib.rs +++ b/runtime/acala/src/lib.rs @@ -118,9 +118,11 @@ pub use xcm::v3::prelude::*; mod authority; mod benchmarking; pub mod constants; +pub mod governance; /// Weights for pallets used in the runtime. mod weights; pub mod xcm_config; +use governance::councils::*; /// This runtime version. #[sp_version::runtime_version] @@ -395,129 +397,6 @@ impl pallet_sudo::Config for Runtime { type WeightInfo = (); } -parameter_types! { - pub const GeneralCouncilMotionDuration: BlockNumber = 3 * DAYS; - pub const CouncilDefaultMaxProposals: u32 = 20; - pub const CouncilDefaultMaxMembers: u32 = 30; - pub MaxProposalWeight: Weight = Perbill::from_percent(50) * RuntimeBlockWeights::get().max_block; -} - -impl pallet_collective::Config for Runtime { - type RuntimeOrigin = RuntimeOrigin; - type Proposal = RuntimeCall; - type RuntimeEvent = RuntimeEvent; - type MotionDuration = GeneralCouncilMotionDuration; - type MaxProposals = CouncilDefaultMaxProposals; - type MaxMembers = CouncilDefaultMaxMembers; - type DefaultVote = pallet_collective::PrimeDefaultVote; - type SetMembersOrigin = EnsureRoot; - type WeightInfo = (); - type MaxProposalWeight = MaxProposalWeight; -} - -impl pallet_membership::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type AddOrigin = EnsureRootOrThreeFourthsGeneralCouncil; - type RemoveOrigin = EnsureRootOrThreeFourthsGeneralCouncil; - type SwapOrigin = EnsureRootOrThreeFourthsGeneralCouncil; - type ResetOrigin = EnsureRootOrThreeFourthsGeneralCouncil; - type PrimeOrigin = EnsureRootOrThreeFourthsGeneralCouncil; - type MembershipInitialized = GeneralCouncil; - type MembershipChanged = GeneralCouncil; - type MaxMembers = CouncilDefaultMaxMembers; - type WeightInfo = (); -} - -parameter_types! { - pub const FinancialCouncilMotionDuration: BlockNumber = 3 * DAYS; -} - -impl pallet_collective::Config for Runtime { - type RuntimeOrigin = RuntimeOrigin; - type Proposal = RuntimeCall; - type RuntimeEvent = RuntimeEvent; - type MotionDuration = FinancialCouncilMotionDuration; - type MaxProposals = CouncilDefaultMaxProposals; - type MaxMembers = CouncilDefaultMaxMembers; - type DefaultVote = pallet_collective::PrimeDefaultVote; - type SetMembersOrigin = EnsureRoot; - type WeightInfo = (); - type MaxProposalWeight = MaxProposalWeight; -} - -impl pallet_membership::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type AddOrigin = EnsureRootOrTwoThirdsGeneralCouncil; - type RemoveOrigin = EnsureRootOrTwoThirdsGeneralCouncil; - type SwapOrigin = EnsureRootOrTwoThirdsGeneralCouncil; - type ResetOrigin = EnsureRootOrTwoThirdsGeneralCouncil; - type PrimeOrigin = EnsureRootOrTwoThirdsGeneralCouncil; - type MembershipInitialized = FinancialCouncil; - type MembershipChanged = FinancialCouncil; - type MaxMembers = CouncilDefaultMaxMembers; - type WeightInfo = (); -} - -parameter_types! { - pub const HomaCouncilMotionDuration: BlockNumber = 3 * DAYS; -} - -impl pallet_collective::Config for Runtime { - type RuntimeOrigin = RuntimeOrigin; - type Proposal = RuntimeCall; - type RuntimeEvent = RuntimeEvent; - type MotionDuration = HomaCouncilMotionDuration; - type MaxProposals = CouncilDefaultMaxProposals; - type MaxMembers = CouncilDefaultMaxMembers; - type DefaultVote = pallet_collective::PrimeDefaultVote; - type SetMembersOrigin = EnsureRoot; - type WeightInfo = (); - type MaxProposalWeight = MaxProposalWeight; -} - -impl pallet_membership::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type AddOrigin = EnsureRootOrTwoThirdsGeneralCouncil; - type RemoveOrigin = EnsureRootOrTwoThirdsGeneralCouncil; - type SwapOrigin = EnsureRootOrTwoThirdsGeneralCouncil; - type ResetOrigin = EnsureRootOrTwoThirdsGeneralCouncil; - type PrimeOrigin = EnsureRootOrTwoThirdsGeneralCouncil; - type MembershipInitialized = HomaCouncil; - type MembershipChanged = HomaCouncil; - type MaxMembers = CouncilDefaultMaxMembers; - type WeightInfo = (); -} - -parameter_types! { - pub const TechnicalCommitteeMotionDuration: BlockNumber = 3 * DAYS; -} - -impl pallet_collective::Config for Runtime { - type RuntimeOrigin = RuntimeOrigin; - type Proposal = RuntimeCall; - type RuntimeEvent = RuntimeEvent; - type MotionDuration = TechnicalCommitteeMotionDuration; - type MaxProposals = CouncilDefaultMaxProposals; - type MaxMembers = CouncilDefaultMaxMembers; - type DefaultVote = pallet_collective::PrimeDefaultVote; - type SetMembersOrigin = EnsureRoot; - type WeightInfo = (); - type MaxProposalWeight = MaxProposalWeight; -} - -impl pallet_membership::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type AddOrigin = EnsureRootOrTwoThirdsGeneralCouncil; - type RemoveOrigin = EnsureRootOrTwoThirdsGeneralCouncil; - type SwapOrigin = EnsureRootOrTwoThirdsGeneralCouncil; - type ResetOrigin = EnsureRootOrTwoThirdsGeneralCouncil; - type PrimeOrigin = EnsureRootOrTwoThirdsGeneralCouncil; - type MembershipInitialized = TechnicalCommittee; - type MembershipChanged = TechnicalCommittee; - type MaxMembers = CouncilDefaultMaxMembers; - type WeightInfo = (); -} - impl pallet_membership::Config for Runtime { type RuntimeEvent = RuntimeEvent; type AddOrigin = EnsureRootOrTwoThirdsGeneralCouncil; @@ -647,59 +526,6 @@ impl pallet_tips::Config for Runtime { type WeightInfo = (); } -parameter_types! { - pub const LaunchPeriod: BlockNumber = 5 * DAYS; - pub const VotingPeriod: BlockNumber = 5 * DAYS; - pub const FastTrackVotingPeriod: BlockNumber = 3 * HOURS; - pub MinimumDeposit: Balance = 1000 * dollar(ACA); - pub const EnactmentPeriod: BlockNumber = 2 * DAYS; - pub const VoteLockingPeriod: BlockNumber = 14 * DAYS; - pub const CooloffPeriod: BlockNumber = 7 * DAYS; -} - -impl pallet_democracy::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type Currency = Balances; - type EnactmentPeriod = EnactmentPeriod; - type LaunchPeriod = LaunchPeriod; - type VotingPeriod = VotingPeriod; - type VoteLockingPeriod = VoteLockingPeriod; - type MinimumDeposit = MinimumDeposit; - /// A straight majority of the council can decide what their next motion is. - type ExternalOrigin = EnsureRootOrHalfGeneralCouncil; - /// A majority can have the next scheduled referendum be a straight majority-carries vote. - type ExternalMajorityOrigin = EnsureRootOrHalfGeneralCouncil; - /// A unanimous council can have the next scheduled referendum be a straight default-carries - /// (NTB) vote. - type ExternalDefaultOrigin = EnsureRootOrAllGeneralCouncil; - /// Two thirds of the technical committee can have an ExternalMajority/ExternalDefault vote - /// be tabled immediately and with a shorter voting/enactment period. - type FastTrackOrigin = EnsureRootOrTwoThirdsTechnicalCommittee; - type InstantOrigin = EnsureRootOrAllTechnicalCommittee; - type InstantAllowed = ConstBool; - type FastTrackVotingPeriod = FastTrackVotingPeriod; - // To cancel a proposal which has been passed, 2/3 of the council must agree to it. - type CancellationOrigin = EnsureRootOrTwoThirdsGeneralCouncil; - type BlacklistOrigin = EnsureRoot; - // To cancel a proposal before it has been passed, the technical committee must be unanimous or - // Root must agree. - type CancelProposalOrigin = EnsureRootOrAllTechnicalCommittee; - // 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 Slash = Treasury; - type Scheduler = Scheduler; - type PalletsOrigin = OriginCaller; - type MaxVotes = ConstU32<100>; - type WeightInfo = pallet_democracy::weights::SubstrateWeight; - type MaxProposals = ConstU32<100>; - type Preimages = Preimage; - type MaxDeposits = ConstU32<100>; - type MaxBlacklisted = ConstU32<100>; - type SubmitOrigin = EnsureSigned; -} - impl orml_auction::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Balance = Balance; @@ -1787,6 +1613,10 @@ construct_runtime!( TechnicalCommittee: pallet_collective:: = 67, TechnicalCommitteeMembership: pallet_membership:: = 68, Democracy: pallet_democracy = 69, + ConvictionVoting: pallet_conviction_voting = 160, + Referenda: pallet_referenda = 161, + Origins: governance::custom_origins::{Origin} = 162, + Whitelist: pallet_whitelist = 163, // Oracle // diff --git a/runtime/karura/Cargo.toml b/runtime/karura/Cargo.toml index df78846da3..d5c692b22e 100644 --- a/runtime/karura/Cargo.toml +++ b/runtime/karura/Cargo.toml @@ -13,6 +13,9 @@ scale-info = { version = "2.9.0", default-features = false, features = ["derive" serde_json = { version = "1.0.85", default-features = false, features = ["alloc"] } hex = { version = "0.4", default-features = false } hex-literal = { version = "0.4.1" } +strum = { version = "0.24", default-features = false, features = [ "derive" ] } +strum_macros = "0.24" +lazy_static = "1.4.0" # substrate frame-executive = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } @@ -53,6 +56,9 @@ sp-staking = { git = "https://github.com/paritytech/polkadot-sdk", branch = "rel sp-std = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } sp-transaction-pool = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } sp-version = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } +pallet-conviction-voting = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } +pallet-referenda = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } +pallet-whitelist = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } # cumulus cumulus-pallet-aura-ext = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } @@ -149,6 +155,7 @@ std = [ "hex/std", "scale-info/std", "serde_json/std", + "strum/std", "frame-benchmarking/std", "frame-executive/std", @@ -192,6 +199,9 @@ std = [ "sp-transaction-pool/std", "sp-version/std", "substrate-wasm-builder", + "pallet-conviction-voting/std", + "pallet-referenda/std", + "pallet-whitelist/std", "cumulus-pallet-aura-ext/std", "cumulus-pallet-dmp-queue/std", @@ -286,6 +296,9 @@ runtime-benchmarks = [ "pallet-utility/runtime-benchmarks", "sp-runtime/runtime-benchmarks", "sp-staking/runtime-benchmarks", + "pallet-conviction-voting/runtime-benchmarks", + "pallet-referenda/runtime-benchmarks", + "pallet-whitelist/runtime-benchmarks", "cumulus-pallet-parachain-system/runtime-benchmarks", "cumulus-pallet-xcmp-queue/runtime-benchmarks", @@ -349,6 +362,9 @@ try-runtime = [ "pallet-treasury/try-runtime", "pallet-utility/try-runtime", "pallet-bags-list/try-runtime", + "pallet-conviction-voting/try-runtime", + "pallet-referenda/try-runtime", + "pallet-whitelist/try-runtime", "cumulus-pallet-aura-ext/try-runtime", "cumulus-pallet-dmp-queue/try-runtime", diff --git a/runtime/karura/src/governance/councils.rs b/runtime/karura/src/governance/councils.rs new file mode 100644 index 0000000000..31bc170ce2 --- /dev/null +++ b/runtime/karura/src/governance/councils.rs @@ -0,0 +1,144 @@ +// This file is part of Acala. + +// Copyright (C) 2020-2023 Acala Foundation. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +//! Councils for Gov1 and Gov2 + +use super::*; + +parameter_types! { + pub const GeneralCouncilMotionDuration: BlockNumber = 3 * DAYS; + pub const CouncilDefaultMaxProposals: u32 = 20; + pub const CouncilDefaultMaxMembers: u32 = 30; + pub MaxProposalWeight: Weight = Perbill::from_percent(50) * RuntimeBlockWeights::get().max_block; +} + +impl pallet_collective::Config for Runtime { + type RuntimeOrigin = RuntimeOrigin; + type Proposal = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type MotionDuration = GeneralCouncilMotionDuration; + type MaxProposals = CouncilDefaultMaxProposals; + type MaxMembers = CouncilDefaultMaxMembers; + type DefaultVote = pallet_collective::PrimeDefaultVote; + type SetMembersOrigin = EnsureRoot; + type WeightInfo = (); + type MaxProposalWeight = MaxProposalWeight; +} + +impl pallet_membership::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type AddOrigin = EnsureRootOrThreeFourthsGeneralCouncil; + type RemoveOrigin = EnsureRootOrThreeFourthsGeneralCouncil; + type SwapOrigin = EnsureRootOrThreeFourthsGeneralCouncil; + type ResetOrigin = EnsureRootOrThreeFourthsGeneralCouncil; + type PrimeOrigin = EnsureRootOrThreeFourthsGeneralCouncil; + type MembershipInitialized = GeneralCouncil; + type MembershipChanged = GeneralCouncil; + type MaxMembers = CouncilDefaultMaxMembers; + type WeightInfo = (); +} + +parameter_types! { + pub const FinancialCouncilMotionDuration: BlockNumber = 3 * DAYS; +} + +impl pallet_collective::Config for Runtime { + type RuntimeOrigin = RuntimeOrigin; + type Proposal = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type MotionDuration = FinancialCouncilMotionDuration; + type MaxProposals = CouncilDefaultMaxProposals; + type MaxMembers = CouncilDefaultMaxMembers; + type DefaultVote = pallet_collective::PrimeDefaultVote; + type SetMembersOrigin = EnsureRoot; + type WeightInfo = (); + type MaxProposalWeight = MaxProposalWeight; +} + +impl pallet_membership::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type AddOrigin = EnsureRootOrTwoThirdsGeneralCouncil; + type RemoveOrigin = EnsureRootOrTwoThirdsGeneralCouncil; + type SwapOrigin = EnsureRootOrTwoThirdsGeneralCouncil; + type ResetOrigin = EnsureRootOrTwoThirdsGeneralCouncil; + type PrimeOrigin = EnsureRootOrTwoThirdsGeneralCouncil; + type MembershipInitialized = FinancialCouncil; + type MembershipChanged = FinancialCouncil; + type MaxMembers = CouncilDefaultMaxMembers; + type WeightInfo = (); +} + +parameter_types! { + pub const HomaCouncilMotionDuration: BlockNumber = 3 * DAYS; +} + +impl pallet_collective::Config for Runtime { + type RuntimeOrigin = RuntimeOrigin; + type Proposal = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type MotionDuration = HomaCouncilMotionDuration; + type MaxProposals = CouncilDefaultMaxProposals; + type MaxMembers = CouncilDefaultMaxMembers; + type DefaultVote = pallet_collective::PrimeDefaultVote; + type SetMembersOrigin = EnsureRoot; + type WeightInfo = (); + type MaxProposalWeight = MaxProposalWeight; +} + +impl pallet_membership::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type AddOrigin = EnsureRootOrTwoThirdsGeneralCouncil; + type RemoveOrigin = EnsureRootOrTwoThirdsGeneralCouncil; + type SwapOrigin = EnsureRootOrTwoThirdsGeneralCouncil; + type ResetOrigin = EnsureRootOrTwoThirdsGeneralCouncil; + type PrimeOrigin = EnsureRootOrTwoThirdsGeneralCouncil; + type MembershipInitialized = HomaCouncil; + type MembershipChanged = HomaCouncil; + type MaxMembers = CouncilDefaultMaxMembers; + type WeightInfo = (); +} + +parameter_types! { + pub const TechnicalCommitteeMotionDuration: BlockNumber = 3 * DAYS; +} + +impl pallet_collective::Config for Runtime { + type RuntimeOrigin = RuntimeOrigin; + type Proposal = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type MotionDuration = TechnicalCommitteeMotionDuration; + type MaxProposals = CouncilDefaultMaxProposals; + type MaxMembers = CouncilDefaultMaxMembers; + type DefaultVote = pallet_collective::PrimeDefaultVote; + type SetMembersOrigin = EnsureRoot; + type WeightInfo = (); + type MaxProposalWeight = MaxProposalWeight; +} + +impl pallet_membership::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type AddOrigin = EnsureRootOrTwoThirdsGeneralCouncil; + type RemoveOrigin = EnsureRootOrTwoThirdsGeneralCouncil; + type SwapOrigin = EnsureRootOrTwoThirdsGeneralCouncil; + type ResetOrigin = EnsureRootOrTwoThirdsGeneralCouncil; + type PrimeOrigin = EnsureRootOrTwoThirdsGeneralCouncil; + type MembershipInitialized = TechnicalCommittee; + type MembershipChanged = TechnicalCommittee; + type MaxMembers = CouncilDefaultMaxMembers; + type WeightInfo = (); +} diff --git a/runtime/karura/src/governance/democracy.rs b/runtime/karura/src/governance/democracy.rs new file mode 100644 index 0000000000..3a809c556f --- /dev/null +++ b/runtime/karura/src/governance/democracy.rs @@ -0,0 +1,75 @@ +// This file is part of Acala. + +// Copyright (C) 2020-2023 Acala Foundation. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +//! Democracy config for Gov1 + +use crate::*; + +parameter_types! { + pub const LaunchPeriod: BlockNumber = 5 * DAYS; + pub const VotingPeriod: BlockNumber = 5 * DAYS; + pub const FastTrackVotingPeriod: BlockNumber = 3 * HOURS; + pub MinimumDeposit: Balance = 100 * dollar(KAR); + pub const EnactmentPeriod: BlockNumber = 2 * DAYS; + pub const VoteLockingPeriod: BlockNumber = 7 * DAYS; + pub const CooloffPeriod: BlockNumber = 7 * DAYS; +} + +impl pallet_democracy::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Currency = Balances; + type EnactmentPeriod = EnactmentPeriod; + type LaunchPeriod = LaunchPeriod; + type VotingPeriod = VotingPeriod; + type VoteLockingPeriod = VoteLockingPeriod; + type MinimumDeposit = MinimumDeposit; + /// A straight majority of the council can decide what their next motion is. + type ExternalOrigin = EnsureRootOrHalfGeneralCouncil; + /// A majority can have the next scheduled referendum be a straight majority-carries vote. + type ExternalMajorityOrigin = EnsureRootOrHalfGeneralCouncil; + /// A unanimous council can have the next scheduled referendum be a straight default-carries + /// (NTB) vote. + type ExternalDefaultOrigin = EnsureRootOrAllGeneralCouncil; + /// Two thirds of the technical committee can have an ExternalMajority/ExternalDefault vote + /// be tabled immediately and with a shorter voting/enactment period. + type FastTrackOrigin = EnsureRootOrTwoThirdsTechnicalCommittee; + type InstantOrigin = EnsureRootOrAllTechnicalCommittee; + type InstantAllowed = ConstBool; + type FastTrackVotingPeriod = FastTrackVotingPeriod; + // To cancel a proposal which has been passed, 2/3 of the council must agree to it. + type CancellationOrigin = EnsureRootOrTwoThirdsGeneralCouncil; + type BlacklistOrigin = EnsureRoot; + // To cancel a proposal before it has been passed, the technical committee must be unanimous or + // Root must agree. + type CancelProposalOrigin = EnsureRootOrAllTechnicalCommittee; + // 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 Slash = Treasury; + type Scheduler = Scheduler; + type PalletsOrigin = OriginCaller; + type MaxVotes = ConstU32<100>; + //TODO: might need to weight for Karura + type WeightInfo = pallet_democracy::weights::SubstrateWeight; + type MaxProposals = ConstU32<100>; + type Preimages = Preimage; + type MaxDeposits = ConstU32<100>; + type MaxBlacklisted = ConstU32<100>; + type SubmitOrigin = EnsureSigned; +} diff --git a/runtime/karura/src/governance/mod.rs b/runtime/karura/src/governance/mod.rs new file mode 100644 index 0000000000..9d8000ef01 --- /dev/null +++ b/runtime/karura/src/governance/mod.rs @@ -0,0 +1,30 @@ +// This file is part of Acala. + +// Copyright (C) 2020-2023 Acala Foundation. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +//! Governance configurations + +pub mod councils; +mod democracy; +pub mod referenda; + +use super::*; + +mod origins; +pub use origins::{custom_origins, GeneralAdmin, ReferendumCanceller, ReferendumKiller, WhitelistedCaller}; +mod tracks; +pub use tracks::TracksInfo; diff --git a/runtime/karura/src/governance/origins.rs b/runtime/karura/src/governance/origins.rs new file mode 100644 index 0000000000..7f7d7237a0 --- /dev/null +++ b/runtime/karura/src/governance/origins.rs @@ -0,0 +1,79 @@ +// This file is part of Acala. + +// Copyright (C) 2020-2023 Acala Foundation. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +//! Custom origins for governance interventions. + +pub use custom_origins::*; + +#[frame_support::pallet] +pub mod custom_origins { + use frame_support::pallet_prelude::*; + use strum_macros::EnumString; + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + + #[derive(PartialEq, Eq, Clone, MaxEncodedLen, Encode, Decode, TypeInfo, RuntimeDebug, EnumString)] + #[strum(serialize_all = "snake_case")] + #[pallet::origin] + pub enum Origin { + /// Origin able to dispatch a whitelisted call. + WhitelistedCaller, + /// General admin + GeneralAdmin, + /// Origin able to cancel referenda. + ReferendumCanceller, + /// Origin able to kill referenda. + ReferendumKiller, + } + + macro_rules! decl_unit_ensures { + ( $name:ident: $success_type:ty = $success:expr ) => { + pub struct $name; + impl> + From> + EnsureOrigin for $name + { + type Success = $success_type; + fn try_origin(o: O) -> Result { + o.into().and_then(|o| match o { + Origin::$name => Ok($success), + r => Err(O::from(r)), + }) + } + #[cfg(feature = "runtime-benchmarks")] + fn try_successful_origin() -> Result { + Ok(O::from(Origin::$name)) + } + } + }; + ( $name:ident ) => { decl_unit_ensures! { $name : () = () } }; + ( $name:ident: $success_type:ty = $success:expr, $( $rest:tt )* ) => { + decl_unit_ensures! { $name: $success_type = $success } + decl_unit_ensures! { $( $rest )* } + }; + ( $name:ident, $( $rest:tt )* ) => { + decl_unit_ensures! { $name } + decl_unit_ensures! { $( $rest )* } + }; + () => {} + } + decl_unit_ensures!(ReferendumCanceller, ReferendumKiller, WhitelistedCaller, GeneralAdmin); +} diff --git a/runtime/karura/src/governance/referenda.rs b/runtime/karura/src/governance/referenda.rs new file mode 100644 index 0000000000..c28801b43b --- /dev/null +++ b/runtime/karura/src/governance/referenda.rs @@ -0,0 +1,94 @@ +// This file is part of Acala. + +// Copyright (C) 2020-2023 Acala Foundation. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +//! # Gov2 config +//! Includes runtime configs for these substrate pallets: +//! 1. pallet-conviction-voting +//! 2. pallet-whitelist +//! 3. pallet-referenda + +use super::*; +use frame_support::traits::{EitherOf, MapSuccess}; +use frame_system::EnsureRootWithSuccess; +use sp_runtime::traits::Replace; + +parameter_types! { + pub const VoteLockingPeriod: BlockNumber = 1 * DAYS; +} + +impl pallet_conviction_voting::Config for Runtime { + type WeightInfo = (); + type RuntimeEvent = RuntimeEvent; + type Currency = Balances; + type Polls = Referenda; + type MaxTurnout = frame_support::traits::TotalIssuanceOf; + // Maximum number of concurrent votes an account may have + type MaxVotes = ConstU32<20>; + // Minimum period of vote locking + type VoteLockingPeriod = VoteLockingPeriod; +} + +// Origin for general admin or root +pub type GeneralAdminOrRoot = EitherOf, origins::GeneralAdmin>; + +impl custom_origins::Config for Runtime {} + +// The purpose of this pallet is to queue calls to be dispatched as by root later => the Dispatch +// origin corresponds to the Gov2 Whitelist track. +impl pallet_whitelist::Config for Runtime { + type WeightInfo = (); + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type WhitelistOrigin = EitherOf< + EnsureRootWithSuccess>, + MapSuccess< + pallet_collective::EnsureProportionAtLeast, + Replace>, + >, + >; + type DispatchWhitelistedOrigin = EitherOf, WhitelistedCaller>; + type Preimages = Preimage; +} + +pallet_referenda::impl_tracksinfo_get!(TracksInfo, Balance, BlockNumber); + +parameter_types! { + pub const AlarmInterval: BlockNumber = 1; + pub SubmissionDeposit: Balance = 10 * dollar(KAR); + pub const UndecidingTimeout: BlockNumber = 14 * DAYS; +} + +impl pallet_referenda::Config for Runtime { + type WeightInfo = (); + type RuntimeCall = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type Scheduler = Scheduler; + type Currency = Balances; + type SubmitOrigin = frame_system::EnsureSigned; + type CancelOrigin = EitherOf, ReferendumCanceller>; + type KillOrigin = EitherOf, ReferendumKiller>; + type Slash = Treasury; + type Votes = pallet_conviction_voting::VotesOf; + type Tally = pallet_conviction_voting::TallyOf; + type SubmissionDeposit = SubmissionDeposit; + type MaxQueued = ConstU32<100>; + type UndecidingTimeout = UndecidingTimeout; + type AlarmInterval = AlarmInterval; + type Tracks = TracksInfo; + type Preimages = Preimage; +} diff --git a/runtime/karura/src/governance/tracks.rs b/runtime/karura/src/governance/tracks.rs new file mode 100644 index 0000000000..df9807e8eb --- /dev/null +++ b/runtime/karura/src/governance/tracks.rs @@ -0,0 +1,178 @@ +// This file is part of Acala. + +// Copyright (C) 2020-2023 Acala Foundation. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +//! Track configurations for governance. + +use super::*; +use pallet_referenda::Curve; +use sp_std::str::FromStr; + +const fn percent(x: i32) -> sp_runtime::FixedI64 { + sp_runtime::FixedI64::from_rational(x as u128, 100) +} +const fn permill(x: i32) -> sp_runtime::FixedI64 { + sp_runtime::FixedI64::from_rational(x as u128, 1000) +} + +lazy_static::lazy_static! { +static ref TRACKS_DATA: [(u16, pallet_referenda::TrackInfo); 5] = [ + ( + 0, + pallet_referenda::TrackInfo { + // Name of this track. + name: "root", + // A limit for the number of referenda on this track that can be being decided at once. + // For Root origin this should generally be just one. + max_deciding: 5, + // Amount that must be placed on deposit before a decision can be made. + decision_deposit: 20 * 1000 * dollar(KAR), + // Amount of time this must be submitted for before a decision can be made. + prepare_period: 1 * DAYS, + // Amount of time that a decision may take to be approved prior to cancellation. + decision_period: 14 * DAYS, + // Amount of time that the approval criteria must hold before it can be approved. + confirm_period: 1 * DAYS, + // Minimum amount of time that an approved proposal must be in the dispatch queue. + min_enactment_period: 1 * DAYS, + // Minimum aye votes as percentage of overall conviction-weighted votes needed for + // approval as a function of time into decision period. + min_approval: Curve::make_reciprocal(4, 14, percent(80), percent(50), percent(100)), + // Minimum pre-conviction aye-votes ("support") as percentage of overall population that + // is needed for approval as a function of time into decision period. + min_support: Curve::make_linear(14, 14, permill(5), percent(25)), + }, + ), + ( + 1, + pallet_referenda::TrackInfo { + name: "whitelisted_caller", + max_deciding: 100, + decision_deposit: 2 * 1000 * dollar(KAR), + prepare_period: 10 * MINUTES, + decision_period: 14 * DAYS, + confirm_period: 10 * MINUTES, + min_enactment_period: 30 * MINUTES, + min_approval: Curve::make_reciprocal(1, 14, percent(96), percent(50), percent(100)), + min_support: Curve::make_reciprocal(1, 14 * 24, percent(1), percent(0), percent(2)), + }, + ), + ( + 2, + pallet_referenda::TrackInfo { + name: "general_admin", + max_deciding: 10, + decision_deposit: 1000 * dollar(KAR), + prepare_period: 1 * HOURS, + decision_period: 14 * DAYS, + confirm_period: 1 * DAYS, + min_enactment_period: 1 * DAYS, + min_approval: Curve::make_reciprocal(4, 14, percent(80), percent(50), percent(100)), + min_support: Curve::make_reciprocal(7, 14, percent(10), percent(0), percent(50)), + }, + ), + ( + 3, + pallet_referenda::TrackInfo { + name: "referendum_canceller", + max_deciding: 20, + decision_deposit: 2 * 1000 * dollar(KAR), + prepare_period: 1 * HOURS, + decision_period: 14 * DAYS, + confirm_period: 3 * HOURS, + min_enactment_period: 10 * MINUTES, + min_approval: Curve::make_reciprocal(1, 14, percent(96), percent(50), percent(100)), + min_support: Curve::make_reciprocal(1, 14, percent(1), percent(0), percent(10)), + }, + ), + ( + 4, + pallet_referenda::TrackInfo { + name: "referendum_killer", + max_deciding: 100, + decision_deposit: 4 * 1000 * dollar(KAR), + prepare_period: 1 * HOURS, + decision_period: 14 * DAYS, + confirm_period: 3 * HOURS, + min_enactment_period: 10 * MINUTES, + min_approval: Curve::make_reciprocal(1, 14, percent(96), percent(50), percent(100)), + min_support: Curve::make_reciprocal(1, 14, percent(1), percent(0), percent(10)), + }, + ), +]; +} + +pub struct TracksInfo; +impl pallet_referenda::TracksInfo for TracksInfo { + type Id = u16; + type RuntimeOrigin = ::PalletsOrigin; + fn tracks() -> &'static [(Self::Id, pallet_referenda::TrackInfo)] { + &TRACKS_DATA[..] + } + fn track_for(id: &Self::RuntimeOrigin) -> Result { + if let Ok(system_origin) = frame_system::RawOrigin::try_from(id.clone()) { + match system_origin { + frame_system::RawOrigin::Root => { + if let Some((track_id, _)) = Self::tracks().into_iter().find(|(_, track)| track.name == "root") { + Ok(*track_id) + } else { + Err(()) + } + } + _ => Err(()), + } + } else if let Ok(custom_origin) = custom_origins::Origin::try_from(id.clone()) { + if let Some((track_id, _)) = Self::tracks().into_iter().find(|(_, track)| { + if let Ok(track_custom_origin) = custom_origins::Origin::from_str(track.name) { + track_custom_origin == custom_origin + } else { + false + } + }) { + Ok(*track_id) + } else { + Err(()) + } + } else { + Err(()) + } + } +} + +#[test] +/// To ensure voters are always locked into their vote +fn vote_locking_always_longer_than_enactment_period() { + for (_, track) in (&TRACKS_DATA).iter() { + assert!( + ::VoteLockingPeriod::get() >= track.min_enactment_period, + "Track {} has enactment period {} < vote locking period {}", + track.name, + track.min_enactment_period, + ::VoteLockingPeriod::get(), + ); + } +} + +#[test] +fn all_tracks_have_origins() { + for (_, track) in (&TRACKS_DATA).iter() { + // check name.into() is successful either converts into "root" or custom origin + let track_is_root = track.name == "root"; + let track_has_custom_origin = custom_origins::Origin::from_str(track.name).is_ok(); + assert!(track_is_root || track_has_custom_origin); + } +} diff --git a/runtime/karura/src/lib.rs b/runtime/karura/src/lib.rs index 95c612e700..769001c133 100644 --- a/runtime/karura/src/lib.rs +++ b/runtime/karura/src/lib.rs @@ -121,9 +121,11 @@ pub use nutsfinance_stable_asset; mod authority; mod benchmarking; pub mod constants; +pub mod governance; /// Weights for pallets used in the runtime. mod weights; pub mod xcm_config; +use governance::councils::*; /// This runtime version. #[sp_version::runtime_version] @@ -401,129 +403,6 @@ impl pallet_sudo::Config for Runtime { type WeightInfo = (); } -parameter_types! { - pub const GeneralCouncilMotionDuration: BlockNumber = 3 * DAYS; - pub const CouncilDefaultMaxProposals: u32 = 20; - pub const CouncilDefaultMaxMembers: u32 = 30; - pub MaxProposalWeight: Weight = Perbill::from_percent(50) * RuntimeBlockWeights::get().max_block; -} - -impl pallet_collective::Config for Runtime { - type RuntimeOrigin = RuntimeOrigin; - type Proposal = RuntimeCall; - type RuntimeEvent = RuntimeEvent; - type MotionDuration = GeneralCouncilMotionDuration; - type MaxProposals = CouncilDefaultMaxProposals; - type MaxMembers = CouncilDefaultMaxMembers; - type DefaultVote = pallet_collective::PrimeDefaultVote; - type SetMembersOrigin = EnsureRoot; - type WeightInfo = (); - type MaxProposalWeight = MaxProposalWeight; -} - -impl pallet_membership::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type AddOrigin = EnsureRootOrThreeFourthsGeneralCouncil; - type RemoveOrigin = EnsureRootOrThreeFourthsGeneralCouncil; - type SwapOrigin = EnsureRootOrThreeFourthsGeneralCouncil; - type ResetOrigin = EnsureRootOrThreeFourthsGeneralCouncil; - type PrimeOrigin = EnsureRootOrThreeFourthsGeneralCouncil; - type MembershipInitialized = GeneralCouncil; - type MembershipChanged = GeneralCouncil; - type MaxMembers = CouncilDefaultMaxMembers; - type WeightInfo = (); -} - -parameter_types! { - pub const FinancialCouncilMotionDuration: BlockNumber = 3 * DAYS; -} - -impl pallet_collective::Config for Runtime { - type RuntimeOrigin = RuntimeOrigin; - type Proposal = RuntimeCall; - type RuntimeEvent = RuntimeEvent; - type MotionDuration = FinancialCouncilMotionDuration; - type MaxProposals = CouncilDefaultMaxProposals; - type MaxMembers = CouncilDefaultMaxMembers; - type DefaultVote = pallet_collective::PrimeDefaultVote; - type SetMembersOrigin = EnsureRoot; - type WeightInfo = (); - type MaxProposalWeight = MaxProposalWeight; -} - -impl pallet_membership::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type AddOrigin = EnsureRootOrTwoThirdsGeneralCouncil; - type RemoveOrigin = EnsureRootOrTwoThirdsGeneralCouncil; - type SwapOrigin = EnsureRootOrTwoThirdsGeneralCouncil; - type ResetOrigin = EnsureRootOrTwoThirdsGeneralCouncil; - type PrimeOrigin = EnsureRootOrTwoThirdsGeneralCouncil; - type MembershipInitialized = FinancialCouncil; - type MembershipChanged = FinancialCouncil; - type MaxMembers = CouncilDefaultMaxMembers; - type WeightInfo = (); -} - -parameter_types! { - pub const HomaCouncilMotionDuration: BlockNumber = 3 * DAYS; -} - -impl pallet_collective::Config for Runtime { - type RuntimeOrigin = RuntimeOrigin; - type Proposal = RuntimeCall; - type RuntimeEvent = RuntimeEvent; - type MotionDuration = HomaCouncilMotionDuration; - type MaxProposals = CouncilDefaultMaxProposals; - type MaxMembers = CouncilDefaultMaxMembers; - type DefaultVote = pallet_collective::PrimeDefaultVote; - type SetMembersOrigin = EnsureRoot; - type WeightInfo = (); - type MaxProposalWeight = MaxProposalWeight; -} - -impl pallet_membership::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type AddOrigin = EnsureRootOrTwoThirdsGeneralCouncil; - type RemoveOrigin = EnsureRootOrTwoThirdsGeneralCouncil; - type SwapOrigin = EnsureRootOrTwoThirdsGeneralCouncil; - type ResetOrigin = EnsureRootOrTwoThirdsGeneralCouncil; - type PrimeOrigin = EnsureRootOrTwoThirdsGeneralCouncil; - type MembershipInitialized = HomaCouncil; - type MembershipChanged = HomaCouncil; - type MaxMembers = CouncilDefaultMaxMembers; - type WeightInfo = (); -} - -parameter_types! { - pub const TechnicalCommitteeMotionDuration: BlockNumber = 3 * DAYS; -} - -impl pallet_collective::Config for Runtime { - type RuntimeOrigin = RuntimeOrigin; - type Proposal = RuntimeCall; - type RuntimeEvent = RuntimeEvent; - type MotionDuration = TechnicalCommitteeMotionDuration; - type MaxProposals = CouncilDefaultMaxProposals; - type MaxMembers = CouncilDefaultMaxMembers; - type DefaultVote = pallet_collective::PrimeDefaultVote; - type SetMembersOrigin = EnsureRoot; - type WeightInfo = (); - type MaxProposalWeight = MaxProposalWeight; -} - -impl pallet_membership::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type AddOrigin = EnsureRootOrTwoThirdsGeneralCouncil; - type RemoveOrigin = EnsureRootOrTwoThirdsGeneralCouncil; - type SwapOrigin = EnsureRootOrTwoThirdsGeneralCouncil; - type ResetOrigin = EnsureRootOrTwoThirdsGeneralCouncil; - type PrimeOrigin = EnsureRootOrTwoThirdsGeneralCouncil; - type MembershipInitialized = TechnicalCommittee; - type MembershipChanged = TechnicalCommittee; - type MaxMembers = CouncilDefaultMaxMembers; - type WeightInfo = (); -} - impl pallet_membership::Config for Runtime { type RuntimeEvent = RuntimeEvent; type AddOrigin = EnsureRootOrTwoThirdsGeneralCouncil; @@ -653,60 +532,6 @@ impl pallet_tips::Config for Runtime { type WeightInfo = (); } -parameter_types! { - pub const LaunchPeriod: BlockNumber = 5 * DAYS; - pub const VotingPeriod: BlockNumber = 5 * DAYS; - pub const FastTrackVotingPeriod: BlockNumber = 3 * HOURS; - pub MinimumDeposit: Balance = 100 * dollar(KAR); - pub const EnactmentPeriod: BlockNumber = 2 * DAYS; - pub const VoteLockingPeriod: BlockNumber = 7 * DAYS; - pub const CooloffPeriod: BlockNumber = 7 * DAYS; -} - -impl pallet_democracy::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type Currency = Balances; - type EnactmentPeriod = EnactmentPeriod; - type LaunchPeriod = LaunchPeriod; - type VotingPeriod = VotingPeriod; - type VoteLockingPeriod = VoteLockingPeriod; - type MinimumDeposit = MinimumDeposit; - /// A straight majority of the council can decide what their next motion is. - type ExternalOrigin = EnsureRootOrHalfGeneralCouncil; - /// A majority can have the next scheduled referendum be a straight majority-carries vote. - type ExternalMajorityOrigin = EnsureRootOrHalfGeneralCouncil; - /// A unanimous council can have the next scheduled referendum be a straight default-carries - /// (NTB) vote. - type ExternalDefaultOrigin = EnsureRootOrAllGeneralCouncil; - /// Two thirds of the technical committee can have an ExternalMajority/ExternalDefault vote - /// be tabled immediately and with a shorter voting/enactment period. - type FastTrackOrigin = EnsureRootOrTwoThirdsTechnicalCommittee; - type InstantOrigin = EnsureRootOrAllTechnicalCommittee; - type InstantAllowed = ConstBool; - type FastTrackVotingPeriod = FastTrackVotingPeriod; - // To cancel a proposal which has been passed, 2/3 of the council must agree to it. - type CancellationOrigin = EnsureRootOrTwoThirdsGeneralCouncil; - type BlacklistOrigin = EnsureRoot; - // To cancel a proposal before it has been passed, the technical committee must be unanimous or - // Root must agree. - type CancelProposalOrigin = EnsureRootOrAllTechnicalCommittee; - // 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 Slash = Treasury; - type Scheduler = Scheduler; - type PalletsOrigin = OriginCaller; - type MaxVotes = ConstU32<100>; - //TODO: might need to weight for Karura - type WeightInfo = pallet_democracy::weights::SubstrateWeight; - type MaxProposals = ConstU32<100>; - type Preimages = Preimage; - type MaxDeposits = ConstU32<100>; - type MaxBlacklisted = ConstU32<100>; - type SubmitOrigin = EnsureSigned; -} - impl orml_auction::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Balance = Balance; @@ -1779,6 +1604,10 @@ construct_runtime!( TechnicalCommittee: pallet_collective:: = 67, TechnicalCommitteeMembership: pallet_membership:: = 68, Democracy: pallet_democracy = 69, + ConvictionVoting: pallet_conviction_voting = 160, + Referenda: pallet_referenda = 161, + Origins: governance::custom_origins::{Origin} = 162, + Whitelist: pallet_whitelist = 163, // Oracle // diff --git a/runtime/mandala/Cargo.toml b/runtime/mandala/Cargo.toml index f938a4aa68..19e66d62b2 100644 --- a/runtime/mandala/Cargo.toml +++ b/runtime/mandala/Cargo.toml @@ -13,6 +13,9 @@ scale-info = { version = "2.9.0", default-features = false, features = ["derive" serde_json = { version = "1.0.85", default-features = false, features = ["alloc"] } hex = { version = "0.4", default-features = false } hex-literal = { version = "0.4.1" } +strum = { version = "0.24", default-features = false, features = [ "derive" ] } +strum_macros = "0.24" +lazy_static = "1.4.0" # substrate frame-executive = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } @@ -57,6 +60,9 @@ sp-staking = { git = "https://github.com/paritytech/polkadot-sdk", branch = "rel sp-std = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } sp-transaction-pool = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } sp-version = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } +pallet-conviction-voting = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } +pallet-referenda = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } +pallet-whitelist = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } # cumulus cumulus-pallet-aura-ext = { git = "https://github.com/paritytech/polkadot-sdk", branch = "release-polkadot-v1.1.0", default-features = false } @@ -161,6 +167,7 @@ std = [ "hex/std", "scale-info/std", "serde_json/std", + "strum/std", "frame-benchmarking/std", "frame-executive/std", @@ -208,6 +215,9 @@ std = [ "sp-transaction-pool/std", "sp-version/std", "substrate-wasm-builder", + "pallet-conviction-voting/std", + "pallet-referenda/std", + "pallet-whitelist/std", "cumulus-pallet-aura-ext/std", "cumulus-pallet-dmp-queue/std", @@ -306,6 +316,9 @@ runtime-benchmarks = [ "pallet-utility/runtime-benchmarks", "sp-runtime/runtime-benchmarks", "sp-staking/runtime-benchmarks", + "pallet-conviction-voting/runtime-benchmarks", + "pallet-referenda/runtime-benchmarks", + "pallet-whitelist/runtime-benchmarks", "cumulus-pallet-parachain-system/runtime-benchmarks", "cumulus-pallet-xcmp-queue/runtime-benchmarks", @@ -378,6 +391,9 @@ try-runtime = [ "pallet-transaction-payment/try-runtime", "pallet-treasury/try-runtime", "pallet-utility/try-runtime", + "pallet-conviction-voting/try-runtime", + "pallet-referenda/try-runtime", + "pallet-whitelist/try-runtime", "cumulus-pallet-aura-ext/try-runtime", "cumulus-pallet-dmp-queue/try-runtime", diff --git a/runtime/mandala/src/governance/councils.rs b/runtime/mandala/src/governance/councils.rs new file mode 100644 index 0000000000..083513cced --- /dev/null +++ b/runtime/mandala/src/governance/councils.rs @@ -0,0 +1,144 @@ +// This file is part of Acala. + +// Copyright (C) 2020-2023 Acala Foundation. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +//! Councils for Gov1 and Gov2 + +use super::*; + +parameter_types! { + pub const GeneralCouncilMotionDuration: BlockNumber = 7 * DAYS; + pub const CouncilDefaultMaxProposals: u32 = 100; + pub const CouncilDefaultMaxMembers: u32 = 100; + pub MaxProposalWeight: Weight = Perbill::from_percent(50) * RuntimeBlockWeights::get().max_block; +} + +impl pallet_collective::Config for Runtime { + type RuntimeOrigin = RuntimeOrigin; + type Proposal = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type MotionDuration = GeneralCouncilMotionDuration; + type MaxProposals = CouncilDefaultMaxProposals; + type MaxMembers = CouncilDefaultMaxMembers; + type DefaultVote = pallet_collective::PrimeDefaultVote; + type SetMembersOrigin = EnsureRoot; + type WeightInfo = (); + type MaxProposalWeight = MaxProposalWeight; +} + +impl pallet_membership::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type AddOrigin = EnsureRootOrThreeFourthsGeneralCouncil; + type RemoveOrigin = EnsureRootOrThreeFourthsGeneralCouncil; + type SwapOrigin = EnsureRootOrThreeFourthsGeneralCouncil; + type ResetOrigin = EnsureRootOrThreeFourthsGeneralCouncil; + type PrimeOrigin = EnsureRootOrThreeFourthsGeneralCouncil; + type MembershipInitialized = GeneralCouncil; + type MembershipChanged = GeneralCouncil; + type MaxMembers = CouncilDefaultMaxMembers; + type WeightInfo = (); +} + +parameter_types! { + pub const FinancialCouncilMotionDuration: BlockNumber = 7 * DAYS; +} + +impl pallet_collective::Config for Runtime { + type RuntimeOrigin = RuntimeOrigin; + type Proposal = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type MotionDuration = FinancialCouncilMotionDuration; + type MaxProposals = CouncilDefaultMaxProposals; + type MaxMembers = CouncilDefaultMaxMembers; + type DefaultVote = pallet_collective::PrimeDefaultVote; + type SetMembersOrigin = EnsureRoot; + type WeightInfo = (); + type MaxProposalWeight = MaxProposalWeight; +} + +impl pallet_membership::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type AddOrigin = EnsureRootOrTwoThirdsGeneralCouncil; + type RemoveOrigin = EnsureRootOrTwoThirdsGeneralCouncil; + type SwapOrigin = EnsureRootOrTwoThirdsGeneralCouncil; + type ResetOrigin = EnsureRootOrTwoThirdsGeneralCouncil; + type PrimeOrigin = EnsureRootOrTwoThirdsGeneralCouncil; + type MembershipInitialized = FinancialCouncil; + type MembershipChanged = FinancialCouncil; + type MaxMembers = CouncilDefaultMaxMembers; + type WeightInfo = (); +} + +parameter_types! { + pub const HomaCouncilMotionDuration: BlockNumber = 7 * DAYS; +} + +impl pallet_collective::Config for Runtime { + type RuntimeOrigin = RuntimeOrigin; + type Proposal = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type MotionDuration = HomaCouncilMotionDuration; + type MaxProposals = CouncilDefaultMaxProposals; + type MaxMembers = CouncilDefaultMaxMembers; + type DefaultVote = pallet_collective::PrimeDefaultVote; + type SetMembersOrigin = EnsureRoot; + type WeightInfo = (); + type MaxProposalWeight = MaxProposalWeight; +} + +impl pallet_membership::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type AddOrigin = EnsureRootOrTwoThirdsGeneralCouncil; + type RemoveOrigin = EnsureRootOrTwoThirdsGeneralCouncil; + type SwapOrigin = EnsureRootOrTwoThirdsGeneralCouncil; + type ResetOrigin = EnsureRootOrTwoThirdsGeneralCouncil; + type PrimeOrigin = EnsureRootOrTwoThirdsGeneralCouncil; + type MembershipInitialized = HomaCouncil; + type MembershipChanged = HomaCouncil; + type MaxMembers = CouncilDefaultMaxMembers; + type WeightInfo = (); +} + +parameter_types! { + pub const TechnicalCommitteeMotionDuration: BlockNumber = 7 * DAYS; +} + +impl pallet_collective::Config for Runtime { + type RuntimeOrigin = RuntimeOrigin; + type Proposal = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type MotionDuration = TechnicalCommitteeMotionDuration; + type MaxProposals = CouncilDefaultMaxProposals; + type MaxMembers = CouncilDefaultMaxMembers; + type DefaultVote = pallet_collective::PrimeDefaultVote; + type SetMembersOrigin = EnsureRoot; + type WeightInfo = (); + type MaxProposalWeight = MaxProposalWeight; +} + +impl pallet_membership::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type AddOrigin = EnsureRootOrTwoThirdsGeneralCouncil; + type RemoveOrigin = EnsureRootOrTwoThirdsGeneralCouncil; + type SwapOrigin = EnsureRootOrTwoThirdsGeneralCouncil; + type ResetOrigin = EnsureRootOrTwoThirdsGeneralCouncil; + type PrimeOrigin = EnsureRootOrTwoThirdsGeneralCouncil; + type MembershipInitialized = TechnicalCommittee; + type MembershipChanged = TechnicalCommittee; + type MaxMembers = CouncilDefaultMaxMembers; + type WeightInfo = (); +} diff --git a/runtime/mandala/src/governance/democracy.rs b/runtime/mandala/src/governance/democracy.rs new file mode 100644 index 0000000000..6373a0300e --- /dev/null +++ b/runtime/mandala/src/governance/democracy.rs @@ -0,0 +1,74 @@ +// This file is part of Acala. + +// Copyright (C) 2020-2023 Acala Foundation. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +//! Democracy config for Gov1 + +use crate::*; + +parameter_types! { + pub const LaunchPeriod: BlockNumber = 2 * HOURS; + pub const VotingPeriod: BlockNumber = HOURS; + pub const FastTrackVotingPeriod: BlockNumber = HOURS; + pub MinimumDeposit: Balance = 100 * cent(ACA); + pub const EnactmentPeriod: BlockNumber = MINUTES; + pub const CooloffPeriod: BlockNumber = MINUTES; +} + +impl pallet_democracy::Config for Runtime { + type RuntimeEvent = RuntimeEvent; + type Currency = Balances; + type EnactmentPeriod = EnactmentPeriod; + type LaunchPeriod = LaunchPeriod; + type VotingPeriod = VotingPeriod; + type VoteLockingPeriod = EnactmentPeriod; // Same as EnactmentPeriod + type MinimumDeposit = MinimumDeposit; + /// A straight majority of the council can decide what their next motion is. + type ExternalOrigin = EnsureRootOrHalfGeneralCouncil; + /// A majority can have the next scheduled referendum be a straight majority-carries vote. + type ExternalMajorityOrigin = EnsureRootOrHalfGeneralCouncil; + /// A unanimous council can have the next scheduled referendum be a straight default-carries + /// (NTB) vote. + type ExternalDefaultOrigin = EnsureRootOrAllGeneralCouncil; + /// Two thirds of the technical committee can have an ExternalMajority/ExternalDefault vote + /// be tabled immediately and with a shorter voting/enactment period. + type FastTrackOrigin = EnsureRootOrTwoThirdsTechnicalCommittee; + type InstantOrigin = EnsureRootOrAllTechnicalCommittee; + type InstantAllowed = ConstBool; + type FastTrackVotingPeriod = FastTrackVotingPeriod; + // To cancel a proposal which has been passed, 2/3 of the council must agree to it. + type CancellationOrigin = EnsureRootOrTwoThirdsGeneralCouncil; + type BlacklistOrigin = EnsureRoot; + // To cancel a proposal before it has been passed, the technical committee must be unanimous or + // Root must agree. + type CancelProposalOrigin = EnsureRootOrAllTechnicalCommittee; + // 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 Slash = Treasury; + type Scheduler = Scheduler; + type PalletsOrigin = OriginCaller; + type MaxVotes = ConstU32<100>; + //TODO: might need to weight for Mandala + type WeightInfo = pallet_democracy::weights::SubstrateWeight; + type MaxProposals = CouncilDefaultMaxProposals; + type Preimages = Preimage; + type MaxDeposits = ConstU32<100>; + type MaxBlacklisted = ConstU32<100>; + type SubmitOrigin = EnsureSigned; +} diff --git a/runtime/mandala/src/governance/mod.rs b/runtime/mandala/src/governance/mod.rs new file mode 100644 index 0000000000..9d8000ef01 --- /dev/null +++ b/runtime/mandala/src/governance/mod.rs @@ -0,0 +1,30 @@ +// This file is part of Acala. + +// Copyright (C) 2020-2023 Acala Foundation. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +//! Governance configurations + +pub mod councils; +mod democracy; +pub mod referenda; + +use super::*; + +mod origins; +pub use origins::{custom_origins, GeneralAdmin, ReferendumCanceller, ReferendumKiller, WhitelistedCaller}; +mod tracks; +pub use tracks::TracksInfo; diff --git a/runtime/mandala/src/governance/origins.rs b/runtime/mandala/src/governance/origins.rs new file mode 100644 index 0000000000..7f7d7237a0 --- /dev/null +++ b/runtime/mandala/src/governance/origins.rs @@ -0,0 +1,79 @@ +// This file is part of Acala. + +// Copyright (C) 2020-2023 Acala Foundation. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +//! Custom origins for governance interventions. + +pub use custom_origins::*; + +#[frame_support::pallet] +pub mod custom_origins { + use frame_support::pallet_prelude::*; + use strum_macros::EnumString; + + #[pallet::config] + pub trait Config: frame_system::Config {} + + #[pallet::pallet] + pub struct Pallet(_); + + #[derive(PartialEq, Eq, Clone, MaxEncodedLen, Encode, Decode, TypeInfo, RuntimeDebug, EnumString)] + #[strum(serialize_all = "snake_case")] + #[pallet::origin] + pub enum Origin { + /// Origin able to dispatch a whitelisted call. + WhitelistedCaller, + /// General admin + GeneralAdmin, + /// Origin able to cancel referenda. + ReferendumCanceller, + /// Origin able to kill referenda. + ReferendumKiller, + } + + macro_rules! decl_unit_ensures { + ( $name:ident: $success_type:ty = $success:expr ) => { + pub struct $name; + impl> + From> + EnsureOrigin for $name + { + type Success = $success_type; + fn try_origin(o: O) -> Result { + o.into().and_then(|o| match o { + Origin::$name => Ok($success), + r => Err(O::from(r)), + }) + } + #[cfg(feature = "runtime-benchmarks")] + fn try_successful_origin() -> Result { + Ok(O::from(Origin::$name)) + } + } + }; + ( $name:ident ) => { decl_unit_ensures! { $name : () = () } }; + ( $name:ident: $success_type:ty = $success:expr, $( $rest:tt )* ) => { + decl_unit_ensures! { $name: $success_type = $success } + decl_unit_ensures! { $( $rest )* } + }; + ( $name:ident, $( $rest:tt )* ) => { + decl_unit_ensures! { $name } + decl_unit_ensures! { $( $rest )* } + }; + () => {} + } + decl_unit_ensures!(ReferendumCanceller, ReferendumKiller, WhitelistedCaller, GeneralAdmin); +} diff --git a/runtime/mandala/src/governance/referenda.rs b/runtime/mandala/src/governance/referenda.rs new file mode 100644 index 0000000000..cd46634046 --- /dev/null +++ b/runtime/mandala/src/governance/referenda.rs @@ -0,0 +1,94 @@ +// This file is part of Acala. + +// Copyright (C) 2020-2023 Acala Foundation. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +//! # Gov2 config +//! Includes runtime configs for these substrate pallets: +//! 1. pallet-conviction-voting +//! 2. pallet-whitelist +//! 3. pallet-referenda + +use super::*; +use frame_support::traits::{EitherOf, MapSuccess}; +use frame_system::EnsureRootWithSuccess; +use sp_runtime::traits::Replace; + +parameter_types! { + pub const VoteLockingPeriod: BlockNumber = 1 * DAYS; +} + +impl pallet_conviction_voting::Config for Runtime { + type WeightInfo = (); + type RuntimeEvent = RuntimeEvent; + type Currency = Balances; + type Polls = Referenda; + type MaxTurnout = frame_support::traits::TotalIssuanceOf; + // Maximum number of concurrent votes an account may have + type MaxVotes = ConstU32<20>; + // Minimum period of vote locking + type VoteLockingPeriod = VoteLockingPeriod; +} + +// Origin for general admin or root +pub type GeneralAdminOrRoot = EitherOf, origins::GeneralAdmin>; + +impl custom_origins::Config for Runtime {} + +// The purpose of this pallet is to queue calls to be dispatched as by root later => the Dispatch +// origin corresponds to the Gov2 Whitelist track. +impl pallet_whitelist::Config for Runtime { + type WeightInfo = (); + type RuntimeEvent = RuntimeEvent; + type RuntimeCall = RuntimeCall; + type WhitelistOrigin = EitherOf< + EnsureRootWithSuccess>, + MapSuccess< + pallet_collective::EnsureProportionAtLeast, + Replace>, + >, + >; + type DispatchWhitelistedOrigin = EitherOf, WhitelistedCaller>; + type Preimages = Preimage; +} + +pallet_referenda::impl_tracksinfo_get!(TracksInfo, Balance, BlockNumber); + +parameter_types! { + pub const AlarmInterval: BlockNumber = 1; + pub SubmissionDeposit: Balance = 10 * dollar(ACA); + pub const UndecidingTimeout: BlockNumber = 14 * DAYS; +} + +impl pallet_referenda::Config for Runtime { + type WeightInfo = (); + type RuntimeCall = RuntimeCall; + type RuntimeEvent = RuntimeEvent; + type Scheduler = Scheduler; + type Currency = Balances; + type SubmitOrigin = frame_system::EnsureSigned; + type CancelOrigin = EitherOf, ReferendumCanceller>; + type KillOrigin = EitherOf, ReferendumKiller>; + type Slash = Treasury; + type Votes = pallet_conviction_voting::VotesOf; + type Tally = pallet_conviction_voting::TallyOf; + type SubmissionDeposit = SubmissionDeposit; + type MaxQueued = ConstU32<100>; + type UndecidingTimeout = UndecidingTimeout; + type AlarmInterval = AlarmInterval; + type Tracks = TracksInfo; + type Preimages = Preimage; +} diff --git a/runtime/mandala/src/governance/tracks.rs b/runtime/mandala/src/governance/tracks.rs new file mode 100644 index 0000000000..ef053401e9 --- /dev/null +++ b/runtime/mandala/src/governance/tracks.rs @@ -0,0 +1,178 @@ +// This file is part of Acala. + +// Copyright (C) 2020-2023 Acala Foundation. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 + +// This program 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. + +// This program 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 this program. If not, see . + +//! Track configurations for governance. + +use super::*; +use pallet_referenda::Curve; +use sp_std::str::FromStr; + +const fn percent(x: i32) -> sp_runtime::FixedI64 { + sp_runtime::FixedI64::from_rational(x as u128, 100) +} +const fn permill(x: i32) -> sp_runtime::FixedI64 { + sp_runtime::FixedI64::from_rational(x as u128, 1000) +} + +lazy_static::lazy_static! { +static ref TRACKS_DATA: [(u16, pallet_referenda::TrackInfo); 5] = [ + ( + 0, + pallet_referenda::TrackInfo { + // Name of this track. + name: "root", + // A limit for the number of referenda on this track that can be being decided at once. + // For Root origin this should generally be just one. + max_deciding: 5, + // Amount that must be placed on deposit before a decision can be made. + decision_deposit: 20 * 1000 * dollar(ACA), + // Amount of time this must be submitted for before a decision can be made. + prepare_period: 1 * DAYS, + // Amount of time that a decision may take to be approved prior to cancellation. + decision_period: 14 * DAYS, + // Amount of time that the approval criteria must hold before it can be approved. + confirm_period: 1 * DAYS, + // Minimum amount of time that an approved proposal must be in the dispatch queue. + min_enactment_period: 1 * DAYS, + // Minimum aye votes as percentage of overall conviction-weighted votes needed for + // approval as a function of time into decision period. + min_approval: Curve::make_reciprocal(4, 14, percent(80), percent(50), percent(100)), + // Minimum pre-conviction aye-votes ("support") as percentage of overall population that + // is needed for approval as a function of time into decision period. + min_support: Curve::make_linear(14, 14, permill(5), percent(25)), + }, + ), + ( + 1, + pallet_referenda::TrackInfo { + name: "whitelisted_caller", + max_deciding: 100, + decision_deposit: 2 * 1000 * dollar(ACA), + prepare_period: 10 * MINUTES, + decision_period: 14 * DAYS, + confirm_period: 10 * MINUTES, + min_enactment_period: 30 * MINUTES, + min_approval: Curve::make_reciprocal(1, 14, percent(96), percent(50), percent(100)), + min_support: Curve::make_reciprocal(1, 14 * 24, percent(1), percent(0), percent(2)), + }, + ), + ( + 2, + pallet_referenda::TrackInfo { + name: "general_admin", + max_deciding: 10, + decision_deposit: 1000 * dollar(ACA), + prepare_period: 1 * HOURS, + decision_period: 14 * DAYS, + confirm_period: 1 * DAYS, + min_enactment_period: 1 * DAYS, + min_approval: Curve::make_reciprocal(4, 14, percent(80), percent(50), percent(100)), + min_support: Curve::make_reciprocal(7, 14, percent(10), percent(0), percent(50)), + }, + ), + ( + 3, + pallet_referenda::TrackInfo { + name: "referendum_canceller", + max_deciding: 20, + decision_deposit: 2 * 1000 * dollar(ACA), + prepare_period: 1 * HOURS, + decision_period: 14 * DAYS, + confirm_period: 3 * HOURS, + min_enactment_period: 10 * MINUTES, + min_approval: Curve::make_reciprocal(1, 14, percent(96), percent(50), percent(100)), + min_support: Curve::make_reciprocal(1, 14, percent(1), percent(0), percent(10)), + }, + ), + ( + 4, + pallet_referenda::TrackInfo { + name: "referendum_killer", + max_deciding: 100, + decision_deposit: 4 * 1000 * dollar(ACA), + prepare_period: 1 * HOURS, + decision_period: 14 * DAYS, + confirm_period: 3 * HOURS, + min_enactment_period: 10 * MINUTES, + min_approval: Curve::make_reciprocal(1, 14, percent(96), percent(50), percent(100)), + min_support: Curve::make_reciprocal(1, 14, percent(1), percent(0), percent(10)), + }, + ), +]; +} + +pub struct TracksInfo; +impl pallet_referenda::TracksInfo for TracksInfo { + type Id = u16; + type RuntimeOrigin = ::PalletsOrigin; + fn tracks() -> &'static [(Self::Id, pallet_referenda::TrackInfo)] { + &TRACKS_DATA[..] + } + fn track_for(id: &Self::RuntimeOrigin) -> Result { + if let Ok(system_origin) = frame_system::RawOrigin::try_from(id.clone()) { + match system_origin { + frame_system::RawOrigin::Root => { + if let Some((track_id, _)) = Self::tracks().into_iter().find(|(_, track)| track.name == "root") { + Ok(*track_id) + } else { + Err(()) + } + } + _ => Err(()), + } + } else if let Ok(custom_origin) = custom_origins::Origin::try_from(id.clone()) { + if let Some((track_id, _)) = Self::tracks().into_iter().find(|(_, track)| { + if let Ok(track_custom_origin) = custom_origins::Origin::from_str(track.name) { + track_custom_origin == custom_origin + } else { + false + } + }) { + Ok(*track_id) + } else { + Err(()) + } + } else { + Err(()) + } + } +} + +#[test] +/// To ensure voters are always locked into their vote +fn vote_locking_always_longer_than_enactment_period() { + for (_, track) in (&TRACKS_DATA).iter() { + assert!( + ::VoteLockingPeriod::get() >= track.min_enactment_period, + "Track {} has enactment period {} < vote locking period {}", + track.name, + track.min_enactment_period, + ::VoteLockingPeriod::get(), + ); + } +} + +#[test] +fn all_tracks_have_origins() { + for (_, track) in (&TRACKS_DATA).iter() { + // check name.into() is successful either converts into "root" or custom origin + let track_is_root = track.name == "root"; + let track_has_custom_origin = custom_origins::Origin::from_str(track.name).is_ok(); + assert!(track_is_root || track_has_custom_origin); + } +} diff --git a/runtime/mandala/src/lib.rs b/runtime/mandala/src/lib.rs index a4d53254fb..d45ae2a340 100644 --- a/runtime/mandala/src/lib.rs +++ b/runtime/mandala/src/lib.rs @@ -125,9 +125,11 @@ pub use nutsfinance_stable_asset; mod authority; mod benchmarking; pub mod constants; +pub mod governance; /// Weights for pallets used in the runtime. mod weights; pub mod xcm_config; +use governance::councils::*; /// This runtime version. #[sp_version::runtime_version] @@ -374,129 +376,6 @@ impl pallet_sudo::Config for Runtime { type WeightInfo = (); } -parameter_types! { - pub const GeneralCouncilMotionDuration: BlockNumber = 7 * DAYS; - pub const CouncilDefaultMaxProposals: u32 = 100; - pub const CouncilDefaultMaxMembers: u32 = 100; - pub MaxProposalWeight: Weight = Perbill::from_percent(50) * RuntimeBlockWeights::get().max_block; -} - -impl pallet_collective::Config for Runtime { - type RuntimeOrigin = RuntimeOrigin; - type Proposal = RuntimeCall; - type RuntimeEvent = RuntimeEvent; - type MotionDuration = GeneralCouncilMotionDuration; - type MaxProposals = CouncilDefaultMaxProposals; - type MaxMembers = CouncilDefaultMaxMembers; - type DefaultVote = pallet_collective::PrimeDefaultVote; - type SetMembersOrigin = EnsureRoot; - type WeightInfo = (); - type MaxProposalWeight = MaxProposalWeight; -} - -impl pallet_membership::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type AddOrigin = EnsureRootOrThreeFourthsGeneralCouncil; - type RemoveOrigin = EnsureRootOrThreeFourthsGeneralCouncil; - type SwapOrigin = EnsureRootOrThreeFourthsGeneralCouncil; - type ResetOrigin = EnsureRootOrThreeFourthsGeneralCouncil; - type PrimeOrigin = EnsureRootOrThreeFourthsGeneralCouncil; - type MembershipInitialized = GeneralCouncil; - type MembershipChanged = GeneralCouncil; - type MaxMembers = CouncilDefaultMaxMembers; - type WeightInfo = (); -} - -parameter_types! { - pub const FinancialCouncilMotionDuration: BlockNumber = 7 * DAYS; -} - -impl pallet_collective::Config for Runtime { - type RuntimeOrigin = RuntimeOrigin; - type Proposal = RuntimeCall; - type RuntimeEvent = RuntimeEvent; - type MotionDuration = FinancialCouncilMotionDuration; - type MaxProposals = CouncilDefaultMaxProposals; - type MaxMembers = CouncilDefaultMaxMembers; - type DefaultVote = pallet_collective::PrimeDefaultVote; - type SetMembersOrigin = EnsureRoot; - type WeightInfo = (); - type MaxProposalWeight = MaxProposalWeight; -} - -impl pallet_membership::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type AddOrigin = EnsureRootOrTwoThirdsGeneralCouncil; - type RemoveOrigin = EnsureRootOrTwoThirdsGeneralCouncil; - type SwapOrigin = EnsureRootOrTwoThirdsGeneralCouncil; - type ResetOrigin = EnsureRootOrTwoThirdsGeneralCouncil; - type PrimeOrigin = EnsureRootOrTwoThirdsGeneralCouncil; - type MembershipInitialized = FinancialCouncil; - type MembershipChanged = FinancialCouncil; - type MaxMembers = CouncilDefaultMaxMembers; - type WeightInfo = (); -} - -parameter_types! { - pub const HomaCouncilMotionDuration: BlockNumber = 7 * DAYS; -} - -impl pallet_collective::Config for Runtime { - type RuntimeOrigin = RuntimeOrigin; - type Proposal = RuntimeCall; - type RuntimeEvent = RuntimeEvent; - type MotionDuration = HomaCouncilMotionDuration; - type MaxProposals = CouncilDefaultMaxProposals; - type MaxMembers = CouncilDefaultMaxMembers; - type DefaultVote = pallet_collective::PrimeDefaultVote; - type SetMembersOrigin = EnsureRoot; - type WeightInfo = (); - type MaxProposalWeight = MaxProposalWeight; -} - -impl pallet_membership::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type AddOrigin = EnsureRootOrTwoThirdsGeneralCouncil; - type RemoveOrigin = EnsureRootOrTwoThirdsGeneralCouncil; - type SwapOrigin = EnsureRootOrTwoThirdsGeneralCouncil; - type ResetOrigin = EnsureRootOrTwoThirdsGeneralCouncil; - type PrimeOrigin = EnsureRootOrTwoThirdsGeneralCouncil; - type MembershipInitialized = HomaCouncil; - type MembershipChanged = HomaCouncil; - type MaxMembers = CouncilDefaultMaxMembers; - type WeightInfo = (); -} - -parameter_types! { - pub const TechnicalCommitteeMotionDuration: BlockNumber = 7 * DAYS; -} - -impl pallet_collective::Config for Runtime { - type RuntimeOrigin = RuntimeOrigin; - type Proposal = RuntimeCall; - type RuntimeEvent = RuntimeEvent; - type MotionDuration = TechnicalCommitteeMotionDuration; - type MaxProposals = CouncilDefaultMaxProposals; - type MaxMembers = CouncilDefaultMaxMembers; - type DefaultVote = pallet_collective::PrimeDefaultVote; - type SetMembersOrigin = EnsureRoot; - type WeightInfo = (); - type MaxProposalWeight = MaxProposalWeight; -} - -impl pallet_membership::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type AddOrigin = EnsureRootOrTwoThirdsGeneralCouncil; - type RemoveOrigin = EnsureRootOrTwoThirdsGeneralCouncil; - type SwapOrigin = EnsureRootOrTwoThirdsGeneralCouncil; - type ResetOrigin = EnsureRootOrTwoThirdsGeneralCouncil; - type PrimeOrigin = EnsureRootOrTwoThirdsGeneralCouncil; - type MembershipInitialized = TechnicalCommittee; - type MembershipChanged = TechnicalCommittee; - type MaxMembers = CouncilDefaultMaxMembers; - type WeightInfo = (); -} - impl pallet_membership::Config for Runtime { type RuntimeEvent = RuntimeEvent; type AddOrigin = EnsureRootOrTwoThirdsGeneralCouncil; @@ -639,59 +518,6 @@ impl pallet_recovery::Config for Runtime { type WeightInfo = (); } -parameter_types! { - pub const LaunchPeriod: BlockNumber = 2 * HOURS; - pub const VotingPeriod: BlockNumber = HOURS; - pub const FastTrackVotingPeriod: BlockNumber = HOURS; - pub MinimumDeposit: Balance = 100 * cent(ACA); - pub const EnactmentPeriod: BlockNumber = MINUTES; - pub const CooloffPeriod: BlockNumber = MINUTES; -} - -impl pallet_democracy::Config for Runtime { - type RuntimeEvent = RuntimeEvent; - type Currency = Balances; - type EnactmentPeriod = EnactmentPeriod; - type LaunchPeriod = LaunchPeriod; - type VotingPeriod = VotingPeriod; - type VoteLockingPeriod = EnactmentPeriod; // Same as EnactmentPeriod - type MinimumDeposit = MinimumDeposit; - /// A straight majority of the council can decide what their next motion is. - type ExternalOrigin = EnsureRootOrHalfGeneralCouncil; - /// A majority can have the next scheduled referendum be a straight majority-carries vote. - type ExternalMajorityOrigin = EnsureRootOrHalfGeneralCouncil; - /// A unanimous council can have the next scheduled referendum be a straight default-carries - /// (NTB) vote. - type ExternalDefaultOrigin = EnsureRootOrAllGeneralCouncil; - /// Two thirds of the technical committee can have an ExternalMajority/ExternalDefault vote - /// be tabled immediately and with a shorter voting/enactment period. - type FastTrackOrigin = EnsureRootOrTwoThirdsTechnicalCommittee; - type InstantOrigin = EnsureRootOrAllTechnicalCommittee; - type InstantAllowed = ConstBool; - type FastTrackVotingPeriod = FastTrackVotingPeriod; - // To cancel a proposal which has been passed, 2/3 of the council must agree to it. - type CancellationOrigin = EnsureRootOrTwoThirdsGeneralCouncil; - type BlacklistOrigin = EnsureRoot; - // To cancel a proposal before it has been passed, the technical committee must be unanimous or - // Root must agree. - type CancelProposalOrigin = EnsureRootOrAllTechnicalCommittee; - // 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 Slash = Treasury; - type Scheduler = Scheduler; - type PalletsOrigin = OriginCaller; - type MaxVotes = ConstU32<100>; - //TODO: might need to weight for Mandala - type WeightInfo = pallet_democracy::weights::SubstrateWeight; - type MaxProposals = CouncilDefaultMaxProposals; - type Preimages = Preimage; - type MaxDeposits = ConstU32<100>; - type MaxBlacklisted = ConstU32<100>; - type SubmitOrigin = EnsureSigned; -} - impl orml_auction::Config for Runtime { type RuntimeEvent = RuntimeEvent; type Balance = Balance; @@ -2004,6 +1830,10 @@ construct_runtime!( HomaCouncilMembership: pallet_membership:: = 55, TechnicalCommittee: pallet_collective:: = 56, TechnicalCommitteeMembership: pallet_membership:: = 57, + ConvictionVoting: pallet_conviction_voting = 150, + Referenda: pallet_referenda = 151, + Origins: governance::custom_origins::{Origin} = 152, + Whitelist: pallet_whitelist = 153, Authority: orml_authority = 70, PhragmenElection: pallet_elections_phragmen = 71,