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 7 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all 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/*
26 changes: 26 additions & 0 deletions upgrade_policy/quorum_upgrade_v2/Move.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# @generated by Move, please check-in and do not edit manually.

[move]
version = 3
manifest_digest = "404350DCC27BF6970E9A41C733BDC6A65BF54FADCF95E48422CA54A1CC5C7B8E"
deps_digest = "F8BBB0CCB2491CA29A3DF03D6F92277A4F3574266507ACD77214D37ECA3F3082"
dependencies = [
{ id = "Sui", name = "Sui" },
]

[[move.package]]
id = "MoveStdlib"
source = { git = "https://github.com/MystenLabs/sui.git", rev = "framework/testnet", subdir = "crates/sui-framework/packages/move-stdlib" }

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

dependencies = [
{ id = "MoveStdlib", name = "MoveStdlib" },
]

[move.toolchain-version]
compiler-version = "1.39.3"
edition = "2024.beta"
flavor = "sui"
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"
130 changes: 130 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,130 @@
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

module quorum_upgrade_v2::quorum_upgrade;

use quorum_upgrade_v2::events;
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>,
}

// ~~~~~~~ ERRORS ~~~~~~~

#[error]
const EInvalidZeroRequiredVotes: vector<u8> = b"Required votes must be greater than 0";
#[error]
const EInvalidVoters: vector<u8> = b"Voter set must contain at least the required number of votes";
#[error]
const EInvalidOldVoter: vector<u8> = b"Old voter does not exist in the quorum";
#[error]
const EInvalidNewVoter: vector<u8> = b"New voter already exists in the quorum";

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

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

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_self(
quorum_upgrade: &mut QuorumUpgrade,
new_voter: address,
ctx: &mut TxContext,
) {
quorum_upgrade.replace_voter(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) {
quorum_upgrade.voters.insert(voter);
events::emit_voter_added_event(quorum_upgrade.id.to_inner(), voter);
}

public(package) fun remove_voter(quorum_upgrade: &mut QuorumUpgrade, voter: address) {
quorum_upgrade.voters.remove(&voter);
events::emit_voter_removed_event(quorum_upgrade.id.to_inner(), voter);
}

public(package) fun replace_voter(
quorum_upgrade: &mut QuorumUpgrade,
old_voter: address,
new_voter: address,
) {
// double check assertions for replace_self
assert!(quorum_upgrade.voters.contains(&old_voter), EInvalidOldVoter);
assert!(!quorum_upgrade.voters.contains(&new_voter), EInvalidNewVoter);
quorum_upgrade.voters.remove(&old_voter);
quorum_upgrade.voters.insert(new_voter);
events::emit_voter_replaced_event(quorum_upgrade.id.to_inner(), old_voter, new_voter);
}

public(package) fun update_threshold(quorum_upgrade: &mut QuorumUpgrade, new_required_votes: u64) {
quorum_upgrade.required_votes = new_required_votes;
events::emit_threshold_updated_event(quorum_upgrade.id.to_inner(), new_required_votes);
}

public(package) fun relinquish_quorum(quorum_upgrade: QuorumUpgrade, new_owner: address) {
Bridgerz marked this conversation as resolved.
Show resolved Hide resolved
let QuorumUpgrade {
id,
upgrade_cap,
proposals: proposals,
..,
} = quorum_upgrade;
events::emit_quorum_relinquished_event(id.to_inner());

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
}
55 changes: 55 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,55 @@
// 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, drop {
voter: address,
new_required_votes: Option<u64>,
}

#[error]
const EInvalidNewVoter: vector<u8> = b"Voter already exists in the quorum";
#[error]
const ERequiredVotesZero: vector<u8> = b"Required votes must be greater than 0";
#[error]
const EInvalidRequiredVotes: vector<u8> =
b"Required votes must be less than or equal to the number of voters";

public fun new(
quorum_upgrade: &QuorumUpgrade,
voter: address,
new_required_votes: Option<u64>,
): AddVoter {
assert!(!quorum_upgrade.voters().contains(&voter), EInvalidNewVoter);
if (new_required_votes.is_some()) {
let required_votes = new_required_votes.borrow();
assert!(*required_votes > 0, ERequiredVotesZero);
assert!(*required_votes <= quorum_upgrade.voters().size() as u64, EInvalidRequiredVotes);
};

AddVoter { voter, new_required_votes }
}

public fun execute(proposal: Proposal<AddVoter>, quorum_upgrade: &mut QuorumUpgrade) {
let AddVoter {
voter,
mut new_required_votes,
} = proposal.execute(quorum_upgrade);

if (new_required_votes.is_some()) {
quorum_upgrade.update_threshold(new_required_votes.extract());
};
quorum_upgrade.add_voter(voter);
}

public fun voter(add_voter: &AddVoter): address {
add_voter.voter
}

public fun required_votes(add_voter: &AddVoter): Option<u64> {
add_voter.new_required_votes
}
114 changes: 114 additions & 0 deletions upgrade_policy/quorum_upgrade_v2/sources/types/events.move
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
module quorum_upgrade_v2::events;

use sui::event;

public struct VoteCastEvent has copy, drop {
proposal_id: ID,
voter: address,
}

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

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

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

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

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

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

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

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

public(package) fun emit_vote_cast_event(proposal_id: ID, voter: address) {
event::emit(VoteCastEvent {
proposal_id,
voter,
});
}

public(package) fun emit_quorum_reached_event(proposal_id: ID) {
event::emit(QuorumReachedEvent {
proposal_id,
});
}

public(package) fun emit_proposal_deleted_event(proposal_id: ID) {
event::emit(ProposalDeletedEvent {
proposal_id,
});
}

public(package) fun emit_proposal_executed_event(proposal_id: ID) {
event::emit(ProposalExecutedEvent {
proposal_id,
});
}

public(package) fun emit_voter_added_event(quorum_upgrade_id: ID, voter: address) {
event::emit(VoterAddedEvent {
quorum_upgrade_id,
voter,
});
}

public(package) fun emit_voter_removed_event(proposal_id: ID, voter: address) {
event::emit(VoterRemovedEvent {
proposal_id,
voter,
});
}

public(package) fun emit_voter_replaced_event(
proposal_id: ID,
old_voter: address,
new_voter: address,
) {
event::emit(VoterReplacedEvent {
proposal_id,
old_voter,
new_voter,
});
}

public(package) fun emit_threshold_updated_event(proposal_id: ID, new_required_votes: u64) {
event::emit(ThresholdUpdatedEvent {
proposal_id,
new_required_votes,
});
}

public(package) fun emit_quorum_relinquished_event(proposal_id: ID) {
event::emit(QuorumRelinquishedEvent {
proposal_id: proposal_id,
});
}

#[test_only]
public fun proposal_id(quorum_reached_event: &QuorumReachedEvent): ID {
quorum_reached_event.proposal_id
}
Loading
Loading