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

Quorum Upgrade V2 WIP #62

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions upgrade_policy/quorum_upgrade/sources/quorum_upgrade_policy.move
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
/// - the creation and usage of a `Ballot` to vote for the upgrade is also being
/// discussed. The ballot will be transferable and an easy way to relate to a proposal
module quorum_upgrade_policy::quorum_upgrade_policy {
use quorum_upgrade_v2::quorum_upgrade;
use sui::event;
use sui::package::{Self, UpgradeCap, UpgradeTicket, UpgradeReceipt};
use sui::vec_set::{Self, VecSet};
Expand Down Expand Up @@ -246,6 +247,18 @@ module quorum_upgrade_policy::quorum_upgrade_policy {
}
}

public fun migrate_quorum_to_v2(cap: QuorumUpgradeCap, ctx: &mut TxContext) {
let QuorumUpgradeCap {
id,
upgrade_cap,
required_votes,
voters,
voter_caps: _voter_caps,
} = cap;
quorum_upgrade::new(upgrade_cap, required_votes, voters, ctx);
id.delete();
}

/// Propose an upgrade.
/// The `digest` of the proposed upgrade is provided to identify the upgrade.
/// The proposer is the sender of the transaction and must be the signer
Expand Down
1 change: 1 addition & 0 deletions upgrade_policy/quorum_upgrade_v2/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
build/*
9 changes: 9 additions & 0 deletions upgrade_policy/quorum_upgrade_v2/Move.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[package]
name = "quorum_upgrade_v2"
edition = "2024.beta"

[dependencies]
Sui = { git = "https://github.com/MystenLabs/sui.git", subdir = "crates/sui-framework/packages/sui-framework", rev = "framework/testnet" }

[addresses]
quorum_upgrade_v2 = "0x0"
182 changes: 182 additions & 0 deletions upgrade_policy/quorum_upgrade_v2/sources/quorum_upgrade.move
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

module quorum_upgrade_v2::quorum_upgrade;

use sui::event;
use sui::package::{Self, UpgradeCap, UpgradeTicket, UpgradeReceipt};
use sui::table_vec::{Self, TableVec};
use sui::vec_set::VecSet;

public struct QuorumUpgrade has key, store {
id: UID,
upgrade_cap: UpgradeCap,
required_votes: u64,
voters: VecSet<address>,
proposals: TableVec<ID>,
}

// ~~~~~~~ Events ~~~~~~~

public struct VoterAddedEvent has copy, drop {
quorum_upgrade_id: ID,
voter: address,
new_required_votes: u64,
}

public struct VoterRemovedEvent has copy, drop {
proposal_id: ID,
voter: address,
new_required_votes: u64,
}

public struct VoterReplacedEvent has copy, drop {
proposal_id: ID,
}

public struct RequiredVotesChangedEvent has copy, drop {
proposal_id: ID,
new_required_votes: u64,
}

public struct QuorumRelinquishedEvent has copy, drop {
proposal_id: ID,
new_required_votes: u64,
}

// ~~~~~~~ Public Functions ~~~~~~~

public fun new(
upgrade_cap: UpgradeCap,
required_votes: u64,
voters: VecSet<address>,
ctx: &mut TxContext,
) {
assert!(required_votes > 0);
assert!(voters.size() >= required_votes);

let id = object::new(ctx);
let quorum_upgrade = QuorumUpgrade {
id,
upgrade_cap,
required_votes,
voters,
proposals: table_vec::empty(ctx),
};
transfer::share_object(quorum_upgrade);
}

public fun replace_voter_by_owner(
quorum_upgrade: &mut QuorumUpgrade,
new_voter: address,
ctx: &mut TxContext,
) {
replace_voter(quorum_upgrade, ctx.sender(), new_voter)
}

public fun commit_upgrade(quorum_upgrade: &mut QuorumUpgrade, receipt: UpgradeReceipt) {
package::commit_upgrade(&mut quorum_upgrade.upgrade_cap, receipt)
}

// ~~~~~~~ Package Functions ~~~~~~~

public(package) fun add_voter(
quorum_upgrade: &mut QuorumUpgrade,
voter: address,
new_required_votes: u64,
) {
quorum_upgrade.voters.insert(voter);
quorum_upgrade.required_votes = new_required_votes;

event::emit(VoterAddedEvent {
quorum_upgrade_id: quorum_upgrade.id.to_inner(),
voter,
new_required_votes,
});
}

public(package) fun remove_voter(
quorum_upgrade: &mut QuorumUpgrade,
voter: address,
new_required_votes: u64,
) {
quorum_upgrade.voters.remove(&voter);
quorum_upgrade.required_votes = new_required_votes;

event::emit(VoterRemovedEvent {
proposal_id: quorum_upgrade.id.to_inner(),
voter,
new_required_votes,
});
}

public(package) fun replace_voter(
quorum_upgrade: &mut QuorumUpgrade,
old_voter: address,
new_voter: address,
) {
// double check assertions for replace_voter_by_owner
assert!(quorum_upgrade.voters.contains(&old_voter));
assert!(!quorum_upgrade.voters.contains(&new_voter));

quorum_upgrade.voters.remove(&old_voter);
quorum_upgrade.voters.insert(new_voter);

event::emit(VoterReplacedEvent {
proposal_id: quorum_upgrade.id.to_inner(),
});
}

public(package) fun update_required_votes(
quorum_upgrade: &mut QuorumUpgrade,
new_required_votes: u64,
) {
quorum_upgrade.required_votes = new_required_votes;

event::emit(RequiredVotesChangedEvent {
proposal_id: quorum_upgrade.id.to_inner(),
new_required_votes,
});
}

public(package) fun relinquish_quorum(quorum_upgrade: QuorumUpgrade, new_owner: address) {
let QuorumUpgrade {
id,
upgrade_cap,
proposals: proposals,
..,
} = quorum_upgrade;

event::emit(QuorumRelinquishedEvent {
proposal_id: id.to_inner(),
new_required_votes: 0,
});

id.delete();

proposals.drop();

transfer::public_transfer(upgrade_cap, new_owner);
}

public(package) fun authorize_upgrade(
quorum_upgrade: &mut QuorumUpgrade,
digest: vector<u8>,
): UpgradeTicket {
let policy = package::upgrade_policy(&quorum_upgrade.upgrade_cap);
package::authorize_upgrade(
&mut quorum_upgrade.upgrade_cap,
policy,
digest,
)
}

// ~~~~~~~ Getters ~~~~~~~

public fun voters(quorum_upgrade: &QuorumUpgrade): &VecSet<address> {
&quorum_upgrade.voters
}

public fun required_votes(quorum_upgrade: &QuorumUpgrade): u64 {
quorum_upgrade.required_votes
}
29 changes: 29 additions & 0 deletions upgrade_policy/quorum_upgrade_v2/sources/types/add_voter.move
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

module quorum_upgrade_v2::add_voter;

use quorum_upgrade_v2::proposal::Proposal;
use quorum_upgrade_v2::quorum_upgrade::QuorumUpgrade;

public struct AddVoter has store {
voter: address,
new_required_votes: u64,
}

public fun new(quorum_upgrade: &QuorumUpgrade, voter: address, new_required_votes: u64): AddVoter {
assert!(!quorum_upgrade.voters().contains(&voter));
assert!(new_required_votes > 0);
AddVoter { voter, new_required_votes }
}

public fun execute(proposal: Proposal<AddVoter>, quorum_upgrade: &mut QuorumUpgrade) {
assert!(!quorum_upgrade.voters().contains(&proposal.data().voter));

let AddVoter {
voter,
new_required_votes,
} = proposal.execute(quorum_upgrade);

quorum_upgrade.add_voter(voter, new_required_votes);
}
111 changes: 111 additions & 0 deletions upgrade_policy/quorum_upgrade_v2/sources/types/proposal.move
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

module quorum_upgrade_v2::proposal;

use quorum_upgrade_v2::quorum_upgrade::QuorumUpgrade;
use sui::event;
use sui::vec_set::{Self, VecSet};

// ~~~~~~~ Structs ~~~~~~~

public struct Proposal<T> has key, store {
id: UID,
creator: address,
quorum_upgrade: ID,
votes: VecSet<address>,
data: T,
}

// ~~~~~~~ Events ~~~~~~~

public struct VoteCastEvent has copy, drop {
proposal_id: ID,
total_votes: u64,
}

public struct ProposalDeletedEvent has copy, drop {
proposal_id: ID,
}

public struct ProposalExecutedEvent has copy, drop {
proposal_id: ID,
}

// ~~~~~~~ Public Functions ~~~~~~~

public fun new<T: store>(quorum_upgrade: &QuorumUpgrade, data: T, ctx: &mut TxContext) {
// only voters can create proposal
assert!(quorum_upgrade.voters().contains(&ctx.sender()));
let votes = vec_set::from_keys(vector[ctx.sender()]);

let proposal = Proposal {
id: object::new(ctx),
creator: ctx.sender(),
quorum_upgrade: object::id(quorum_upgrade),
votes,
data,
};
transfer::share_object(proposal);
}

public fun vote<T>(proposal: &mut Proposal<T>, ctx: &mut TxContext) {
assert!(!proposal.votes.contains(&ctx.sender()));
proposal.votes.insert(ctx.sender());

event::emit(VoteCastEvent {
proposal_id: proposal.id.to_inner(),
total_votes: proposal.votes.size(),
});
}

public fun quorum_reached<T>(proposal: &Proposal<T>, quorum_upgrade: &QuorumUpgrade): bool {
let current_votes = proposal.votes.size();
let required_votes = quorum_upgrade.required_votes();
current_votes >= required_votes
}

public fun delete_proposal_by_creator<T: drop>(proposal: Proposal<T>, ctx: &mut TxContext) {
assert!(proposal.creator == ctx.sender());
event::emit(ProposalDeletedEvent {
proposal_id: proposal.id.to_inner(),
});
proposal.delete();
}

// ~~~~~~~ Package Functions ~~~~~~~

public(package) fun execute<T>(proposal: Proposal<T>, quorum_upgrade: &QuorumUpgrade): T {
assert!(proposal.quorum_reached(quorum_upgrade));
assert!(proposal.quorum_upgrade == object::id(quorum_upgrade));

event::emit(ProposalExecutedEvent {
proposal_id: proposal.id.to_inner(),
});

proposal.delete()
}

public(package) fun delete<T>(proposal: Proposal<T>): T {
let Proposal<T> {
id,
data,
..,
} = proposal;
id.delete();
data
}

// ~~~~~~~ Getters ~~~~~~~

public fun data<T>(proposal: &Proposal<T>): &T {
&proposal.data
}

public fun quorum_upgrade<T>(proposal: &Proposal<T>): ID {
proposal.quorum_upgrade
}

public fun votes<T>(proposal: &Proposal<T>): &VecSet<address> {
&proposal.votes
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

module quorum_upgrade_v2::relinquish_quorum;

use quorum_upgrade_v2::proposal::Proposal;
use quorum_upgrade_v2::quorum_upgrade::QuorumUpgrade;

public struct RelinquishQuorum has drop {
new_owner: address,
}

// Does/can validation happen here on the provided params
public fun new(new_owner: address): RelinquishQuorum {
RelinquishQuorum { new_owner }
}

public fun execute(proposal: Proposal<RelinquishQuorum>, quorum_upgrade: QuorumUpgrade) {
let RelinquishQuorum {
new_owner,
} = proposal.data();

quorum_upgrade.relinquish_quorum(*new_owner);

proposal.delete();
}
Loading
Loading