Skip to content
This repository has been archived by the owner on Jan 10, 2025. It is now read-only.

Governance: vote options #2544

Merged
merged 57 commits into from
Nov 11, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
5bff3e9
feat: use VoteChoice instead of VoteWeight
SebastianBor Oct 25, 2021
13b53cf
chore: make clippy happy
SebastianBor Oct 25, 2021
508f925
feat: use options for ye/no vote
SebastianBor Oct 25, 2021
867c760
feat: use choices for CastVote instruction
SebastianBor Oct 25, 2021
a618faa
chore: move Vote enum to tests
SebastianBor Oct 25, 2021
417670c
fix: iterate overall choices for withdrawal
SebastianBor Oct 25, 2021
0a887c6
chore: split ProposalOption and ProposalOptionVote
SebastianBor Oct 25, 2021
aabe4e3
fix: calculate multi option proposal size
SebastianBor Oct 25, 2021
4ce9cc0
chore: split weighted and fractional vote choices
SebastianBor Oct 25, 2021
fc4c0dc
feat: add proposal type
SebastianBor Oct 25, 2021
8207e16
feat: add reject option flag
SebastianBor Oct 26, 2021
40c18cf
feat: calculate final state for proposal using options results
SebastianBor Oct 26, 2021
ae9e1d2
chore: make clippy happy
SebastianBor Oct 26, 2021
9966dee
fix: generalise max vote weight calculation for multiple options
SebastianBor Oct 26, 2021
0316db8
feat: gate vote tipping for yes/no proposals only
SebastianBor Oct 26, 2021
ef3c6b7
chore: make clippy happy
SebastianBor Oct 26, 2021
170ba0e
feat: add option_index to instruction
SebastianBor Oct 27, 2021
b5bd430
feat: move instructions to options
SebastianBor Oct 27, 2021
38f34cd
chore: advance clock
SebastianBor Oct 27, 2021
9ed8f14
chore: add await
SebastianBor Oct 27, 2021
53153ab
chore: add multi option proposal tests
SebastianBor Oct 27, 2021
772c005
chore: move governing_mint to account list
SebastianBor Oct 27, 2021
529d152
feat: assert valid proposal options
SebastianBor Oct 27, 2021
b258554
feat: assert proposal is executable when instruction is added
SebastianBor Oct 27, 2021
484a8c8
chore: make clippy happy
SebastianBor Oct 27, 2021
537ab3f
chore: add tests to insert instructions into multi option proposal
SebastianBor Oct 27, 2021
c507cc7
chore: make clippy happy
SebastianBor Oct 27, 2021
6421761
feat: use explicit reject_option_vote_weight
SebastianBor Oct 27, 2021
e64d156
feat: use Vote struct for vote results
SebastianBor Oct 27, 2021
1d19e0d
feat: validate vote
SebastianBor Oct 27, 2021
c23cd24
feat: reject empty proposal options
SebastianBor Oct 27, 2021
512a97d
chore: update comments
SebastianBor Oct 27, 2021
6e20486
fix: allow execute only successful options
SebastianBor Oct 27, 2021
5f45f7f
chore: add assertions for option statuses
SebastianBor Oct 28, 2021
e77bc18
chore: add partial success test
SebastianBor Oct 28, 2021
6a16453
chore: add full success execution test
SebastianBor Oct 28, 2021
530e0af
chore: add test for instructions execution for fully denied proposal
SebastianBor Oct 28, 2021
f407e16
feat: finalise none executable proposals into completed state
SebastianBor Oct 28, 2021
81d9115
chore: fix chat
SebastianBor Oct 28, 2021
03df0d9
feat: add vote_record v1 v2 roundtrip serialization
SebastianBor Oct 28, 2021
c864a16
eat: add proposal_instruction v1 v2 roundtrip serialisation
SebastianBor Oct 28, 2021
aac5806
chore: use VoteRecordV1
SebastianBor Oct 28, 2021
6285f0a
chore: use legacy structs instead of legacy crate version
SebastianBor Oct 28, 2021
c7b1170
chore: rename proposal to V2
SebastianBor Oct 28, 2021
e1f1420
feat: translate Proposal v1 v2 versions
SebastianBor Oct 28, 2021
1b5d74e
chore: make clippy happy
SebastianBor Oct 28, 2021
0a71dd9
chore: make clippy happy
SebastianBor Oct 28, 2021
2967adf
chore: remove unnecessary clone for match
SebastianBor Oct 30, 2021
677794a
chore: rename get_final_vote_state to resolve_final_vote_state
SebastianBor Oct 30, 2021
126464b
fix proposal account name
SebastianBor Oct 30, 2021
e6c4f77
chore: fix compilation
SebastianBor Oct 30, 2021
8fa1ba3
chore: use borsh::maybestd::io::Write
SebastianBor Oct 30, 2021
c6efecd
chore: consume self in serialise instructions to avoid cloning
SebastianBor Oct 30, 2021
00a446b
chore: update comments
SebastianBor Oct 30, 2021
ce61b13
feat: add N limit placeholder to multi choice vote type
SebastianBor Oct 30, 2021
a534d61
feat: increase options size to u16
SebastianBor Oct 30, 2021
172ead5
fix: use checked math
SebastianBor Oct 30, 2021
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
Prev Previous commit
Next Next commit
feat: validate vote
  • Loading branch information
