Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(mainnet): add gov: collective, membership, motion #448

Open
wants to merge 36 commits into
base: main
Choose a base branch
from

Conversation

peterwht
Copy link
Collaborator

@peterwht peterwht commented Jan 23, 2025

[This is a spike and an incomplete PR. But the governance is implemented and can be used]

Adds a Council based governance that:

  • Has NO elections (selected councillors, to start)
  • Uses pallet-motion to allow council to use Root origin

To familiarize yourself with this governance mechanism, please use the following guide.

Guide

Goal Checklist

  • Create a Motion that requires Root
    • Vote on Motion
    • Execute Motion
  • Create a Motion that adds a member
    • Vote on Motion
    • Execute Motion
  • Create a Motion that removes a member
    • Vote on Motion
    • Execute Motion

Create a Motion that requires Root

  1. Launch chain pop up parachain -f networks/mainnet.toml
  2. Navigate to Council page and note Members
  3. Navigate to sub-page for Motions
  4. Press "Propose Motion"
  5. Min threshold is currently 1/2 + 1. I recommend setting threshold to 4 (unanimous)
  6. Set the proposal call to motion.unanimous, providing Root origin
  7. Set the sub-call to something that requires Root. One recommendation is to use balances.forceSetBalance
  8. You will see a council.Proposed event
  9. On the Motions page you will see a new Motion available for vote, along with proposal info
  10. Press the Vote button and vote AYE. Repeat this with 4 dev accounts
  11. After the threshold is surpassed, the Vote button now changes to Close. Press this button and submit. The proposal is now passed! You will see several events such as council.voted, council.Approved, council.Executed

Screenshot 2025-01-23 at 10 10 07 AM

Create a Motion that adds a member

  1. Navigate to the Motions page
  2. Press Propose motion
  3. Set threshold 4 (more realistic)
  4. Select councilMembership.addMember for proposal (we do NOT need to use pallet-motion for this). Note, do not use council to set members.
  5. Add Eve or Ferdie (or other as you please)
  6. Press Propose
  7. Go through the voting process and execute the proposal
  8. Wait a bit and refresh the council page. You should now see a new member added that can propose motions and vote.

Screenshot 2025-01-23 at 10 16 46 AM

Create a Motion that removes a member

  1. Navigate to the Motions page
  2. Press Propose motion
  3. Set threshold 4 (more realistic)
  4. Select councilMembership.removeMember for proposal (we do NOT need to use pallet-motion for this). Note, do not use council to set members.
  5. Add Eve or Ferdie (or other as you please)
  6. Press Propose
  7. Go through the voting process and execute the proposal
  8. Wait a bit and refresh the council page. You should now see one less member.

Notes

  • One important thing to confirm for ANY governance we choose: do we have to pay DOT to propose a runtime upgrade. This can be costly if so.

node/src/chain_spec.rs Outdated Show resolved Hide resolved
@peterwht
Copy link
Collaborator Author

[sc-2571]

@al3mart
Copy link
Collaborator

al3mart commented Jan 24, 2025

image

Polakdot js apps seems to crash after inputting an external motion 😓 with the expectation of pallet_democracy being present ?

For the record the call I noted as a preimage was balances.forceSetBalance.

I'd assume this is expected as the runtime doesn't feature a way for non council members to cast votes. I think the app will have just wrapped the noted call as a democracy call.
Though is just ugly that the app presents that message.

Copy link
Collaborator

@al3mart al3mart left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The inclusion of these seemed like a sensible choice. Provides a little better UX for the accounts voting on the motions.

Also seems more flexible looking ahead.

runtime/mainnet/src/config/governance.rs Outdated Show resolved Hide resolved
runtime/mainnet/src/config/governance.rs Outdated Show resolved Hide resolved
runtime/mainnet/src/config/governance.rs Outdated Show resolved Hide resolved
runtime/mainnet/src/config/governance.rs Outdated Show resolved Hide resolved
runtime/mainnet/src/config/governance.rs Outdated Show resolved Hide resolved
@peterwht peterwht changed the base branch from main to al3mart/refactor-mainnet-governance January 24, 2025 17:44
@peterwht peterwht changed the base branch from al3mart/refactor-mainnet-governance to main January 24, 2025 17:44
@peterwht
Copy link
Collaborator Author

