Skip to content
This repository was archived by the owner on Mar 11, 2025. It is now read-only.

Commit 925f8f5

Browse files
authored
Governance: Limit proposal options to 10 (#2841)
* fix: limit proposal options to 10 * chore: increase reserved space to 64
1 parent 0e85d2e commit 925f8f5

File tree

7 files changed

+116
-19
lines changed

7 files changed

+116
-19
lines changed

governance/program/src/instruction.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ pub enum GovernanceInstruction {
232232
InsertTransaction {
233233
#[allow(dead_code)]
234234
/// The index of the option the transaction is for
235-
option_index: u16,
235+
option_index: u8,
236236
#[allow(dead_code)]
237237
/// Transaction index to be inserted at.
238238
index: u16,
@@ -1168,7 +1168,7 @@ pub fn insert_transaction(
11681168
governance_authority: &Pubkey,
11691169
payer: &Pubkey,
11701170
// Args
1171-
option_index: u16,
1171+
option_index: u8,
11721172
index: u16,
11731173
hold_up_time: u32,
11741174
instructions: Vec<InstructionData>,

governance/program/src/processor/process_create_proposal.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,7 @@ pub fn process_create_proposal(
151151
max_voting_time: None,
152152
vote_threshold_percentage: None,
153153

154-
reserved: [0; 8],
154+
reserved: [0; 64],
155155
};
156156

157157
create_and_serialize_account_signed::<ProposalV2>(

governance/program/src/processor/process_insert_transaction.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ use crate::{
2828
pub fn process_insert_transaction(
2929
program_id: &Pubkey,
3030
accounts: &[AccountInfo],
31-
option_index: u16,
31+
option_index: u8,
3232
instruction_index: u16,
3333
hold_up_time: u32,
3434
instructions: Vec<InstructionData>,

governance/program/src/state/proposal.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -90,14 +90,14 @@ pub enum VoteType {
9090
/// By default it equals to the number of available options
9191
/// Note: In the current version the limit is not supported and not enforced yet
9292
#[allow(dead_code)]
93-
max_voter_options: u16,
93+
max_voter_options: u8,
9494

9595
/// The max number of wining options
9696
/// For executable proposals it limits how many options can be executed for a Proposal
9797
/// By default it equals to the number of available options
9898
/// Note: In the current version the limit is not supported and not enforced yet
9999
#[allow(dead_code)]
100-
max_winning_options: u16,
100+
max_winning_options: u8,
101101
},
102102
}
103103

@@ -193,7 +193,7 @@ pub struct ProposalV2 {
193193
pub vote_threshold_percentage: Option<VoteThresholdPercentage>,
194194

195195
/// Reserved space for future versions
196-
pub reserved: [u8; 8],
196+
pub reserved: [u8; 64],
197197

198198
/// Proposal name
199199
pub name: String,
@@ -205,7 +205,7 @@ pub struct ProposalV2 {
205205
impl AccountMaxSize for ProposalV2 {
206206
fn get_max_size(&self) -> Option<usize> {
207207
let options_size: usize = self.options.iter().map(|o| o.label.len() + 19).sum();
208-
Some(self.name.len() + self.description_link.len() + options_size + 241)
208+
Some(self.name.len() + self.description_link.len() + options_size + 295)
209209
}
210210
}
211211

@@ -897,7 +897,7 @@ pub fn get_proposal_data(
897897
vote_threshold_percentage: proposal_data_v1.vote_threshold_percentage,
898898
name: proposal_data_v1.name,
899899
description_link: proposal_data_v1.description_link,
900-
reserved: [0; 8],
900+
reserved: [0; 64],
901901
});
902902
}
903903

@@ -968,7 +968,7 @@ pub fn assert_valid_proposal_options(
968968
options: &[String],
969969
vote_type: &VoteType,
970970
) -> Result<(), ProgramError> {
971-
if options.is_empty() {
971+
if options.is_empty() || options.len() > 10 {
972972
return Err(GovernanceError::InvalidProposalOptions.into());
973973
}
974974

@@ -1051,7 +1051,7 @@ mod test {
10511051
max_voting_time: Some(0),
10521052
vote_threshold_percentage: Some(VoteThresholdPercentage::YesVote(100)),
10531053

1054-
reserved: [0; 8],
1054+
reserved: [0; 64],
10551055
}
10561056
}
10571057

governance/program/src/state/proposal_transaction.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ pub struct ProposalTransactionV2 {
9595
pub proposal: Pubkey,
9696

9797
/// The option index the instruction belongs to
98-
pub option_index: u16,
98+
pub option_index: u8,
9999

100100
/// Unique transaction index within it's parent Proposal
101101
pub transaction_index: u16,
@@ -125,7 +125,7 @@ impl AccountMaxSize for ProposalTransactionV2 {
125125
.sum::<usize>()
126126
+ 4;
127127

128-
Some(instructions_size + 91)
128+
Some(instructions_size + 90)
129129
}
130130
}
131131

@@ -166,7 +166,7 @@ impl ProposalTransactionV2 {
166166
/// Returns ProposalTransaction PDA seeds
167167
pub fn get_proposal_transaction_address_seeds<'a>(
168168
proposal: &'a Pubkey,
169-
option_index: &'a [u8; 2], // u16 le bytes
169+
option_index: &'a [u8; 1], // u8 le bytes
170170
instruction_index_le_bytes: &'a [u8; 2], // u16 le bytes
171171
) -> [&'a [u8]; 4] {
172172
[
@@ -181,7 +181,7 @@ pub fn get_proposal_transaction_address_seeds<'a>(
181181
pub fn get_proposal_transaction_address<'a>(
182182
program_id: &Pubkey,
183183
proposal: &'a Pubkey,
184-
option_index_le_bytes: &'a [u8; 2], // u16 le bytes
184+
option_index_le_bytes: &'a [u8; 1], // u8 le bytes
185185
instruction_index_le_bytes: &'a [u8; 2], // u16 le bytes
186186
) -> Pubkey {
187187
Pubkey::find_program_address(

governance/program/tests/program_test/mod.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1809,7 +1809,7 @@ impl GovernanceProgramTest {
18091809
max_voting_time: None,
18101810
vote_threshold_percentage: None,
18111811

1812-
reserved: [0; 8],
1812+
reserved: [0; 64],
18131813
};
18141814

18151815
let proposal_address = get_proposal_address(
@@ -2197,7 +2197,7 @@ impl GovernanceProgramTest {
21972197
governed_mint_cookie: &GovernedMintCookie,
21982198
proposal_cookie: &mut ProposalCookie,
21992199
token_owner_record_cookie: &TokenOwnerRecordCookie,
2200-
option_index: u16,
2200+
option_index: u8,
22012201
index: Option<u16>,
22022202
) -> Result<ProposalTransactionCookie, ProgramError> {
22032203
let token_account_keypair = Keypair::new();
@@ -2366,7 +2366,7 @@ impl GovernanceProgramTest {
23662366
&mut self,
23672367
proposal_cookie: &mut ProposalCookie,
23682368
token_owner_record_cookie: &TokenOwnerRecordCookie,
2369-
option_index: u16,
2369+
option_index: u8,
23702370
index: Option<u16>,
23712371
) -> Result<ProposalTransactionCookie, ProgramError> {
23722372
// Create NOP instruction as a placeholder
@@ -2392,7 +2392,7 @@ impl GovernanceProgramTest {
23922392
&mut self,
23932393
proposal_cookie: &mut ProposalCookie,
23942394
token_owner_record_cookie: &TokenOwnerRecordCookie,
2395-
option_index: u16,
2395+
option_index: u8,
23962396
index: Option<u16>,
23972397
instruction: &mut Instruction,
23982398
) -> Result<ProposalTransactionCookie, ProgramError> {

governance/program/tests/use_proposals_with_multiple_options.rs

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -976,3 +976,100 @@ async fn test_try_execute_proposal_with_multiple_options_and_full_deny() {
976976
GovernanceError::InvalidStateCannotExecuteTransaction.into()
977977
);
978978
}
979+
980+
#[tokio::test]
981+
async fn test_create_proposal_with_10_options_and_cast_vote() {
982+
// Arrange
983+
let mut governance_test = GovernanceProgramTest::start_new().await;
984+
985+
let realm_cookie = governance_test.with_realm().await;
986+
let governed_account_cookie = governance_test.with_governed_account().await;
987+
988+
let token_owner_record_cookie = governance_test
989+
.with_community_token_deposit(&realm_cookie)
990+
.await
991+
.unwrap();
992+
993+
let mut governance_cookie = governance_test
994+
.with_governance(
995+
&realm_cookie,
996+
&governed_account_cookie,
997+
&token_owner_record_cookie,
998+
)
999+
.await
1000+
.unwrap();
1001+
1002+
let options_count = 10;
1003+
1004+
let options: Vec<String> = (0..options_count)
1005+
.into_iter()
1006+
.map(|n| format!("option {:?}", n))
1007+
.collect();
1008+
1009+
let options_len = options.len() as u8;
1010+
1011+
let proposal_cookie = governance_test
1012+
.with_multi_option_proposal(
1013+
&token_owner_record_cookie,
1014+
&mut governance_cookie,
1015+
options,
1016+
false,
1017+
VoteType::MultiChoice {
1018+
max_winning_options: options_len,
1019+
max_voter_options: options_len,
1020+
},
1021+
)
1022+
.await
1023+
.unwrap();
1024+
1025+
governance_test
1026+
.sign_off_proposal_by_owner(&proposal_cookie, &token_owner_record_cookie)
1027+
.await
1028+
.unwrap();
1029+
1030+
let vote = Vote::Approve(
1031+
(0..options_count)
1032+
.into_iter()
1033+
.map(|_| VoteChoice {
1034+
rank: 0,
1035+
weight_percentage: 100,
1036+
})
1037+
.collect(),
1038+
);
1039+
1040+
// Act
1041+
governance_test
1042+
.with_cast_multi_option_vote(&proposal_cookie, &token_owner_record_cookie, vote)
1043+
.await
1044+
.unwrap();
1045+
1046+
let clock = governance_test.bench.get_clock().await;
1047+
1048+
governance_test
1049+
.advance_clock_past_timestamp(
1050+
governance_cookie.account.config.max_voting_time as i64 + clock.unix_timestamp,
1051+
)
1052+
.await;
1053+
1054+
governance_test
1055+
.finalize_vote(&realm_cookie, &proposal_cookie, None)
1056+
.await
1057+
.unwrap();
1058+
1059+
// Assert
1060+
1061+
let proposal_account = governance_test
1062+
.get_proposal_account(&proposal_cookie.address)
1063+
.await;
1064+
1065+
assert_eq!(
1066+
proposal_account.vote_type,
1067+
VoteType::MultiChoice {
1068+
max_winning_options: options_len,
1069+
max_voter_options: options_len,
1070+
}
1071+
);
1072+
assert!(!proposal_account.deny_vote_weight.is_some());
1073+
1074+
assert_eq!(ProposalState::Completed, proposal_account.state);
1075+
}

0 commit comments

Comments
 (0)