SebastianBor committed Oct 27, 2021
commit 1d19e0d2bf44e08cce29b0bb816fe757f34ee428
4 changes: 4 additions & 0 deletions governance/program/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,10 @@ pub enum GovernanceError {
/// Proposal is not not executable
#[error("Proposal is not not executable")]
ProposalIsNotExecutable,

/// Invalid vote
#[error("Invalid vote")]
InvalidVote,
}

impl PrintProgramError for GovernanceError {
Expand Down
6 changes: 1 addition & 5 deletions governance/program/src/processor/process_cast_vote.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,13 +105,9 @@ pub fn process_cast_vote(
&realm_data,
)?;

// TODO: Add test to reject proposal without rejection
proposal_data.assert_valid_vote(&vote)?;

// Calculate Proposal voting weights
// TODO: Validate choices are valid for given proposal vote type. Single choice should have one choice only, no deny shouldn't have Deny vote
// Assert option.len() === choices().len()
// YesNo - only once choice 100% and no rank

match vote.clone() {
SebastianBor marked this conversation as resolved.
Show resolved Hide resolved
Vote::Approve(choices) => {
for (option, choice) in proposal_data.options.iter_mut().zip(choices) {
Expand Down
177 changes: 177 additions & 0 deletions governance/program/src/state/proposal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ use crate::{
};
use borsh::{BorshDeserialize, BorshSchema, BorshSerialize};

use super::vote_record::Vote;

/// Proposal option vote status
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)]
pub enum OptionVoteResult {
Expand Down Expand Up @@ -553,6 +555,51 @@ impl Proposal {

Ok(())
}

/// Asserts the given vote is valid for the proposal
pub fn assert_valid_vote(&self, vote: &Vote) -> Result<(), ProgramError> {
match vote {
Vote::Approve(choices) => {
if self.options.len() != choices.len() {
return Err(GovernanceError::InvalidVote.into());
}

let mut choice_count = 0;

for choice in choices {
if choice.rank > 0 {
return Err(GovernanceError::InvalidVote.into());
}

if choice.weight_percentage == 100 {
choice_count += 1;
} else if choice.weight_percentage != 0 {
return Err(GovernanceError::InvalidVote.into());
}
}

match self.vote_type {
VoteType::SingleChoice => {
if choice_count != 1 {
return Err(GovernanceError::InvalidVote.into());
}
}
VoteType::MultiChoice => {
if choice_count == 0 {
return Err(GovernanceError::InvalidVote.into());
}
}
}
}
Vote::Deny => {
if self.deny_vote_weight.is_none() {
return Err(GovernanceError::InvalidVote.into());
}
}
}

Ok(())
}
}

/// Converts threshold in percentages to actual vote weight
Expand Down Expand Up @@ -671,6 +718,7 @@ mod test {
use crate::state::{
enums::{MintMaxVoteWeightSource, VoteThresholdPercentage, VoteWeightSource},
realm::RealmConfig,
vote_record::VoteChoice,
};

use {super::*, proptest::prelude::*};
Expand Down Expand Up @@ -1593,4 +1641,133 @@ mod test {
// Assert
assert_eq!(result, Ok(()));
}

#[test]
pub fn test_assert_valid_vote_with_deny_vote_for_survey_only_proposal_error() {
// Arrange
let mut proposal = create_test_proposal();
proposal.deny_vote_weight = None;

let vote = Vote::Deny;

// Act
let result = proposal.assert_valid_vote(&vote);

// Assert
assert_eq!(result, Err(GovernanceError::InvalidVote.into()));
}

#[test]
pub fn test_assert_valid_vote_with_too_many_options_error() {
// Arrange
let proposal = create_test_proposal();

let choices = vec![
VoteChoice {
rank: 0,
weight_percentage: 100,
},
VoteChoice {
rank: 0,
weight_percentage: 100,
},
];

let vote = Vote::Approve(choices.clone());

// Ensure
assert!(proposal.options.len() != choices.len());

// Act
let result = proposal.assert_valid_vote(&vote);

// Assert
assert_eq!(result, Err(GovernanceError::InvalidVote.into()));
}

#[test]
pub fn test_assert_valid_vote_with_no_choice_for_single_choice_error() {
// Arrange
let proposal = create_test_proposal();

let choices = vec![VoteChoice {
rank: 0,
weight_percentage: 0,
}];

let vote = Vote::Approve(choices.clone());

// Ensure
assert_eq!(proposal.options.len(), choices.len());

// Act
let result = proposal.assert_valid_vote(&vote);

// Assert
assert_eq!(result, Err(GovernanceError::InvalidVote.into()));
}

#[test]
pub fn test_assert_valid_vote_with_to_many_choices_for_single_choice_error() {
// Arrange
let proposal = create_test_multi_option_proposal();
let choices = vec![
VoteChoice {
rank: 0,
weight_percentage: 100,
},
VoteChoice {
rank: 0,
weight_percentage: 100,
},
VoteChoice {
rank: 0,
weight_percentage: 0,
},
];

let vote = Vote::Approve(choices.clone());

// Ensure
assert_eq!(proposal.options.len(), choices.len());

// Act
let result = proposal.assert_valid_vote(&vote);

// Assert
assert_eq!(result, Err(GovernanceError::InvalidVote.into()));
}

#[test]
pub fn test_assert_valid_vote_with_no_choices_for_multi_choice_error() {
// Arrange
let mut proposal = create_test_multi_option_proposal();
proposal.vote_type = VoteType::MultiChoice;

let choices = vec![
VoteChoice {
rank: 0,
weight_percentage: 0,
},
VoteChoice {
rank: 0,
weight_percentage: 0,
},
VoteChoice {
rank: 0,
weight_percentage: 0,
},
];

let vote = Vote::Approve(choices.clone());

// Ensure
assert_eq!(proposal.options.len(), choices.len());

// Act
let result = proposal.assert_valid_vote(&vote);

// Assert
assert_eq!(result, Err(GovernanceError::InvalidVote.into()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ async fn test_cast_vote_with_voter_weight_addin() {
.get_proposal_account(&proposal_cookie.address)
.await;

assert_eq!(100, proposal_account.options[0].vote_weight);
assert_eq!(120, proposal_account.options[0].vote_weight);
}

#[tokio::test]
Expand Down