[sc-2200]

@AlexD10S
Copy link
Collaborator

The step-by-step guide in the PR description is great. I've been testing and looks good 👌

@al3mart
Copy link
Collaborator

al3mart commented Feb 5, 2025

I have applied the feedback in this thread.

Introducing a more strict configuration in: 25029be
And adding configuration tests in: 78e9e79

@codecov-commenter
Copy link

codecov-commenter commented Feb 5, 2025

Codecov Report

Attention: Patch coverage is 95.06531% with 34 lines in your changes missing coverage. Please review.

Project coverage is 74.42%. Comparing base (168cf85) to head (a76aa2e).

Files with missing lines Patch % Lines
pallets/motion/src/weights.rs 50.00% 21 Missing ⚠️
node/src/chain_spec.rs 0.00% 7 Missing ⚠️
pallets/motion/src/lib.rs 82.14% 3 Missing and 2 partials ⚠️
pallets/motion/src/benchmarking.rs 85.71% 0 Missing and 1 partial ⚠️
@@            Coverage Diff             @@
##             main     #448      +/-   ##
==========================================
+ Coverage   73.50%   74.42%   +0.91%     
==========================================
  Files          78       84       +6     
  Lines       15696    16384     +688     
  Branches    15696    16384     +688     
==========================================
+ Hits        11538    12193     +655     
- Misses       3897     3927      +30     
- Partials      261      264       +3     
Files with missing lines Coverage Δ
pallets/motion/src/mock.rs 100.00% <100.00%> (ø)
pallets/motion/src/tests.rs 100.00% <100.00%> (ø)
runtime/mainnet/src/config/governance.rs 100.00% <100.00%> (ø)
runtime/mainnet/src/lib.rs 81.37% <ø> (ø)
pallets/motion/src/benchmarking.rs 85.71% <85.71%> (ø)
pallets/motion/src/lib.rs 82.14% <82.14%> (ø)
node/src/chain_spec.rs 9.96% <0.00%> (-0.21%) ⬇️
pallets/motion/src/weights.rs 50.00% <50.00%> (ø)

@al3mart al3mart marked this pull request as ready for review February 5, 2025 03:39
Copy link
Collaborator Author

@peterwht peterwht left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for taking this on!

I left a few comments -- biggest one about the min origin required for root.

In a separate PR (maybe with the genesis config presets), I think it would make our lives easier to have a Dev version of mainnet so we can use the dev accounts. Just noting for later.

FYI: I can't approve this PR as I am the original author.

type SimpleMajorityOrigin = NeverEnsureOrigin<()>;
// SuperMajority origin check will always fail.
// Making it not possible for SimpleMajority to dispatch as root.
type SuperMajorityOrigin = NeverEnsureOrigin<()>;
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Frank made a good point about unanimous only for root.

What happens if a vulnerability is discovered, one of the council members is OOO, and we need to immediately push a fix? We would not be able to do this if just one council is OOO for unanimous.

Based on this a 3/4 would be better (enable super majority)

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This makes 👍

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

runtime/mainnet/src/config/governance.rs Outdated Show resolved Hide resolved
use super::*;

#[test]
fn simple_majority_always_fails() {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
fn simple_majority_always_fails() {
fn simple_majority_is_never_origin() {

Maybe this is a more accurate name?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}

#[test]
fn super_majority_always_fails() {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if we update to 3/4 for super-majority, this would change.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Collaborator

@evilrobot-01 evilrobot-01 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Saw that prior comments were never submitted, so submitting now in case they are helpful.

@@ -0,0 +1,32 @@
# Motion Pallet
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we reference the source of this code, giving proper credit.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pub mod weights;
pub use pallet::*;
use sp_runtime::DispatchResult;
use sp_std::prelude::*;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Think sp_std is deprecated, should be an easy fix to update.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pub struct Pallet<T>(_);

#[pallet::config]
pub trait Config: frame_system::Config {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doc comments on these would be appreciated.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And the rest of the pub types and traits

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor: sorting these alphabetically would also be appreciated (but not essential), mostly as our rust formatting rules for trait impls do as much automatically, meaning that the ordering is consistent.

#[pallet::event]
#[pallet::generate_deposit(pub(super) fn deposit_event)]
pub enum Event<T: Config> {
/// A SimpleMajority motion was executed. motion_result contains the call result
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/// A SimpleMajority motion was executed. motion_result contains the call result
/// A [SimpleMajorityOrigin] motion was executed. [motion_result] contains the call result

Assuming this allows control-clicking on motion_result and it takes one to the field. Same for other variants in the enum.

The origin reference is an assumption.

@@ -34,6 +34,15 @@ balances = [
["5CiPPseXPECbkjWCa6MnjNokrgYjMqmKndv2rSnekmSK2DjL", 10000000000000000],
]

[parachains.genesis_overrides.councilMembership]
members = [
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice!

Assuming we proceed with this, can we add these directly to the genesis preset/chain-spec for the devnet runtime too? Rationale is having devnet as close to mainnet as practically possible, so devs are familiar with the process from the ground floor. This is based on the assumption that we can still have a 1/1 threshold for devnet so as quick as sudo would be.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

will address when including the genesis presets! 👍

@@ -0,0 +1,90 @@

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should ideally regenerate these before merging, only to ensure that it all still works - especially with the mainnet config.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can be addressed along with the rest of benchmarks that need to be run.

@@ -82,6 +82,11 @@ pallet-collator-selection.workspace = true
parachain-info.workspace = true
parachains-common.workspace = true

# Governance
pallet-collective.workspace = true
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fine for initial spike, but should be moved to Substrate block before merge.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#[runtime::pallet_index(17)]
pub type CouncilMembership = pallet_membership::Pallet<Runtime, Instance1>;
#[runtime::pallet_index(18)]
pub type Motion = pallet_motion::Pallet<Runtime>;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
pub type Motion = pallet_motion::Pallet<Runtime>;
pub type Motion = pallet_motion;

Syntax can be simplified.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

impl pallet_motion::Config for Runtime {
type RuntimeCall = RuntimeCall;
type RuntimeEvent = RuntimeEvent;
// SimpleMajority origin check will always fail.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That was an unfortunate comment from my side.
I meant to say that the origin check as configured with NeverEnsureOrigin will always fail.

// Making it not possible for SimpleMajority to dispatch as root.
type SimpleMajorityOrigin = NeverEnsureOrigin<()>;
// At least 3/4 of the council vote is needed.
type SuperMajorityOrigin = EnsureProportionAtLeast<AccountId, CouncilCollective, 3, 4>;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplication with type aliases defined above - can they not be reused/shared with pallet_membership?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They can and it looks way better: 6d87835

@al3mart al3mart requested a review from evilrobot-01 February 7, 2025 00:55
Copy link
Collaborator

@Daanvdplas Daanvdplas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looking good! Left a few comments

pallets/motion/Cargo.toml Outdated Show resolved Hide resolved
pub struct Pallet<T>(_);

#[pallet::config]
pub trait Config: frame_system::Config {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And the rest of the pub types and traits

runtime/mainnet/src/config/governance.rs Outdated Show resolved Hide resolved
runtime/mainnet/src/config/governance.rs Show resolved Hide resolved
runtime/mainnet/src/config/governance.rs Outdated Show resolved Hide resolved
runtime/mainnet/src/config/governance.rs Outdated Show resolved Hide resolved
@Daanvdplas Daanvdplas self-requested a review February 7, 2025 10:11
Copy link
Collaborator

@Daanvdplas Daanvdplas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

Copy link
Collaborator

@evilrobot-01 evilrobot-01 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

High level review only, with various suggestions which is left up to author to decide on whether they should be addressed or not.

Note: did not look at any of the tests.

@@ -0,0 +1,51 @@
[package]
authors = [ "Substrate DevHub <https://github.com/substrate-developer-hub>" ]
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This differs from the credited source? https://github.com/Watr-Protocol/watr/blob/12f372af121e2b62532664eff89c7325427f2165/pallets/motion/Cargo.toml#L4

Suggest it is set to the same as the forked pallet and then possibly add us as well if there have been any improvements made.

pallets/motion/src/benchmarking.rs Show resolved Hide resolved
pallets/motion/src/benchmarking.rs Show resolved Hide resolved
pallets/motion/src/benchmarking.rs Show resolved Hide resolved
pallets/motion/src/benchmarking.rs Show resolved Hide resolved
#[pallet::call_index(1)]
pub fn simple_majority(
origin: OriginFor<T>,
call: Box<<T as Config>::RuntimeCall>,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most of these <T as Config>::RuntimeCall can be simplified to T::RuntimeCall if the additional associated types are removed, leaving just the one from the underlying frame_system trait.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Didn't pursue as per: #448 (comment)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@@ -36,6 +37,8 @@ frame-try-runtime.workspace = true
pallet-aura.workspace = true
pallet-authorship.workspace = true
pallet-balances.workspace = true
pallet-collective.workspace = true
pallet-membership.workspace = true
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FYI - Could be addressed in a follow up, but I spiked a similar implementation without the membership pallet and it seems to work just fine for a small set, which I imagine we will be for the first foreseeable period. Simplifies the implementation.

runtime/mainnet/src/config/governance.rs Show resolved Hide resolved
runtime/mainnet/src/config/governance.rs Outdated Show resolved Hide resolved

pub type CouncilMembership = pallet_collective::Instance1;
impl pallet_membership::Config<CouncilMembership> for Runtime {
type AddOrigin = UnanimousCouncilVote;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Interesting. Why does the whole council need to agree to add a member, but only 3/4 to remove? I get that a member may vote nay and block removal, but the same could be said for adding. I vote nay to an add as my voting power/control is being reduced.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess this configuration is heavily opinionated.

I'll start by the removal as it's simpler. My assumption was that in case the council needs to remove a bad actor, this will always vote against being removed. Maybe a configuration more similar to "all votes but one" could be way better for this case.

Regarding unanimous additions: My thought was that if we are seeding the council with actors that just want to retain power (not adding member because it results in reduced control) we might as well just run with pallet sudo only. At the same time, more members means more nuanced scenarios, that's why I though that the current councilors should strongly agree on it.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Membership brings flexibility, but also more questions.

Copy link
Collaborator

@al3mart al3mart Feb 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RemoveOrigin could look like this: 604bb64.
But it's pretty rigid and can't adapt to on chain activity.

al3mart and others added 2 commits February 7, 2025 12:56
Co-authored-by: Frank Bell <60948618+evilrobot-01@users.noreply.github.com>

Update pallets/motion/src/benchmarking.rs

Co-authored-by: Frank Bell <60948618+evilrobot-01@users.noreply.github.com>

Update pallets/motion/src/benchmarking.rs

Co-authored-by: Frank Bell <60948618+evilrobot-01@users.noreply.github.com>

Update pallets/motion/src/benchmarking.rs

Co-authored-by: Frank Bell <60948618+evilrobot-01@users.noreply.github.com>

Update pallets/motion/src/benchmarking.rs

Co-authored-by: Frank Bell <60948618+evilrobot-01@users.noreply.github.com>
Copy link
Collaborator

@evilrobot-01 evilrobot-01 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems good to me. I have not looked at the tests or config in any detail so would not be comfortable giving it an approval.

Great work in gettting it all neatened up and ready for prime time!

type AllMembersButOne = EitherOfDiverse<
EnsureRoot<AccountId>,
// Currently we set 4 members at genesis.
EnsureMembers<AccountId, CouncilCollective, 3>,
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what happens if we add or remove a new member. Wouldn't this break?

Copy link
Collaborator

@al3mart al3mart Feb 7, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will keep on ensuring that at least the provided amount of members voted aye. Which is not ideal if the number of total members changes :/

I'm not specially happy with that option because 3 will only be every member minus one under that concrete condition.

I guess whether this is useful or not depends on how we foresee the council to change in the short term.

I'd be more comfortable merging with AtLeastThreeFourthsOfCouncil than this fixed amount of members.

al3mart and others added 2 commits February 7, 2025 21:27
Co-authored-by: Peter White <petras9789@gmail.com>
@al3mart al3mart requested review from Daanvdplas, evilrobot-01 and a team February 10, 2025 16:54
@@ -238,7 +241,10 @@ fn mainnet_genesis(
"polkadotXcm": {
"safeXcmVersion": Some(SAFE_XCM_VERSION),
},
"sudo": { "key": Some(root) }
"sudo": { "key": Some(root) },
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DQ: do we still need sudo?

Copy link
Collaborator

@al3mart al3mart Feb 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right at the start I'd say it could be convenient.
The pallet conveniently includes a way to kill the SUDO key.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

6 participants