From b79c4c4cab38a940968e7cdbd0fb8f37be058008 Mon Sep 17 00:00:00 2001 From: "Sebastian.Bor" Date: Fri, 18 Nov 2022 21:17:50 +0000 Subject: [PATCH 01/16] wip: add voting_cool_off_time --- .../chat/program/tests/program_test/mod.rs | 3 +- .../src/processor/process_cast_vote.rs | 2 +- governance/program/src/state/governance.rs | 31 +++++++++++++------ governance/program/src/state/proposal.rs | 28 ++++++++++++++--- governance/program/tests/program_test/mod.rs | 3 +- 5 files changed, 50 insertions(+), 17 deletions(-) diff --git a/governance/chat/program/tests/program_test/mod.rs b/governance/chat/program/tests/program_test/mod.rs index ff177d0b8cd..61f10563451 100644 --- a/governance/chat/program/tests/program_test/mod.rs +++ b/governance/chat/program/tests/program_test/mod.rs @@ -196,7 +196,8 @@ impl GovernanceChatProgramTest { council_veto_vote_threshold: VoteThreshold::YesVotePercentage(50), council_vote_tipping: spl_governance::state::enums::VoteTipping::Strict, community_veto_vote_threshold: VoteThreshold::YesVotePercentage(55), - reserved: [0; 5], + reserved: 0, + voting_cool_off_time: 1, }; let token_owner_record_address = get_token_owner_record_address( diff --git a/governance/program/src/processor/process_cast_vote.rs b/governance/program/src/processor/process_cast_vote.rs index 043efa4f736..2dfcdaecfdb 100644 --- a/governance/program/src/processor/process_cast_vote.rs +++ b/governance/program/src/processor/process_cast_vote.rs @@ -82,7 +82,7 @@ pub fn process_cast_vote( governance_info.key, &proposal_governing_token_mint, )?; - proposal_data.assert_can_cast_vote(&governance_data.config, clock.unix_timestamp)?; + proposal_data.assert_can_cast_vote(&governance_data.config, &vote, clock.unix_timestamp)?; let mut voter_token_owner_record_data = get_token_owner_record_data_for_realm_and_governing_mint( diff --git a/governance/program/src/state/governance.rs b/governance/program/src/state/governance.rs index ef7e5c5a519..5f36fc06156 100644 --- a/governance/program/src/state/governance.rs +++ b/governance/program/src/state/governance.rs @@ -55,8 +55,11 @@ pub struct GovernanceConfig { /// The threshold for Community Veto votes pub community_veto_vote_threshold: VoteThreshold, + /// Voting cool of time + pub voting_cool_off_time: u32, + /// Reserved space for future versions - pub reserved: [u8; 5], + pub reserved: u8, } /// Governance Account @@ -178,7 +181,7 @@ impl GovernanceV2 { } else if is_governance_v1_account_type(&self.account_type) { // V1 account can't be resized and we have to translate it back to the original format - // If reserved_v2 is used it must be individually assesed for v1 backward compatibility impact + // If reserved_v2 is used it must be individually assessed for v1 backward compatibility impact if self.reserved_v2 != [0; 128] { panic!("Extended data not supported by GovernanceV1") } @@ -310,7 +313,8 @@ pub fn get_governance_data( governance_data.config.community_veto_vote_threshold = VoteThreshold::Disabled; // Reset reserved space previously used for voting_proposal_count - governance_data.config.reserved = [0; 5]; + governance_data.config.voting_cool_off_time = 0; + governance_data.config.reserved = 0; } Ok(governance_data) @@ -516,7 +520,8 @@ mod test { council_veto_vote_threshold: VoteThreshold::YesVotePercentage(50), council_vote_tipping: VoteTipping::Strict, community_veto_vote_threshold: VoteThreshold::YesVotePercentage(40), - reserved: [0; 5], + reserved: 0, + voting_cool_off_time: 2, } } @@ -621,7 +626,8 @@ mod test { governance.config.community_vote_tipping ); - assert_eq!(governance.config.reserved, [0; 5]); + assert_eq!(governance.config.reserved, 0); + assert_eq!(governance.config.voting_cool_off_time, 0); } #[test] @@ -638,7 +644,8 @@ mod test { min_council_weight_to_create_proposal: 1, council_vote_tipping: VoteTipping::Strict, community_veto_vote_threshold: VoteThreshold::YesVotePercentage(1), - reserved: [0; 5], + reserved: 0, + voting_cool_off_time: 1, }; // Act @@ -664,7 +671,8 @@ mod test { min_council_weight_to_create_proposal: 1, council_vote_tipping: VoteTipping::Strict, community_veto_vote_threshold: VoteThreshold::YesVotePercentage(1), - reserved: [0; 5], + reserved: 0, + voting_cool_off_time: 1, }; // Act @@ -690,7 +698,8 @@ mod test { min_council_weight_to_create_proposal: 1, council_vote_tipping: VoteTipping::Strict, community_veto_vote_threshold: VoteThreshold::YesVotePercentage(1), - reserved: [0; 5], + reserved: 0, + voting_cool_off_time: 1, }; // Act @@ -716,7 +725,8 @@ mod test { min_council_weight_to_create_proposal: 1, council_vote_tipping: VoteTipping::Strict, community_veto_vote_threshold: VoteThreshold::YesVotePercentage(1), - reserved: [0; 5], + reserved: 0, + voting_cool_off_time: 1, }; // Act @@ -742,7 +752,8 @@ mod test { min_council_weight_to_create_proposal: 1, community_veto_vote_threshold: VoteThreshold::YesVotePercentage(0), community_vote_tipping: VoteTipping::Strict, - reserved: [0; 5], + reserved: 0, + voting_cool_off_time: 1, }; // Act diff --git a/governance/program/src/state/proposal.rs b/governance/program/src/state/proposal.rs index b0f15823651..3b5b4b445e6 100644 --- a/governance/program/src/state/proposal.rs +++ b/governance/program/src/state/proposal.rs @@ -264,6 +264,7 @@ impl ProposalV2 { pub fn assert_can_cast_vote( &self, config: &GovernanceConfig, + vote: &Vote, current_unix_timestamp: UnixTimestamp, ) -> Result<(), ProgramError> { self.assert_is_voting_state() @@ -274,7 +275,21 @@ impl ProposalV2 { return Err(GovernanceError::ProposalVotingTimeExpired.into()); } - Ok(()) + match vote { + // Once in the voting cool off time approving votes are no longer accepted + // Abstain is considered as positive vote because when attendance quorum is used it can tip the scales + Vote::Approve(_) | Vote::Abstain => { + if self.vote_end_time(config) - (config.voting_cool_off_time as i64) + < current_unix_timestamp + { + Err(GovernanceError::ProposalVotingTimeExpired.into()) + } else { + Ok(()) + } + } + // Within voting cool off time only counter votes are allowed + Vote::Deny | Vote::Veto => Ok(()), + } } /// Vote end time determined by the configured max_voting_time period @@ -1177,7 +1192,8 @@ mod test { council_veto_vote_threshold: VoteThreshold::YesVotePercentage(50), council_vote_tipping: VoteTipping::Strict, community_veto_vote_threshold: VoteThreshold::YesVotePercentage(40), - reserved: [0; 5], + reserved: 0, + voting_cool_off_time: 0, } } @@ -2227,9 +2243,11 @@ mod test { let current_timestamp = proposal.voting_at.unwrap() + governance_config.max_voting_time as i64 + 1; + let vote = Vote::Approve(vec![]); + // Act let err = proposal - .assert_can_cast_vote(&governance_config, current_timestamp) + .assert_can_cast_vote(&governance_config, &vote, current_timestamp) .err() .unwrap(); @@ -2247,8 +2265,10 @@ mod test { let current_timestamp = proposal.voting_at.unwrap() + governance_config.max_voting_time as i64; + let vote = Vote::Approve(vec![]); + // Act - let result = proposal.assert_can_cast_vote(&governance_config, current_timestamp); + let result = proposal.assert_can_cast_vote(&governance_config, &vote, current_timestamp); // Assert assert_eq!(result, Ok(())); diff --git a/governance/program/tests/program_test/mod.rs b/governance/program/tests/program_test/mod.rs index d5404b32cb9..954b6e95fde 100644 --- a/governance/program/tests/program_test/mod.rs +++ b/governance/program/tests/program_test/mod.rs @@ -1415,7 +1415,8 @@ impl GovernanceProgramTest { council_veto_vote_threshold: VoteThreshold::YesVotePercentage(55), council_vote_tipping: spl_governance::state::enums::VoteTipping::Strict, community_veto_vote_threshold: VoteThreshold::YesVotePercentage(80), - reserved: [0; 5], + reserved: 0, + voting_cool_off_time: 0, } } From 0fa9dfb1f1b14ba03005c5cd265ea1294d2a7c9b Mon Sep 17 00:00:00 2001 From: "Sebastian.Bor" Date: Fri, 18 Nov 2022 22:06:47 +0000 Subject: [PATCH 02/16] feat: Default voting_cool_off_time to 1h for 10h or longer votes --- governance/program/src/state/governance.rs | 78 ++++++++++++++++++++-- 1 file changed, 74 insertions(+), 4 deletions(-) diff --git a/governance/program/src/state/governance.rs b/governance/program/src/state/governance.rs index 5f36fc06156..816c9827168 100644 --- a/governance/program/src/state/governance.rs +++ b/governance/program/src/state/governance.rs @@ -291,13 +291,14 @@ pub fn get_governance_data( // In previous versions of spl-gov (< 3) we had config.proposal_cool_off_time:u32 which was unused and always 0 // In version 3.0.0 proposal_cool_off_time was replaced with council_vote_threshold:VoteThreshold and council_veto_vote_threshold:VoteThreshold - // // If we read a legacy account then council_vote_threshold == VoteThreshold::YesVotePercentage(0) - // and we coerce it to be equal to community_vote_threshold which was used for both council and community thresholds before // // Note: assert_is_valid_governance_config() prevents setting council_vote_threshold to VoteThreshold::YesVotePercentage(0) // which gives as guarantee that it is a legacy account layout set with proposal_cool_off_time = 0 + // + // Note: All the settings below are one time config migration from V1 & V2 account data to V3 if governance_data.config.council_vote_threshold == VoteThreshold::YesVotePercentage(0) { + // Set council_vote_threshold to community_vote_threshold which was used for both council and community thresholds before governance_data.config.council_vote_threshold = governance_data.config.community_vote_threshold.clone(); @@ -312,8 +313,16 @@ pub fn get_governance_data( // For legacy accounts set the community Veto threshold to Disabled governance_data.config.community_veto_vote_threshold = VoteThreshold::Disabled; - // Reset reserved space previously used for voting_proposal_count - governance_data.config.voting_cool_off_time = 0; + // Reset voting_cool_off_time and reserved space previously used for voting_proposal_count + + // Default voting_cool_off_time to 1h for max_voting_time >= 10h + governance_data.config.voting_cool_off_time = + if governance_data.config.max_voting_time >= 36000 { + 3600 + } else { + 0 + }; + governance_data.config.reserved = 0; } @@ -657,6 +666,67 @@ mod test { assert_eq!(err, GovernanceError::InvalidVoteThresholdPercentage.into()); } + #[test] + fn test_migrate_governance_config_from_legacy_data_to_v3() { + // Arrange + let mut governance_legacy_data = create_test_governance(); + + governance_legacy_data.config.council_vote_threshold = VoteThreshold::YesVotePercentage(0); + governance_legacy_data.config.council_veto_vote_threshold = + VoteThreshold::YesVotePercentage(0); + governance_legacy_data.config.council_vote_tipping = VoteTipping::Disabled; + governance_legacy_data.config.community_veto_vote_threshold = + VoteThreshold::YesVotePercentage(0); + governance_legacy_data.config.voting_cool_off_time = 1; + governance_legacy_data.config.max_voting_time = 36000; + + let mut legacy_data = vec![]; + governance_legacy_data.serialize(&mut legacy_data).unwrap(); + + let program_id = Pubkey::new_unique(); + + let info_key = Pubkey::new_unique(); + let mut lamports = 10u64; + + let legacy_account_info = AccountInfo::new( + &info_key, + false, + false, + &mut lamports, + &mut legacy_data[..], + &program_id, + false, + Epoch::default(), + ); + // Act + let governance_v3 = get_governance_data(&program_id, &legacy_account_info).unwrap(); + + // Assert + assert_eq!( + governance_v3.config.council_vote_threshold, + VoteThreshold::YesVotePercentage(60) + ); + + assert_eq!( + governance_v3.config.council_veto_vote_threshold, + VoteThreshold::YesVotePercentage(60) + ); + + assert_eq!( + governance_v3.config.community_veto_vote_threshold, + VoteThreshold::Disabled + ); + + assert_eq!( + governance_v3.config.council_vote_tipping, + VoteTipping::Strict + ); + + assert_eq!(governance_v3.config.voting_cool_off_time, 3600); + + assert_eq!(governance_v3.config.reserved, 0); + } + #[test] fn test_assert_config_invalid_with_community_zero_yes_vote_threshold() { // Arrange From e064b4e2241705cb29d9130b4b82b7e10a175945 Mon Sep 17 00:00:00 2001 From: "Sebastian.Bor" Date: Fri, 18 Nov 2022 22:32:45 +0000 Subject: [PATCH 03/16] wip: Assert valid voting cool of time --- governance/program/src/error.rs | 8 ++ governance/program/src/state/governance.rs | 8 ++ governance/program/src/state/proposal.rs | 90 +++++++++++++++++++++- 3 files changed, 105 insertions(+), 1 deletion(-) diff --git a/governance/program/src/error.rs b/governance/program/src/error.rs index 7bd1445852d..3d119ee70ad 100644 --- a/governance/program/src/error.rs +++ b/governance/program/src/error.rs @@ -438,6 +438,14 @@ pub enum GovernanceError { /// Voter weight threshold disabled #[error("Voter weight threshold disabled")] VoterWeightThresholdDisabled, // 605 + + /// Vote not allowed in cool off time + #[error("Vote not allowed in cool off time")] + VoteNotAllowedInCoolOffTime, // 606 + + /// Invalid voting cool off time + #[error("Invalid voting cool off time")] + InvalidVotingCoolOffTime, // 607 } impl PrintProgramError for GovernanceError { diff --git a/governance/program/src/state/governance.rs b/governance/program/src/state/governance.rs index 816c9827168..4d649ef9c08 100644 --- a/governance/program/src/state/governance.rs +++ b/governance/program/src/state/governance.rs @@ -491,6 +491,14 @@ pub fn assert_is_valid_governance_config( return Err(GovernanceError::AtLeastOneVoteThresholdRequired.into()); } + if governance_config.voting_cool_off_time > governance_config.max_voting_time { + return Err(GovernanceError::InvalidVotingCoolOffTime.into()); + } + + if governance_config.reserved != 0 { + return Err(GovernanceError::ReservedBufferMustBeEmpty.into()); + } + Ok(()) } diff --git a/governance/program/src/state/proposal.rs b/governance/program/src/state/proposal.rs index 3b5b4b445e6..37795145577 100644 --- a/governance/program/src/state/proposal.rs +++ b/governance/program/src/state/proposal.rs @@ -282,7 +282,7 @@ impl ProposalV2 { if self.vote_end_time(config) - (config.voting_cool_off_time as i64) < current_unix_timestamp { - Err(GovernanceError::ProposalVotingTimeExpired.into()) + Err(GovernanceError::VoteNotAllowedInCoolOffTime.into()) } else { Ok(()) } @@ -2274,6 +2274,94 @@ mod test { assert_eq!(result, Ok(())); } + #[test] + pub fn test_assert_can_vote_approve_before_voting_cool_off_time() { + // Arrange + let mut proposal = create_test_proposal(); + proposal.state = ProposalState::Voting; + + let mut governance_config = create_test_governance_config(); + governance_config.voting_cool_off_time = 2; + + let current_timestamp = proposal.voting_at.unwrap() + + governance_config.max_voting_time as i64 + - governance_config.voting_cool_off_time as i64; + + let vote = Vote::Approve(vec![]); + + // Act + let result = proposal.assert_can_cast_vote(&governance_config, &vote, current_timestamp); + + // Assert + assert_eq!(result, Ok(())); + } + + #[test] + pub fn test_assert_cannot_vote_approve_within_voting_cool_off_time() { + // Arrange + let mut proposal = create_test_proposal(); + proposal.state = ProposalState::Voting; + + let mut governance_config = create_test_governance_config(); + governance_config.voting_cool_off_time = 2; + + let current_timestamp = + proposal.voting_at.unwrap() + governance_config.max_voting_time as i64 - 1; + + let vote = Vote::Approve(vec![]); + + // Act + let err = proposal + .assert_can_cast_vote(&governance_config, &vote, current_timestamp) + .err() + .unwrap(); + + // Assert + assert_eq!(err, GovernanceError::VoteNotAllowedInCoolOffTime.into()); + } + + #[test] + pub fn test_assert_can_vote_veto_within_voting_cool_off_time() { + // Arrange + let mut proposal = create_test_proposal(); + proposal.state = ProposalState::Voting; + + let mut governance_config = create_test_governance_config(); + governance_config.voting_cool_off_time = 2; + + let current_timestamp = + proposal.voting_at.unwrap() + governance_config.max_voting_time as i64 - 1; + + let vote = Vote::Veto; + + // Act + let result = proposal.assert_can_cast_vote(&governance_config, &vote, current_timestamp); + + // Assert + assert_eq!(result, Ok(())); + } + + #[test] + pub fn test_assert_can_vote_deny_within_voting_cool_off_time() { + // Arrange + let mut proposal = create_test_proposal(); + proposal.state = ProposalState::Voting; + + let mut governance_config = create_test_governance_config(); + governance_config.voting_cool_off_time = 2; + + let current_timestamp = + proposal.voting_at.unwrap() + governance_config.max_voting_time as i64 - 1; + + let vote = Vote::Deny; + + // Act + let result = proposal.assert_can_cast_vote(&governance_config, &vote, current_timestamp); + + // Assert + assert_eq!(result, Ok(())); + } + #[test] pub fn test_assert_valid_vote_with_deny_vote_for_survey_only_proposal_error() { // Arrange From cbc2fbcf76555ac03c219189737f705552a02d24 Mon Sep 17 00:00:00 2001 From: "Sebastian.Bor" Date: Fri, 18 Nov 2022 22:55:03 +0000 Subject: [PATCH 04/16] chore: cleanup GovernanceConfig tests --- governance/program/src/state/governance.rs | 83 ++++------------------ 1 file changed, 12 insertions(+), 71 deletions(-) diff --git a/governance/program/src/state/governance.rs b/governance/program/src/state/governance.rs index 4d649ef9c08..5027dca33f1 100644 --- a/governance/program/src/state/governance.rs +++ b/governance/program/src/state/governance.rs @@ -491,7 +491,7 @@ pub fn assert_is_valid_governance_config( return Err(GovernanceError::AtLeastOneVoteThresholdRequired.into()); } - if governance_config.voting_cool_off_time > governance_config.max_voting_time { + if governance_config.voting_cool_off_time >= governance_config.max_voting_time { return Err(GovernanceError::InvalidVotingCoolOffTime.into()); } @@ -650,20 +650,8 @@ mod test { #[test] fn test_assert_config_invalid_with_council_zero_yes_vote_threshold() { // Arrange - let governance_config = GovernanceConfig { - community_vote_threshold: VoteThreshold::YesVotePercentage(1), - min_community_weight_to_create_proposal: 1, - min_transaction_hold_up_time: 1, - max_voting_time: 1, - community_vote_tipping: VoteTipping::Strict, - council_vote_threshold: VoteThreshold::YesVotePercentage(0), - council_veto_vote_threshold: VoteThreshold::YesVotePercentage(1), - min_council_weight_to_create_proposal: 1, - council_vote_tipping: VoteTipping::Strict, - community_veto_vote_threshold: VoteThreshold::YesVotePercentage(1), - reserved: 0, - voting_cool_off_time: 1, - }; + let mut governance_config = create_test_governance_config(); + governance_config.council_vote_threshold = VoteThreshold::YesVotePercentage(0); // Act let err = assert_is_valid_governance_config(&governance_config) @@ -738,20 +726,8 @@ mod test { #[test] fn test_assert_config_invalid_with_community_zero_yes_vote_threshold() { // Arrange - let governance_config = GovernanceConfig { - community_vote_threshold: VoteThreshold::YesVotePercentage(0), - min_community_weight_to_create_proposal: 1, - min_transaction_hold_up_time: 1, - max_voting_time: 1, - community_vote_tipping: VoteTipping::Strict, - council_vote_threshold: VoteThreshold::YesVotePercentage(1), - council_veto_vote_threshold: VoteThreshold::YesVotePercentage(1), - min_council_weight_to_create_proposal: 1, - council_vote_tipping: VoteTipping::Strict, - community_veto_vote_threshold: VoteThreshold::YesVotePercentage(1), - reserved: 0, - voting_cool_off_time: 1, - }; + let mut governance_config = create_test_governance_config(); + governance_config.community_vote_threshold = VoteThreshold::YesVotePercentage(0); // Act let err = assert_is_valid_governance_config(&governance_config) @@ -765,20 +741,9 @@ mod test { #[test] fn test_assert_config_invalid_with_all_vote_thresholds_disabled() { // Arrange - let governance_config = GovernanceConfig { - community_vote_threshold: VoteThreshold::Disabled, - min_community_weight_to_create_proposal: 1, - min_transaction_hold_up_time: 1, - max_voting_time: 1, - community_vote_tipping: VoteTipping::Strict, - council_vote_threshold: VoteThreshold::Disabled, - council_veto_vote_threshold: VoteThreshold::YesVotePercentage(1), - min_council_weight_to_create_proposal: 1, - council_vote_tipping: VoteTipping::Strict, - community_veto_vote_threshold: VoteThreshold::YesVotePercentage(1), - reserved: 0, - voting_cool_off_time: 1, - }; + let mut governance_config = create_test_governance_config(); + governance_config.community_vote_threshold = VoteThreshold::Disabled; + governance_config.council_vote_threshold = VoteThreshold::Disabled; // Act let err = assert_is_valid_governance_config(&governance_config) @@ -792,20 +757,8 @@ mod test { #[test] fn test_assert_config_invalid_with_council_zero_yes_veto_vote_threshold() { // Arrange - let governance_config = GovernanceConfig { - community_vote_threshold: VoteThreshold::YesVotePercentage(1), - min_community_weight_to_create_proposal: 1, - min_transaction_hold_up_time: 1, - max_voting_time: 1, - community_vote_tipping: VoteTipping::Strict, - council_vote_threshold: VoteThreshold::YesVotePercentage(1), - council_veto_vote_threshold: VoteThreshold::YesVotePercentage(0), - min_council_weight_to_create_proposal: 1, - council_vote_tipping: VoteTipping::Strict, - community_veto_vote_threshold: VoteThreshold::YesVotePercentage(1), - reserved: 0, - voting_cool_off_time: 1, - }; + let mut governance_config = create_test_governance_config(); + governance_config.council_veto_vote_threshold = VoteThreshold::YesVotePercentage(0); // Act let err = assert_is_valid_governance_config(&governance_config) @@ -819,20 +772,8 @@ mod test { #[test] fn test_assert_config_invalid_with_community_zero_yes_veto_vote_threshold() { // Arrange - let governance_config = GovernanceConfig { - community_vote_threshold: VoteThreshold::YesVotePercentage(1), - min_community_weight_to_create_proposal: 1, - min_transaction_hold_up_time: 1, - max_voting_time: 1, - council_vote_tipping: VoteTipping::Strict, - council_vote_threshold: VoteThreshold::YesVotePercentage(1), - council_veto_vote_threshold: VoteThreshold::YesVotePercentage(1), - min_council_weight_to_create_proposal: 1, - community_veto_vote_threshold: VoteThreshold::YesVotePercentage(0), - community_vote_tipping: VoteTipping::Strict, - reserved: 0, - voting_cool_off_time: 1, - }; + let mut governance_config = create_test_governance_config(); + governance_config.community_veto_vote_threshold = VoteThreshold::YesVotePercentage(0); // Act let err = assert_is_valid_governance_config(&governance_config) From 84d6d2e920397dee83261a80b3aa84904080e809 Mon Sep 17 00:00:00 2001 From: "Sebastian.Bor" Date: Fri, 18 Nov 2022 22:59:30 +0000 Subject: [PATCH 05/16] chore: Rename vote_end_time() to expected_vote_end_time() --- governance/program/src/state/proposal.rs | 12 ++++++------ governance/program/tests/process_finalize_vote.rs | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/governance/program/src/state/proposal.rs b/governance/program/src/state/proposal.rs index 37795145577..074c428c899 100644 --- a/governance/program/src/state/proposal.rs +++ b/governance/program/src/state/proposal.rs @@ -279,7 +279,7 @@ impl ProposalV2 { // Once in the voting cool off time approving votes are no longer accepted // Abstain is considered as positive vote because when attendance quorum is used it can tip the scales Vote::Approve(_) | Vote::Abstain => { - if self.vote_end_time(config) - (config.voting_cool_off_time as i64) + if self.expected_vote_end_time(config) - (config.voting_cool_off_time as i64) < current_unix_timestamp { Err(GovernanceError::VoteNotAllowedInCoolOffTime.into()) @@ -292,8 +292,8 @@ impl ProposalV2 { } } - /// Vote end time determined by the configured max_voting_time period - pub fn vote_end_time(&self, config: &GovernanceConfig) -> UnixTimestamp { + /// Expected vote end time determined by the configured max_voting_time period + pub fn expected_vote_end_time(&self, config: &GovernanceConfig) -> UnixTimestamp { self.voting_at .unwrap() .checked_add(config.max_voting_time as i64) @@ -307,7 +307,7 @@ impl ProposalV2 { current_unix_timestamp: UnixTimestamp, ) -> bool { // Check if we passed vote_end_time - self.vote_end_time(config) < current_unix_timestamp + self.expected_vote_end_time(config) < current_unix_timestamp } /// Checks if Proposal can be finalized @@ -339,7 +339,7 @@ impl ProposalV2 { self.assert_can_finalize_vote(config, current_unix_timestamp)?; self.state = self.resolve_final_vote_state(max_voter_weight, vote_threshold)?; - self.voting_completed_at = Some(self.vote_end_time(config)); + self.voting_completed_at = Some(self.expected_vote_end_time(config)); // Capture vote params to correctly display historical results self.max_vote_weight = Some(max_voter_weight); @@ -1668,7 +1668,7 @@ mod test { // Assert assert_eq!(proposal.state,test_case.expected_finalized_state,"CASE: {:?}",test_case); assert_eq!( - Some(proposal.vote_end_time(&governance_config)), + Some(proposal.expected_vote_end_time(&governance_config)), proposal.voting_completed_at ); diff --git a/governance/program/tests/process_finalize_vote.rs b/governance/program/tests/process_finalize_vote.rs index 57889866e24..0b6c54828da 100644 --- a/governance/program/tests/process_finalize_vote.rs +++ b/governance/program/tests/process_finalize_vote.rs @@ -82,7 +82,7 @@ async fn test_finalize_vote_to_succeeded() { assert_eq!(proposal_account.state, ProposalState::Succeeded); assert_eq!( - Some(proposal_account.vote_end_time(&governance_cookie.account.config)), + Some(proposal_account.expected_vote_end_time(&governance_cookie.account.config)), proposal_account.voting_completed_at ); @@ -379,7 +379,7 @@ async fn test_finalize_council_vote() { assert_eq!(proposal_account.state, ProposalState::Succeeded); assert_eq!( - Some(proposal_account.vote_end_time(&governance_cookie.account.config)), + Some(proposal_account.expected_vote_end_time(&governance_cookie.account.config)), proposal_account.voting_completed_at ); From 2f1518a5535f5ba91dab97fee7a25b438e364020 Mon Sep 17 00:00:00 2001 From: "Sebastian.Bor" Date: Fri, 18 Nov 2022 23:15:23 +0000 Subject: [PATCH 06/16] chore: test_cast_council_veto_vote_within_cool_off_time --- governance/program/tests/use_veto_vote.rs | 66 +++++++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/governance/program/tests/use_veto_vote.rs b/governance/program/tests/use_veto_vote.rs index 05257b77f58..ffb09f82365 100644 --- a/governance/program/tests/use_veto_vote.rs +++ b/governance/program/tests/use_veto_vote.rs @@ -962,3 +962,69 @@ async fn test_veto_vote_with_community_max_voter_weight_addin_and_veto_not_tippe assert_eq!(proposal_account.state, ProposalState::Voting); } + +#[tokio::test] +async fn test_cast_council_veto_vote_within_cool_off_time() { + // Arrange + let mut governance_test = GovernanceProgramTest::start_new().await; + + let realm_cookie = governance_test.with_realm().await; + let governed_account_cookie = governance_test.with_governed_account().await; + + let token_owner_record_cookie = governance_test + .with_council_token_deposit(&realm_cookie) + .await + .unwrap(); + + // Mint extra council tokens for total supply of 120 + governance_test.mint_council_tokens(&realm_cookie, 20).await; + + // Set cool off time to start in the middle of the voting time + let mut governance_config = governance_test.get_default_governance_config(); + governance_config.max_voting_time = 100; + governance_config.voting_cool_off_time = 50; + + let mut governance_cookie = governance_test + .with_governance_using_config( + &realm_cookie, + &governed_account_cookie, + &token_owner_record_cookie, + &governance_config, + ) + .await + .unwrap(); + + let proposal_owner_record_cookie = governance_test + .with_community_token_deposit(&realm_cookie) + .await + .unwrap(); + + let proposal_cookie = governance_test + .with_signed_off_proposal(&proposal_owner_record_cookie, &mut governance_cookie) + .await + .unwrap(); + + // Advance timestamp past max_voting_time + let clock = governance_test.bench.get_clock().await; + + governance_test + .advance_clock_past_timestamp( + clock.unix_timestamp + + (governance_cookie.account.config.max_voting_time + - governance_config.voting_cool_off_time) as i64, + ) + .await; + + // Act + governance_test + .with_cast_vote(&proposal_cookie, &token_owner_record_cookie, Vote::Veto) + .await + .unwrap(); + + // Assert + let proposal_account = governance_test + .get_proposal_account(&proposal_cookie.address) + .await; + + assert_eq!(proposal_account.state, ProposalState::Vetoed); +} From 9fef20423bd2e46b1b806bf487e072b6d1cbc172 Mon Sep 17 00:00:00 2001 From: "Sebastian.Bor" Date: Fri, 18 Nov 2022 23:22:44 +0000 Subject: [PATCH 07/16] chore: test_cast_approve_vote_with_cannot_vote_in_cool_off_time_error --- governance/program/src/error.rs | 6 +- governance/program/src/state/realm_config.rs | 2 +- governance/program/tests/process_cast_vote.rs | 57 +++++++++++++++++++ .../program/tests/process_set_realm_config.rs | 2 +- 4 files changed, 62 insertions(+), 5 deletions(-) diff --git a/governance/program/src/error.rs b/governance/program/src/error.rs index 3d119ee70ad..3256fb14999 100644 --- a/governance/program/src/error.rs +++ b/governance/program/src/error.rs @@ -431,9 +431,9 @@ pub enum GovernanceError { #[error("Invalid GoverningToken source")] InvalidGoverningTokenSource, // 603 - /// Cannot change community TokenType to Memebership - #[error("Cannot change community TokenType to Memebership")] - CannotChangeCommunityTokenTypeToMemebership, // 604 + /// Cannot change community TokenType to Membership + #[error("Cannot change community TokenType to Membership")] + CannotChangeCommunityTokenTypeToMembership, // 604 /// Voter weight threshold disabled #[error("Voter weight threshold disabled")] diff --git a/governance/program/src/state/realm_config.rs b/governance/program/src/state/realm_config.rs index e8e7ab8baf5..af0bfbdbdf6 100644 --- a/governance/program/src/state/realm_config.rs +++ b/governance/program/src/state/realm_config.rs @@ -211,7 +211,7 @@ impl RealmConfigAccount { && realm_config_args.community_token_config_args.token_type == GoverningTokenType::Membership { - return Err(GovernanceError::CannotChangeCommunityTokenTypeToMemebership.into()); + return Err(GovernanceError::CannotChangeCommunityTokenTypeToMembership.into()); } Ok(()) diff --git a/governance/program/tests/process_cast_vote.rs b/governance/program/tests/process_cast_vote.rs index b16d111ed96..0e3e77efb54 100644 --- a/governance/program/tests/process_cast_vote.rs +++ b/governance/program/tests/process_cast_vote.rs @@ -1550,3 +1550,60 @@ async fn test_cast_vote_with_strict_tipping_and_inflated_max_vote_weight() { // max_vote_weight should be coerced from 60 to 100 assert_eq!(proposal_account.max_vote_weight, Some(100)) } + +#[tokio::test] +async fn test_cast_approve_vote_with_cannot_vote_in_cool_off_time_error() { + // Arrange + let mut governance_test = GovernanceProgramTest::start_new().await; + + let realm_cookie = governance_test.with_realm().await; + let governed_account_cookie = governance_test.with_governed_account().await; + + let token_owner_record_cookie = governance_test + .with_community_token_deposit(&realm_cookie) + .await + .unwrap(); + + // Set cool off time to start in the middle of the voting time + let mut governance_config = governance_test.get_default_governance_config(); + governance_config.max_voting_time = 100; + governance_config.voting_cool_off_time = 50; + + let mut governance_cookie = governance_test + .with_governance_using_config( + &realm_cookie, + &governed_account_cookie, + &token_owner_record_cookie, + &governance_config, + ) + .await + .unwrap(); + + let proposal_cookie = governance_test + .with_signed_off_proposal(&token_owner_record_cookie, &mut governance_cookie) + .await + .unwrap(); + + // Advance timestamp past max_voting_time + let clock = governance_test.bench.get_clock().await; + + governance_test + .advance_clock_past_timestamp( + clock.unix_timestamp + + (governance_cookie.account.config.max_voting_time + - governance_config.voting_cool_off_time) as i64, + ) + .await; + + // Act + + let err = governance_test + .with_cast_yes_no_vote(&proposal_cookie, &token_owner_record_cookie, YesNoVote::Yes) + .await + .err() + .unwrap(); + + // Assert + + assert_eq!(err, GovernanceError::VoteNotAllowedInCoolOffTime.into()); +} diff --git a/governance/program/tests/process_set_realm_config.rs b/governance/program/tests/process_set_realm_config.rs index a906ab4af76..d87c19af46f 100644 --- a/governance/program/tests/process_set_realm_config.rs +++ b/governance/program/tests/process_set_realm_config.rs @@ -231,7 +231,7 @@ async fn test_set_realm_config_with_liquid_community_token_cannot_be_changed_to_ // Assert assert_eq!( err, - GovernanceError::CannotChangeCommunityTokenTypeToMemebership.into() + GovernanceError::CannotChangeCommunityTokenTypeToMembership.into() ); } From da8e127c3f47079f632d42618cb8962598172943 Mon Sep 17 00:00:00 2001 From: "Sebastian.Bor" Date: Fri, 18 Nov 2022 23:32:46 +0000 Subject: [PATCH 08/16] chore: test_change_yes_vote_to_no_within_cool_off_time --- governance/program/tests/process_cast_vote.rs | 2 +- .../program/tests/process_relinquish_vote.rs | 76 +++++++++++++++++++ governance/program/tests/use_veto_vote.rs | 2 +- 3 files changed, 78 insertions(+), 2 deletions(-) diff --git a/governance/program/tests/process_cast_vote.rs b/governance/program/tests/process_cast_vote.rs index 0e3e77efb54..0c5566a529a 100644 --- a/governance/program/tests/process_cast_vote.rs +++ b/governance/program/tests/process_cast_vote.rs @@ -1584,7 +1584,7 @@ async fn test_cast_approve_vote_with_cannot_vote_in_cool_off_time_error() { .await .unwrap(); - // Advance timestamp past max_voting_time + // Advance timestamp into voting_cool_off_time let clock = governance_test.bench.get_clock().await; governance_test diff --git a/governance/program/tests/process_relinquish_vote.rs b/governance/program/tests/process_relinquish_vote.rs index c4d1f72a92e..a835d4208bb 100644 --- a/governance/program/tests/process_relinquish_vote.rs +++ b/governance/program/tests/process_relinquish_vote.rs @@ -598,3 +598,79 @@ async fn test_relinquish_and_cast_vote_in_single_transaction() { assert_eq!(vote_record_cookie.account, vote_record_account); } + +#[tokio::test] +async fn test_change_yes_vote_to_no_within_cool_off_time() { + // Arrange + let mut governance_test = GovernanceProgramTest::start_new().await; + + let realm_cookie = governance_test.with_realm().await; + let governed_account_cookie = governance_test.with_governed_account().await; + + let token_owner_record_cookie = governance_test + .with_community_token_deposit(&realm_cookie) + .await + .unwrap(); + + // Set cool off time to start in the middle of the voting time + let mut governance_config = governance_test.get_default_governance_config(); + governance_config.max_voting_time = 100; + governance_config.voting_cool_off_time = 50; + + let mut governance_cookie = governance_test + .with_governance_using_config( + &realm_cookie, + &governed_account_cookie, + &token_owner_record_cookie, + &governance_config, + ) + .await + .unwrap(); + + // Total 300 tokens + governance_test + .mint_community_tokens(&realm_cookie, 200) + .await; + + let proposal_cookie = governance_test + .with_signed_off_proposal(&token_owner_record_cookie, &mut governance_cookie) + .await + .unwrap(); + + governance_test + .with_cast_yes_no_vote(&proposal_cookie, &token_owner_record_cookie, YesNoVote::Yes) + .await + .unwrap(); + + // Advance timestamp into voting_cool_off_time + let clock = governance_test.bench.get_clock().await; + + governance_test + .advance_clock_past_timestamp( + clock.unix_timestamp + + (governance_cookie.account.config.max_voting_time + - governance_config.voting_cool_off_time) as i64, + ) + .await; + + // Act + governance_test + .relinquish_vote(&proposal_cookie, &token_owner_record_cookie) + .await + .unwrap(); + + governance_test + .with_cast_yes_no_vote(&proposal_cookie, &token_owner_record_cookie, YesNoVote::No) + .await + .unwrap(); + + // Assert + + let proposal_account = governance_test + .get_proposal_account(&proposal_cookie.address) + .await; + + assert_eq!(0, proposal_account.options[0].vote_weight); + assert_eq!(100, proposal_account.deny_vote_weight.unwrap()); + assert_eq!(ProposalState::Voting, proposal_account.state); +} diff --git a/governance/program/tests/use_veto_vote.rs b/governance/program/tests/use_veto_vote.rs index ffb09f82365..7cf43a864cf 100644 --- a/governance/program/tests/use_veto_vote.rs +++ b/governance/program/tests/use_veto_vote.rs @@ -1004,7 +1004,7 @@ async fn test_cast_council_veto_vote_within_cool_off_time() { .await .unwrap(); - // Advance timestamp past max_voting_time + // Advance timestamp into voting_cool_off_time let clock = governance_test.bench.get_clock().await; governance_test From ef697fedddda09c1202fe864fc18e31b9d88c4fb Mon Sep 17 00:00:00 2001 From: "Sebastian.Bor" Date: Sat, 19 Nov 2022 10:54:01 +0000 Subject: [PATCH 09/16] chore: Reorder GovernanceConfig fields --- governance/chat/program/tests/program_test/mod.rs | 6 +++--- governance/program/src/state/governance.rs | 6 +++--- governance/program/src/state/proposal.rs | 6 +++--- governance/program/tests/program_test/mod.rs | 6 +++--- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/governance/chat/program/tests/program_test/mod.rs b/governance/chat/program/tests/program_test/mod.rs index 61f10563451..b15a9254408 100644 --- a/governance/chat/program/tests/program_test/mod.rs +++ b/governance/chat/program/tests/program_test/mod.rs @@ -186,18 +186,18 @@ impl GovernanceChatProgramTest { let governed_account_address = Pubkey::new_unique(); let governance_config = GovernanceConfig { + community_vote_threshold: VoteThreshold::YesVotePercentage(60), min_community_weight_to_create_proposal: 5, - min_council_weight_to_create_proposal: 2, min_transaction_hold_up_time: 10, max_voting_time: 10, - community_vote_threshold: VoteThreshold::YesVotePercentage(60), community_vote_tipping: spl_governance::state::enums::VoteTipping::Strict, council_vote_threshold: VoteThreshold::YesVotePercentage(10), council_veto_vote_threshold: VoteThreshold::YesVotePercentage(50), + min_council_weight_to_create_proposal: 2, council_vote_tipping: spl_governance::state::enums::VoteTipping::Strict, community_veto_vote_threshold: VoteThreshold::YesVotePercentage(55), - reserved: 0, voting_cool_off_time: 1, + reserved: 0, }; let token_owner_record_address = get_token_owner_record_address( diff --git a/governance/program/src/state/governance.rs b/governance/program/src/state/governance.rs index 5027dca33f1..803bc3a75e0 100644 --- a/governance/program/src/state/governance.rs +++ b/governance/program/src/state/governance.rs @@ -527,18 +527,18 @@ mod test { fn create_test_governance_config() -> GovernanceConfig { GovernanceConfig { + community_vote_threshold: VoteThreshold::YesVotePercentage(60), min_community_weight_to_create_proposal: 5, - min_council_weight_to_create_proposal: 1, min_transaction_hold_up_time: 10, max_voting_time: 5, - community_vote_threshold: VoteThreshold::YesVotePercentage(60), community_vote_tipping: VoteTipping::Strict, council_vote_threshold: VoteThreshold::YesVotePercentage(60), council_veto_vote_threshold: VoteThreshold::YesVotePercentage(50), + min_council_weight_to_create_proposal: 1, council_vote_tipping: VoteTipping::Strict, community_veto_vote_threshold: VoteThreshold::YesVotePercentage(40), - reserved: 0, voting_cool_off_time: 2, + reserved: 0, } } diff --git a/governance/program/src/state/proposal.rs b/governance/program/src/state/proposal.rs index 074c428c899..9e921df9249 100644 --- a/governance/program/src/state/proposal.rs +++ b/governance/program/src/state/proposal.rs @@ -1182,18 +1182,18 @@ mod test { fn create_test_governance_config() -> GovernanceConfig { GovernanceConfig { + community_vote_threshold: VoteThreshold::YesVotePercentage(60), min_community_weight_to_create_proposal: 5, - min_council_weight_to_create_proposal: 1, min_transaction_hold_up_time: 10, max_voting_time: 5, - community_vote_threshold: VoteThreshold::YesVotePercentage(60), community_vote_tipping: VoteTipping::Strict, council_vote_threshold: VoteThreshold::YesVotePercentage(60), council_veto_vote_threshold: VoteThreshold::YesVotePercentage(50), + min_council_weight_to_create_proposal: 1, council_vote_tipping: VoteTipping::Strict, community_veto_vote_threshold: VoteThreshold::YesVotePercentage(40), - reserved: 0, voting_cool_off_time: 0, + reserved: 0, } } diff --git a/governance/program/tests/program_test/mod.rs b/governance/program/tests/program_test/mod.rs index 954b6e95fde..be32ae9c550 100644 --- a/governance/program/tests/program_test/mod.rs +++ b/governance/program/tests/program_test/mod.rs @@ -1405,18 +1405,18 @@ impl GovernanceProgramTest { pub fn get_default_governance_config(&mut self) -> GovernanceConfig { GovernanceConfig { + community_vote_threshold: VoteThreshold::YesVotePercentage(60), min_community_weight_to_create_proposal: 5, - min_council_weight_to_create_proposal: 2, min_transaction_hold_up_time: 10, max_voting_time: 10, - community_vote_threshold: VoteThreshold::YesVotePercentage(60), community_vote_tipping: spl_governance::state::enums::VoteTipping::Strict, council_vote_threshold: VoteThreshold::YesVotePercentage(80), council_veto_vote_threshold: VoteThreshold::YesVotePercentage(55), + min_council_weight_to_create_proposal: 2, council_vote_tipping: spl_governance::state::enums::VoteTipping::Strict, community_veto_vote_threshold: VoteThreshold::YesVotePercentage(80), - reserved: 0, voting_cool_off_time: 0, + reserved: 0, } } From 1e61b65c68c87cc32b15409274575d7ed52b5ca7 Mon Sep 17 00:00:00 2001 From: "Sebastian.Bor" Date: Sun, 20 Nov 2022 13:21:25 +0000 Subject: [PATCH 10/16] chore: test_finalize_vote_with_cannot_finalize_during_cool_off_time_error --- .../program/tests/process_finalize_vote.rs | 119 ++++++++++++++++++ 1 file changed, 119 insertions(+) diff --git a/governance/program/tests/process_finalize_vote.rs b/governance/program/tests/process_finalize_vote.rs index 0b6c54828da..cf3757087ab 100644 --- a/governance/program/tests/process_finalize_vote.rs +++ b/governance/program/tests/process_finalize_vote.rs @@ -390,3 +390,122 @@ async fn test_finalize_council_vote() { proposal_account.vote_threshold ); } + +#[tokio::test] +async fn test_finalize_vote_with_cannot_finalize_during_voting_time_error() { + // Arrange + let mut governance_test = GovernanceProgramTest::start_new().await; + + let realm_cookie = governance_test.with_realm().await; + let governed_account_cookie = governance_test.with_governed_account().await; + + let token_owner_record_cookie = governance_test + .with_community_token_deposit(&realm_cookie) + .await + .unwrap(); + + let mut governance_cookie = governance_test + .with_governance( + &realm_cookie, + &governed_account_cookie, + &token_owner_record_cookie, + ) + .await + .unwrap(); + + // Total 210 tokens + governance_test + .mint_community_tokens(&realm_cookie, 110) + .await; + + let proposal_cookie = governance_test + .with_signed_off_proposal(&token_owner_record_cookie, &mut governance_cookie) + .await + .unwrap(); + + governance_test + .with_cast_yes_no_vote(&proposal_cookie, &token_owner_record_cookie, YesNoVote::Yes) + .await + .unwrap(); + + governance_test.advance_clock().await; + + // Act + + let err = governance_test + .finalize_vote(&realm_cookie, &proposal_cookie, None) + .await + .err() + .unwrap(); + + // Assert + + assert_eq!(err, GovernanceError::CannotFinalizeVotingInProgress.into()); +} + +#[tokio::test] +async fn test_finalize_vote_with_cannot_finalize_during_cool_off_time_error() { + // Arrange + let mut governance_test = GovernanceProgramTest::start_new().await; + + let realm_cookie = governance_test.with_realm().await; + let governed_account_cookie = governance_test.with_governed_account().await; + + let token_owner_record_cookie = governance_test + .with_community_token_deposit(&realm_cookie) + .await + .unwrap(); + + // Set cool off time to start in the middle of the voting time + let mut governance_config = governance_test.get_default_governance_config(); + governance_config.max_voting_time = 100; + governance_config.voting_cool_off_time = 50; + + let mut governance_cookie = governance_test + .with_governance_using_config( + &realm_cookie, + &governed_account_cookie, + &token_owner_record_cookie, + &governance_config, + ) + .await + .unwrap(); + + // Total 210 tokens + governance_test + .mint_community_tokens(&realm_cookie, 110) + .await; + + let proposal_cookie = governance_test + .with_signed_off_proposal(&token_owner_record_cookie, &mut governance_cookie) + .await + .unwrap(); + + governance_test + .with_cast_yes_no_vote(&proposal_cookie, &token_owner_record_cookie, YesNoVote::Yes) + .await + .unwrap(); + + // Advance timestamp into voting_cool_off_time + let clock = governance_test.bench.get_clock().await; + + governance_test + .advance_clock_past_timestamp( + clock.unix_timestamp + + (governance_cookie.account.config.max_voting_time + - governance_config.voting_cool_off_time) as i64, + ) + .await; + + // Act + + let err = governance_test + .finalize_vote(&realm_cookie, &proposal_cookie, None) + .await + .err() + .unwrap(); + + // Assert + + assert_eq!(err, GovernanceError::CannotFinalizeVotingInProgress.into()); +} From 5837dd8450d2f49605746af664c7bc90d8e45b9c Mon Sep 17 00:00:00 2001 From: "Sebastian.Bor" Date: Wed, 23 Nov 2022 12:04:22 +0000 Subject: [PATCH 11/16] fix: Remove default voting_cool_off_time --- governance/program/src/state/governance.rs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/governance/program/src/state/governance.rs b/governance/program/src/state/governance.rs index 803bc3a75e0..152658564f0 100644 --- a/governance/program/src/state/governance.rs +++ b/governance/program/src/state/governance.rs @@ -316,12 +316,7 @@ pub fn get_governance_data( // Reset voting_cool_off_time and reserved space previously used for voting_proposal_count // Default voting_cool_off_time to 1h for max_voting_time >= 10h - governance_data.config.voting_cool_off_time = - if governance_data.config.max_voting_time >= 36000 { - 3600 - } else { - 0 - }; + governance_data.config.voting_cool_off_time = 0; governance_data.config.reserved = 0; } @@ -718,7 +713,7 @@ mod test { VoteTipping::Strict ); - assert_eq!(governance_v3.config.voting_cool_off_time, 3600); + assert_eq!(governance_v3.config.voting_cool_off_time, 0); assert_eq!(governance_v3.config.reserved, 0); } From aec197c4bea813869df679a53ccea9077bd1e5de Mon Sep 17 00:00:00 2001 From: "Sebastian.Bor" Date: Wed, 23 Nov 2022 13:54:12 +0000 Subject: [PATCH 12/16] fix: Use max = base + cool_off voting time setup --- .../chat/program/tests/program_test/mod.rs | 2 +- governance/program/src/error.rs | 4 -- .../src/processor/process_relinquish_vote.rs | 4 +- governance/program/src/state/governance.rs | 14 ++--- governance/program/src/state/proposal.rs | 56 ++++++++++--------- .../program/tests/process_cancel_proposal.rs | 2 +- governance/program/tests/process_cast_vote.rs | 8 +-- .../program/tests/process_finalize_vote.rs | 16 +++--- .../program/tests/process_relinquish_vote.rs | 8 +-- governance/program/tests/program_test/mod.rs | 4 +- .../use_proposals_with_multiple_options.rs | 12 ++-- governance/program/tests/use_veto_vote.rs | 6 +- 12 files changed, 63 insertions(+), 73 deletions(-) diff --git a/governance/chat/program/tests/program_test/mod.rs b/governance/chat/program/tests/program_test/mod.rs index b15a9254408..9f10b3b6526 100644 --- a/governance/chat/program/tests/program_test/mod.rs +++ b/governance/chat/program/tests/program_test/mod.rs @@ -189,7 +189,7 @@ impl GovernanceChatProgramTest { community_vote_threshold: VoteThreshold::YesVotePercentage(60), min_community_weight_to_create_proposal: 5, min_transaction_hold_up_time: 10, - max_voting_time: 10, + base_voting_time: 10, community_vote_tipping: spl_governance::state::enums::VoteTipping::Strict, council_vote_threshold: VoteThreshold::YesVotePercentage(10), council_veto_vote_threshold: VoteThreshold::YesVotePercentage(50), diff --git a/governance/program/src/error.rs b/governance/program/src/error.rs index 3256fb14999..f2adea1f542 100644 --- a/governance/program/src/error.rs +++ b/governance/program/src/error.rs @@ -442,10 +442,6 @@ pub enum GovernanceError { /// Vote not allowed in cool off time #[error("Vote not allowed in cool off time")] VoteNotAllowedInCoolOffTime, // 606 - - /// Invalid voting cool off time - #[error("Invalid voting cool off time")] - InvalidVotingCoolOffTime, // 607 } impl PrintProgramError for GovernanceError { diff --git a/governance/program/src/processor/process_relinquish_vote.rs b/governance/program/src/processor/process_relinquish_vote.rs index 49c22d0a5d5..d381b646e61 100644 --- a/governance/program/src/processor/process_relinquish_vote.rs +++ b/governance/program/src/processor/process_relinquish_vote.rs @@ -65,10 +65,10 @@ pub fn process_relinquish_vote(program_id: &Pubkey, accounts: &[AccountInfo]) -> let clock = Clock::get()?; // If the Proposal is still being voted on then the token owner vote will be withdrawn and it won't count towards the vote outcome - // Note: If there is no tipping point the proposal can be still in Voting state but already past the configured max_voting_time + // Note: If there is no tipping point the proposal can be still in Voting state but already past the configured max voting time (base + cool off voting time) // It means it awaits manual finalization (FinalizeVote) and it should no longer be possible to withdraw the vote if proposal_data.state == ProposalState::Voting - && !proposal_data.has_vote_time_ended(&governance_data.config, clock.unix_timestamp) + && !proposal_data.has_max_voting_time_ended(&governance_data.config, clock.unix_timestamp) { let governance_authority_info = next_account_info(account_info_iter)?; // 5 let beneficiary_info = next_account_info(account_info_iter)?; // 6 diff --git a/governance/program/src/state/governance.rs b/governance/program/src/state/governance.rs index 152658564f0..8f2f1930ffd 100644 --- a/governance/program/src/state/governance.rs +++ b/governance/program/src/state/governance.rs @@ -33,8 +33,10 @@ pub struct GovernanceConfig { /// Minimum waiting time in seconds for a transaction to be executed after proposal is voted on pub min_transaction_hold_up_time: u32, - /// Time limit in seconds for proposal to be open for voting - pub max_voting_time: u32, + /// The base voting time in seconds for proposal to be open for voting + /// Voting is unrestricted during the base voting time and any vote types can be cast + /// The base voting time can be extend by optional cool off time when only negative votes (Veto and Deny) are allowed + pub base_voting_time: u32, /// Conditions under which a Community vote will complete early pub community_vote_tipping: VoteTipping, @@ -486,10 +488,6 @@ pub fn assert_is_valid_governance_config( return Err(GovernanceError::AtLeastOneVoteThresholdRequired.into()); } - if governance_config.voting_cool_off_time >= governance_config.max_voting_time { - return Err(GovernanceError::InvalidVotingCoolOffTime.into()); - } - if governance_config.reserved != 0 { return Err(GovernanceError::ReservedBufferMustBeEmpty.into()); } @@ -525,7 +523,7 @@ mod test { community_vote_threshold: VoteThreshold::YesVotePercentage(60), min_community_weight_to_create_proposal: 5, min_transaction_hold_up_time: 10, - max_voting_time: 5, + base_voting_time: 5, community_vote_tipping: VoteTipping::Strict, council_vote_threshold: VoteThreshold::YesVotePercentage(60), council_veto_vote_threshold: VoteThreshold::YesVotePercentage(50), @@ -669,7 +667,7 @@ mod test { governance_legacy_data.config.community_veto_vote_threshold = VoteThreshold::YesVotePercentage(0); governance_legacy_data.config.voting_cool_off_time = 1; - governance_legacy_data.config.max_voting_time = 36000; + governance_legacy_data.config.base_voting_time = 36000; let mut legacy_data = vec![]; governance_legacy_data.serialize(&mut legacy_data).unwrap(); diff --git a/governance/program/src/state/proposal.rs b/governance/program/src/state/proposal.rs index 9e921df9249..eaa963ccaa0 100644 --- a/governance/program/src/state/proposal.rs +++ b/governance/program/src/state/proposal.rs @@ -270,18 +270,16 @@ impl ProposalV2 { self.assert_is_voting_state() .map_err(|_| GovernanceError::InvalidStateCannotVote)?; - // Check if we are still within the configured max_voting_time period - if self.has_vote_time_ended(config, current_unix_timestamp) { + // Check if we are still within the configured max voting time period + if self.has_max_voting_time_ended(config, current_unix_timestamp) { return Err(GovernanceError::ProposalVotingTimeExpired.into()); } match vote { - // Once in the voting cool off time approving votes are no longer accepted + // Once the base voting time passes and we are in the voting cool off time approving votes are no longer accepted // Abstain is considered as positive vote because when attendance quorum is used it can tip the scales Vote::Approve(_) | Vote::Abstain => { - if self.expected_vote_end_time(config) - (config.voting_cool_off_time as i64) - < current_unix_timestamp - { + if self.base_voting_end_time(config) < current_unix_timestamp { Err(GovernanceError::VoteNotAllowedInCoolOffTime.into()) } else { Ok(()) @@ -292,22 +290,28 @@ impl ProposalV2 { } } - /// Expected vote end time determined by the configured max_voting_time period - pub fn expected_vote_end_time(&self, config: &GovernanceConfig) -> UnixTimestamp { + /// Expected base vote end time determined by the configured base_voting_time and actual voting start time + pub fn base_voting_end_time(&self, config: &GovernanceConfig) -> UnixTimestamp { self.voting_at .unwrap() - .checked_add(config.max_voting_time as i64) + .checked_add(config.base_voting_time as i64) + .unwrap() + } + /// Expected max vote end time determined by the configured base_voting_time, optional voting_cool_off_time and actual voting start time + pub fn max_voting_end_time(&self, config: &GovernanceConfig) -> UnixTimestamp { + self.base_voting_end_time(config) + .checked_add(config.voting_cool_off_time as i64) .unwrap() } - /// Checks whether the voting time has ended for the proposal - pub fn has_vote_time_ended( + /// Checks whether the max voting time has ended for the proposal + pub fn has_max_voting_time_ended( &self, config: &GovernanceConfig, current_unix_timestamp: UnixTimestamp, ) -> bool { - // Check if we passed vote_end_time - self.expected_vote_end_time(config) < current_unix_timestamp + // Check if we passed the max vote end time + self.max_voting_end_time(config) < current_unix_timestamp } /// Checks if Proposal can be finalized @@ -320,7 +324,7 @@ impl ProposalV2 { .map_err(|_| GovernanceError::InvalidStateCannotFinalize)?; // We can only finalize the vote after the configured max_voting_time has expired and vote time ended - if !self.has_vote_time_ended(config, current_unix_timestamp) { + if !self.has_max_voting_time_ended(config, current_unix_timestamp) { return Err(GovernanceError::CannotFinalizeVotingInProgress.into()); } @@ -339,7 +343,7 @@ impl ProposalV2 { self.assert_can_finalize_vote(config, current_unix_timestamp)?; self.state = self.resolve_final_vote_state(max_voter_weight, vote_threshold)?; - self.voting_completed_at = Some(self.expected_vote_end_time(config)); + self.voting_completed_at = Some(self.max_voting_end_time(config)); // Capture vote params to correctly display historical results self.max_vote_weight = Some(max_voter_weight); @@ -674,7 +678,7 @@ impl ProposalV2 { ProposalState::Voting => { // Note: If there is no tipping point the proposal can be still in Voting state but already past the configured max_voting_time // In that case we treat the proposal as finalized and it's no longer allowed to be canceled - if self.has_vote_time_ended(config, current_unix_timestamp) { + if self.has_max_voting_time_ended(config, current_unix_timestamp) { return Err(GovernanceError::ProposalVotingTimeExpired.into()); } Ok(()) @@ -1185,7 +1189,7 @@ mod test { community_vote_threshold: VoteThreshold::YesVotePercentage(60), min_community_weight_to_create_proposal: 5, min_transaction_hold_up_time: 10, - max_voting_time: 5, + base_voting_time: 5, community_vote_tipping: VoteTipping::Strict, council_vote_threshold: VoteThreshold::YesVotePercentage(60), council_veto_vote_threshold: VoteThreshold::YesVotePercentage(50), @@ -1668,7 +1672,7 @@ mod test { // Assert assert_eq!(proposal.state,test_case.expected_finalized_state,"CASE: {:?}",test_case); assert_eq!( - Some(proposal.expected_vote_end_time(&governance_config)), + Some(proposal.max_voting_end_time(&governance_config)), proposal.voting_completed_at ); @@ -2174,7 +2178,7 @@ mod test { let governance_config = create_test_governance_config(); let current_timestamp = - proposal.voting_at.unwrap() + governance_config.max_voting_time as i64; + proposal.voting_at.unwrap() + governance_config.base_voting_time as i64; let realm = create_test_realm(); let governing_token_mint = proposal.governing_token_mint; @@ -2209,7 +2213,7 @@ mod test { let governance_config = create_test_governance_config(); let current_timestamp = - proposal.voting_at.unwrap() + governance_config.max_voting_time as i64 + 1; + proposal.voting_at.unwrap() + governance_config.base_voting_time as i64 + 1; let realm = create_test_realm(); let governing_token_mint = proposal.governing_token_mint; @@ -2241,7 +2245,7 @@ mod test { let governance_config = create_test_governance_config(); let current_timestamp = - proposal.voting_at.unwrap() + governance_config.max_voting_time as i64 + 1; + proposal.voting_at.unwrap() + governance_config.base_voting_time as i64 + 1; let vote = Vote::Approve(vec![]); @@ -2263,7 +2267,7 @@ mod test { let governance_config = create_test_governance_config(); let current_timestamp = - proposal.voting_at.unwrap() + governance_config.max_voting_time as i64; + proposal.voting_at.unwrap() + governance_config.base_voting_time as i64; let vote = Vote::Approve(vec![]); @@ -2284,7 +2288,7 @@ mod test { governance_config.voting_cool_off_time = 2; let current_timestamp = proposal.voting_at.unwrap() - + governance_config.max_voting_time as i64 + + governance_config.base_voting_time as i64 - governance_config.voting_cool_off_time as i64; let vote = Vote::Approve(vec![]); @@ -2306,7 +2310,7 @@ mod test { governance_config.voting_cool_off_time = 2; let current_timestamp = - proposal.voting_at.unwrap() + governance_config.max_voting_time as i64 - 1; + proposal.voting_at.unwrap() + governance_config.base_voting_time as i64 + 1; let vote = Vote::Approve(vec![]); @@ -2330,7 +2334,7 @@ mod test { governance_config.voting_cool_off_time = 2; let current_timestamp = - proposal.voting_at.unwrap() + governance_config.max_voting_time as i64 - 1; + proposal.voting_at.unwrap() + governance_config.base_voting_time as i64 - 1; let vote = Vote::Veto; @@ -2351,7 +2355,7 @@ mod test { governance_config.voting_cool_off_time = 2; let current_timestamp = - proposal.voting_at.unwrap() + governance_config.max_voting_time as i64 - 1; + proposal.voting_at.unwrap() + governance_config.base_voting_time as i64 - 1; let vote = Vote::Deny; diff --git a/governance/program/tests/process_cancel_proposal.rs b/governance/program/tests/process_cancel_proposal.rs index dbc3535130b..f42944a6d7e 100644 --- a/governance/program/tests/process_cancel_proposal.rs +++ b/governance/program/tests/process_cancel_proposal.rs @@ -190,7 +190,7 @@ async fn test_cancel_proposal_with_vote_time_expired_error() { // Advance timestamp past max_voting_time governance_test .advance_clock_past_timestamp( - governance_cookie.account.config.max_voting_time as i64 + clock.unix_timestamp, + governance_cookie.account.config.base_voting_time as i64 + clock.unix_timestamp, ) .await; diff --git a/governance/program/tests/process_cast_vote.rs b/governance/program/tests/process_cast_vote.rs index 0c5566a529a..c7859cf4f48 100644 --- a/governance/program/tests/process_cast_vote.rs +++ b/governance/program/tests/process_cast_vote.rs @@ -1017,7 +1017,7 @@ async fn test_cast_vote_with_voting_time_expired_error() { .await; let vote_expired_at = proposal_account.voting_at.unwrap() - + governance_cookie.account.config.max_voting_time as i64; + + governance_cookie.account.config.base_voting_time as i64; governance_test .advance_clock_past_timestamp(vote_expired_at) @@ -1566,7 +1566,7 @@ async fn test_cast_approve_vote_with_cannot_vote_in_cool_off_time_error() { // Set cool off time to start in the middle of the voting time let mut governance_config = governance_test.get_default_governance_config(); - governance_config.max_voting_time = 100; + governance_config.base_voting_time = 100; governance_config.voting_cool_off_time = 50; let mut governance_cookie = governance_test @@ -1589,9 +1589,7 @@ async fn test_cast_approve_vote_with_cannot_vote_in_cool_off_time_error() { governance_test .advance_clock_past_timestamp( - clock.unix_timestamp - + (governance_cookie.account.config.max_voting_time - - governance_config.voting_cool_off_time) as i64, + clock.unix_timestamp + governance_cookie.account.config.base_voting_time as i64, ) .await; diff --git a/governance/program/tests/process_finalize_vote.rs b/governance/program/tests/process_finalize_vote.rs index cf3757087ab..4912843c120 100644 --- a/governance/program/tests/process_finalize_vote.rs +++ b/governance/program/tests/process_finalize_vote.rs @@ -62,7 +62,7 @@ async fn test_finalize_vote_to_succeeded() { // Advance timestamp past max_voting_time governance_test .advance_clock_past_timestamp( - governance_cookie.account.config.max_voting_time as i64 + governance_cookie.account.config.base_voting_time as i64 + proposal_account.voting_at.unwrap(), ) .await; @@ -82,7 +82,7 @@ async fn test_finalize_vote_to_succeeded() { assert_eq!(proposal_account.state, ProposalState::Succeeded); assert_eq!( - Some(proposal_account.expected_vote_end_time(&governance_cookie.account.config)), + Some(proposal_account.max_voting_end_time(&governance_cookie.account.config)), proposal_account.voting_completed_at ); @@ -153,7 +153,7 @@ async fn test_finalize_vote_to_defeated() { // Advance clock past max_voting_time governance_test .advance_clock_past_timestamp( - governance_cookie.account.config.max_voting_time as i64 + governance_cookie.account.config.base_voting_time as i64 + proposal_account.voting_at.unwrap(), ) .await; @@ -359,7 +359,7 @@ async fn test_finalize_council_vote() { // Advance timestamp past max_voting_time governance_test .advance_clock_past_timestamp( - governance_cookie.account.config.max_voting_time as i64 + governance_cookie.account.config.base_voting_time as i64 + proposal_account.voting_at.unwrap(), ) .await; @@ -379,7 +379,7 @@ async fn test_finalize_council_vote() { assert_eq!(proposal_account.state, ProposalState::Succeeded); assert_eq!( - Some(proposal_account.expected_vote_end_time(&governance_cookie.account.config)), + Some(proposal_account.max_voting_end_time(&governance_cookie.account.config)), proposal_account.voting_completed_at ); @@ -458,7 +458,7 @@ async fn test_finalize_vote_with_cannot_finalize_during_cool_off_time_error() { // Set cool off time to start in the middle of the voting time let mut governance_config = governance_test.get_default_governance_config(); - governance_config.max_voting_time = 100; + governance_config.base_voting_time = 100; governance_config.voting_cool_off_time = 50; let mut governance_cookie = governance_test @@ -491,9 +491,7 @@ async fn test_finalize_vote_with_cannot_finalize_during_cool_off_time_error() { governance_test .advance_clock_past_timestamp( - clock.unix_timestamp - + (governance_cookie.account.config.max_voting_time - - governance_config.voting_cool_off_time) as i64, + clock.unix_timestamp + governance_cookie.account.config.base_voting_time as i64, ) .await; diff --git a/governance/program/tests/process_relinquish_vote.rs b/governance/program/tests/process_relinquish_vote.rs index a835d4208bb..d17978ac4b8 100644 --- a/governance/program/tests/process_relinquish_vote.rs +++ b/governance/program/tests/process_relinquish_vote.rs @@ -497,7 +497,7 @@ async fn test_relinquish_proposal_with_cannot_relinquish_in_finalizing_state_err // Advance timestamp past max_voting_time governance_test .advance_clock_past_timestamp( - governance_cookie.account.config.max_voting_time as i64 + clock.unix_timestamp, + governance_cookie.account.config.base_voting_time as i64 + clock.unix_timestamp, ) .await; @@ -614,7 +614,7 @@ async fn test_change_yes_vote_to_no_within_cool_off_time() { // Set cool off time to start in the middle of the voting time let mut governance_config = governance_test.get_default_governance_config(); - governance_config.max_voting_time = 100; + governance_config.base_voting_time = 100; governance_config.voting_cool_off_time = 50; let mut governance_cookie = governance_test @@ -647,9 +647,7 @@ async fn test_change_yes_vote_to_no_within_cool_off_time() { governance_test .advance_clock_past_timestamp( - clock.unix_timestamp - + (governance_cookie.account.config.max_voting_time - - governance_config.voting_cool_off_time) as i64, + clock.unix_timestamp + governance_cookie.account.config.base_voting_time as i64, ) .await; diff --git a/governance/program/tests/program_test/mod.rs b/governance/program/tests/program_test/mod.rs index be32ae9c550..617a614aa7b 100644 --- a/governance/program/tests/program_test/mod.rs +++ b/governance/program/tests/program_test/mod.rs @@ -1408,7 +1408,7 @@ impl GovernanceProgramTest { community_vote_threshold: VoteThreshold::YesVotePercentage(60), min_community_weight_to_create_proposal: 5, min_transaction_hold_up_time: 10, - max_voting_time: 10, + base_voting_time: 10, community_vote_tipping: spl_governance::state::enums::VoteTipping::Strict, council_vote_threshold: VoteThreshold::YesVotePercentage(80), council_veto_vote_threshold: VoteThreshold::YesVotePercentage(55), @@ -2898,7 +2898,7 @@ impl GovernanceProgramTest { let clock = self.bench.get_clock().await; self.advance_clock_past_timestamp( - clock.unix_timestamp + governance_cookie.account.config.max_voting_time as i64, + clock.unix_timestamp + governance_cookie.account.config.base_voting_time as i64, ) .await; } diff --git a/governance/program/tests/use_proposals_with_multiple_options.rs b/governance/program/tests/use_proposals_with_multiple_options.rs index de05ebb4db8..91eb7a8f22a 100644 --- a/governance/program/tests/use_proposals_with_multiple_options.rs +++ b/governance/program/tests/use_proposals_with_multiple_options.rs @@ -298,7 +298,7 @@ async fn test_vote_on_none_executable_single_choice_proposal_with_multiple_optio // Advance timestamp past max_voting_time governance_test .advance_clock_past_timestamp( - governance_cookie.account.config.max_voting_time as i64 + clock.unix_timestamp, + governance_cookie.account.config.base_voting_time as i64 + clock.unix_timestamp, ) .await; @@ -403,7 +403,7 @@ async fn test_vote_on_none_executable_multi_choice_proposal_with_multiple_option // Advance timestamp past max_voting_time governance_test .advance_clock_past_timestamp( - governance_cookie.account.config.max_voting_time as i64 + clock.unix_timestamp, + governance_cookie.account.config.base_voting_time as i64 + clock.unix_timestamp, ) .await; @@ -563,7 +563,7 @@ async fn test_vote_on_executable_proposal_with_multiple_options_and_partial_succ // Advance timestamp past max_voting_time governance_test .advance_clock_past_timestamp( - governance_cookie.account.config.max_voting_time as i64 + clock.unix_timestamp, + governance_cookie.account.config.base_voting_time as i64 + clock.unix_timestamp, ) .await; @@ -755,7 +755,7 @@ async fn test_execute_proposal_with_multiple_options_and_partial_success() { // Advance timestamp past max_voting_time governance_test - .advance_clock_by_min_timespan(governance_cookie.account.config.max_voting_time as u64) + .advance_clock_by_min_timespan(governance_cookie.account.config.base_voting_time as u64) .await; governance_test @@ -921,7 +921,7 @@ async fn test_try_execute_proposal_with_multiple_options_and_full_deny() { // Advance timestamp past max_voting_time governance_test - .advance_clock_by_min_timespan(governance_cookie.account.config.max_voting_time as u64) + .advance_clock_by_min_timespan(governance_cookie.account.config.base_voting_time as u64) .await; governance_test @@ -1053,7 +1053,7 @@ async fn test_create_proposal_with_10_options_and_cast_vote() { governance_test .advance_clock_past_timestamp( - governance_cookie.account.config.max_voting_time as i64 + clock.unix_timestamp, + governance_cookie.account.config.base_voting_time as i64 + clock.unix_timestamp, ) .await; diff --git a/governance/program/tests/use_veto_vote.rs b/governance/program/tests/use_veto_vote.rs index 7cf43a864cf..ce011805b04 100644 --- a/governance/program/tests/use_veto_vote.rs +++ b/governance/program/tests/use_veto_vote.rs @@ -981,7 +981,7 @@ async fn test_cast_council_veto_vote_within_cool_off_time() { // Set cool off time to start in the middle of the voting time let mut governance_config = governance_test.get_default_governance_config(); - governance_config.max_voting_time = 100; + governance_config.base_voting_time = 100; governance_config.voting_cool_off_time = 50; let mut governance_cookie = governance_test @@ -1009,9 +1009,7 @@ async fn test_cast_council_veto_vote_within_cool_off_time() { governance_test .advance_clock_past_timestamp( - clock.unix_timestamp - + (governance_cookie.account.config.max_voting_time - - governance_config.voting_cool_off_time) as i64, + clock.unix_timestamp + governance_cookie.account.config.base_voting_time as i64, ) .await; From e8e053cc9cbff2a087c8481c15a188e36a753c3d Mon Sep 17 00:00:00 2001 From: "Sebastian.Bor" Date: Wed, 23 Nov 2022 16:15:38 +0000 Subject: [PATCH 13/16] chore: Cleanup --- governance/program/src/state/governance.rs | 8 ++- governance/program/src/state/proposal.rs | 15 +++-- .../program/tests/process_cancel_proposal.rs | 56 +++++++++++++++++++ governance/program/tests/process_cast_vote.rs | 3 +- .../program/tests/process_finalize_vote.rs | 3 +- .../program/tests/process_relinquish_vote.rs | 3 +- governance/program/tests/use_veto_vote.rs | 3 +- 7 files changed, 72 insertions(+), 19 deletions(-) diff --git a/governance/program/src/state/governance.rs b/governance/program/src/state/governance.rs index 8f2f1930ffd..f47dec7e138 100644 --- a/governance/program/src/state/governance.rs +++ b/governance/program/src/state/governance.rs @@ -316,10 +316,7 @@ pub fn get_governance_data( governance_data.config.community_veto_vote_threshold = VoteThreshold::Disabled; // Reset voting_cool_off_time and reserved space previously used for voting_proposal_count - - // Default voting_cool_off_time to 1h for max_voting_time >= 10h governance_data.config.voting_cool_off_time = 0; - governance_data.config.reserved = 0; } @@ -660,7 +657,12 @@ mod test { // Arrange let mut governance_legacy_data = create_test_governance(); + governance_legacy_data.config.community_vote_threshold = + VoteThreshold::YesVotePercentage(60); + + // council_vote_threshold == YesVotePercentage(0) indicates legacy account from V1 & V2 program versions governance_legacy_data.config.council_vote_threshold = VoteThreshold::YesVotePercentage(0); + governance_legacy_data.config.council_veto_vote_threshold = VoteThreshold::YesVotePercentage(0); governance_legacy_data.config.council_vote_tipping = VoteTipping::Disabled; diff --git a/governance/program/src/state/proposal.rs b/governance/program/src/state/proposal.rs index eaa963ccaa0..b5143a7e933 100644 --- a/governance/program/src/state/proposal.rs +++ b/governance/program/src/state/proposal.rs @@ -276,9 +276,9 @@ impl ProposalV2 { } match vote { - // Once the base voting time passes and we are in the voting cool off time approving votes are no longer accepted - // Abstain is considered as positive vote because when attendance quorum is used it can tip the scales Vote::Approve(_) | Vote::Abstain => { + // Once the base voting time passes and we are in the voting cool off time approving votes are no longer accepted + // Abstain is considered as positive vote because when attendance quorum is used it can tip the scales if self.base_voting_end_time(config) < current_unix_timestamp { Err(GovernanceError::VoteNotAllowedInCoolOffTime.into()) } else { @@ -2287,9 +2287,8 @@ mod test { let mut governance_config = create_test_governance_config(); governance_config.voting_cool_off_time = 2; - let current_timestamp = proposal.voting_at.unwrap() - + governance_config.base_voting_time as i64 - - governance_config.voting_cool_off_time as i64; + let current_timestamp = + proposal.voting_at.unwrap() + governance_config.base_voting_time as i64 - 1; let vote = Vote::Approve(vec![]); @@ -2334,7 +2333,7 @@ mod test { governance_config.voting_cool_off_time = 2; let current_timestamp = - proposal.voting_at.unwrap() + governance_config.base_voting_time as i64 - 1; + proposal.voting_at.unwrap() + governance_config.base_voting_time as i64 + 1; let vote = Vote::Veto; @@ -2352,10 +2351,10 @@ mod test { proposal.state = ProposalState::Voting; let mut governance_config = create_test_governance_config(); - governance_config.voting_cool_off_time = 2; + governance_config.voting_cool_off_time = 1; let current_timestamp = - proposal.voting_at.unwrap() + governance_config.base_voting_time as i64 - 1; + proposal.voting_at.unwrap() + governance_config.base_voting_time as i64 + 1; let vote = Vote::Deny; diff --git a/governance/program/tests/process_cancel_proposal.rs b/governance/program/tests/process_cancel_proposal.rs index f42944a6d7e..30482193c73 100644 --- a/governance/program/tests/process_cancel_proposal.rs +++ b/governance/program/tests/process_cancel_proposal.rs @@ -207,6 +207,62 @@ async fn test_cancel_proposal_with_vote_time_expired_error() { assert_eq!(err, GovernanceError::ProposalVotingTimeExpired.into()); } +#[tokio::test] +async fn test_cancel_proposal_after_voting_cool_off_with_vote_time_expired_error() { + // Arrange + let mut governance_test = GovernanceProgramTest::start_new().await; + + let realm_cookie = governance_test.with_realm().await; + let governed_account_cookie = governance_test.with_governed_account().await; + + let token_owner_record_cookie = governance_test + .with_community_token_deposit(&realm_cookie) + .await + .unwrap(); + + // Set none default voting cool off time + let mut governance_config = governance_test.get_default_governance_config(); + governance_config.voting_cool_off_time = 10; + + let mut governance_cookie = governance_test + .with_governance_using_config( + &realm_cookie, + &governed_account_cookie, + &token_owner_record_cookie, + &governance_config, + ) + .await + .unwrap(); + + let clock = governance_test.bench.get_clock().await; + + let proposal_cookie = governance_test + .with_signed_off_proposal(&token_owner_record_cookie, &mut governance_cookie) + .await + .unwrap(); + + // Advance timestamp past max_voting_time + governance_test + .advance_clock_past_timestamp( + (governance_cookie.account.config.base_voting_time + + governance_cookie.account.config.voting_cool_off_time) as i64 + + clock.unix_timestamp, + ) + .await; + + // Act + + let err = governance_test + .cancel_proposal(&proposal_cookie, &token_owner_record_cookie) + .await + .err() + .unwrap(); + + // Assert + + assert_eq!(err, GovernanceError::ProposalVotingTimeExpired.into()); +} + #[tokio::test] async fn test_cancel_proposal_in_voting_state() { // Arrange diff --git a/governance/program/tests/process_cast_vote.rs b/governance/program/tests/process_cast_vote.rs index c7859cf4f48..85e1332bc17 100644 --- a/governance/program/tests/process_cast_vote.rs +++ b/governance/program/tests/process_cast_vote.rs @@ -1564,9 +1564,8 @@ async fn test_cast_approve_vote_with_cannot_vote_in_cool_off_time_error() { .await .unwrap(); - // Set cool off time to start in the middle of the voting time + // Set none default voting cool off time let mut governance_config = governance_test.get_default_governance_config(); - governance_config.base_voting_time = 100; governance_config.voting_cool_off_time = 50; let mut governance_cookie = governance_test diff --git a/governance/program/tests/process_finalize_vote.rs b/governance/program/tests/process_finalize_vote.rs index 4912843c120..aa4f8930070 100644 --- a/governance/program/tests/process_finalize_vote.rs +++ b/governance/program/tests/process_finalize_vote.rs @@ -456,9 +456,8 @@ async fn test_finalize_vote_with_cannot_finalize_during_cool_off_time_error() { .await .unwrap(); - // Set cool off time to start in the middle of the voting time + // Set none default voting cool off time let mut governance_config = governance_test.get_default_governance_config(); - governance_config.base_voting_time = 100; governance_config.voting_cool_off_time = 50; let mut governance_cookie = governance_test diff --git a/governance/program/tests/process_relinquish_vote.rs b/governance/program/tests/process_relinquish_vote.rs index d17978ac4b8..a041466b7fe 100644 --- a/governance/program/tests/process_relinquish_vote.rs +++ b/governance/program/tests/process_relinquish_vote.rs @@ -612,10 +612,9 @@ async fn test_change_yes_vote_to_no_within_cool_off_time() { .await .unwrap(); - // Set cool off time to start in the middle of the voting time + // Set none default voting cool off time let mut governance_config = governance_test.get_default_governance_config(); governance_config.base_voting_time = 100; - governance_config.voting_cool_off_time = 50; let mut governance_cookie = governance_test .with_governance_using_config( diff --git a/governance/program/tests/use_veto_vote.rs b/governance/program/tests/use_veto_vote.rs index ce011805b04..48507367cc1 100644 --- a/governance/program/tests/use_veto_vote.rs +++ b/governance/program/tests/use_veto_vote.rs @@ -979,9 +979,8 @@ async fn test_cast_council_veto_vote_within_cool_off_time() { // Mint extra council tokens for total supply of 120 governance_test.mint_council_tokens(&realm_cookie, 20).await; - // Set cool off time to start in the middle of the voting time + // Set none default voting cool off time let mut governance_config = governance_test.get_default_governance_config(); - governance_config.base_voting_time = 100; governance_config.voting_cool_off_time = 50; let mut governance_cookie = governance_test From b1ba612cd2621f6dcd89784c98f2dbdbb1811537 Mon Sep 17 00:00:00 2001 From: "Sebastian.Bor" Date: Thu, 24 Nov 2022 10:55:34 +0000 Subject: [PATCH 14/16] chore: Fix test_change_yes_vote_to_no_within_cool_off_time --- governance/program/tests/process_relinquish_vote.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/governance/program/tests/process_relinquish_vote.rs b/governance/program/tests/process_relinquish_vote.rs index a041466b7fe..46f487afeb1 100644 --- a/governance/program/tests/process_relinquish_vote.rs +++ b/governance/program/tests/process_relinquish_vote.rs @@ -614,7 +614,7 @@ async fn test_change_yes_vote_to_no_within_cool_off_time() { // Set none default voting cool off time let mut governance_config = governance_test.get_default_governance_config(); - governance_config.base_voting_time = 100; + governance_config.voting_cool_off_time = 50; let mut governance_cookie = governance_test .with_governance_using_config( From 5ea83ae9b39d7e6c092f7d6c3f14eb801cc3984d Mon Sep 17 00:00:00 2001 From: "Sebastian.Bor" Date: Fri, 25 Nov 2022 11:08:56 +0000 Subject: [PATCH 15/16] chore: Adjust base, cool off and max voting time names --- .../chat/program/tests/program_test/mod.rs | 2 +- .../src/processor/process_relinquish_vote.rs | 2 +- governance/program/src/state/governance.rs | 6 +-- governance/program/src/state/proposal.rs | 42 +++++++++---------- .../program/tests/process_cancel_proposal.rs | 4 +- governance/program/tests/process_cast_vote.rs | 4 +- .../program/tests/process_finalize_vote.rs | 12 +++--- .../program/tests/process_relinquish_vote.rs | 4 +- governance/program/tests/program_test/mod.rs | 4 +- .../use_proposals_with_multiple_options.rs | 12 +++--- governance/program/tests/use_veto_vote.rs | 2 +- 11 files changed, 47 insertions(+), 47 deletions(-) diff --git a/governance/chat/program/tests/program_test/mod.rs b/governance/chat/program/tests/program_test/mod.rs index 9f10b3b6526..4eeebc7c22c 100644 --- a/governance/chat/program/tests/program_test/mod.rs +++ b/governance/chat/program/tests/program_test/mod.rs @@ -189,7 +189,7 @@ impl GovernanceChatProgramTest { community_vote_threshold: VoteThreshold::YesVotePercentage(60), min_community_weight_to_create_proposal: 5, min_transaction_hold_up_time: 10, - base_voting_time: 10, + voting_base_time: 10, community_vote_tipping: spl_governance::state::enums::VoteTipping::Strict, council_vote_threshold: VoteThreshold::YesVotePercentage(10), council_veto_vote_threshold: VoteThreshold::YesVotePercentage(50), diff --git a/governance/program/src/processor/process_relinquish_vote.rs b/governance/program/src/processor/process_relinquish_vote.rs index d381b646e61..724003ab46e 100644 --- a/governance/program/src/processor/process_relinquish_vote.rs +++ b/governance/program/src/processor/process_relinquish_vote.rs @@ -68,7 +68,7 @@ pub fn process_relinquish_vote(program_id: &Pubkey, accounts: &[AccountInfo]) -> // Note: If there is no tipping point the proposal can be still in Voting state but already past the configured max voting time (base + cool off voting time) // It means it awaits manual finalization (FinalizeVote) and it should no longer be possible to withdraw the vote if proposal_data.state == ProposalState::Voting - && !proposal_data.has_max_voting_time_ended(&governance_data.config, clock.unix_timestamp) + && !proposal_data.has_voting_max_time_ended(&governance_data.config, clock.unix_timestamp) { let governance_authority_info = next_account_info(account_info_iter)?; // 5 let beneficiary_info = next_account_info(account_info_iter)?; // 6 diff --git a/governance/program/src/state/governance.rs b/governance/program/src/state/governance.rs index f47dec7e138..4417b5d4552 100644 --- a/governance/program/src/state/governance.rs +++ b/governance/program/src/state/governance.rs @@ -36,7 +36,7 @@ pub struct GovernanceConfig { /// The base voting time in seconds for proposal to be open for voting /// Voting is unrestricted during the base voting time and any vote types can be cast /// The base voting time can be extend by optional cool off time when only negative votes (Veto and Deny) are allowed - pub base_voting_time: u32, + pub voting_base_time: u32, /// Conditions under which a Community vote will complete early pub community_vote_tipping: VoteTipping, @@ -520,7 +520,7 @@ mod test { community_vote_threshold: VoteThreshold::YesVotePercentage(60), min_community_weight_to_create_proposal: 5, min_transaction_hold_up_time: 10, - base_voting_time: 5, + voting_base_time: 5, community_vote_tipping: VoteTipping::Strict, council_vote_threshold: VoteThreshold::YesVotePercentage(60), council_veto_vote_threshold: VoteThreshold::YesVotePercentage(50), @@ -669,7 +669,7 @@ mod test { governance_legacy_data.config.community_veto_vote_threshold = VoteThreshold::YesVotePercentage(0); governance_legacy_data.config.voting_cool_off_time = 1; - governance_legacy_data.config.base_voting_time = 36000; + governance_legacy_data.config.voting_base_time = 36000; let mut legacy_data = vec![]; governance_legacy_data.serialize(&mut legacy_data).unwrap(); diff --git a/governance/program/src/state/proposal.rs b/governance/program/src/state/proposal.rs index b5143a7e933..36c94aea394 100644 --- a/governance/program/src/state/proposal.rs +++ b/governance/program/src/state/proposal.rs @@ -271,7 +271,7 @@ impl ProposalV2 { .map_err(|_| GovernanceError::InvalidStateCannotVote)?; // Check if we are still within the configured max voting time period - if self.has_max_voting_time_ended(config, current_unix_timestamp) { + if self.has_voting_max_time_ended(config, current_unix_timestamp) { return Err(GovernanceError::ProposalVotingTimeExpired.into()); } @@ -279,7 +279,7 @@ impl ProposalV2 { Vote::Approve(_) | Vote::Abstain => { // Once the base voting time passes and we are in the voting cool off time approving votes are no longer accepted // Abstain is considered as positive vote because when attendance quorum is used it can tip the scales - if self.base_voting_end_time(config) < current_unix_timestamp { + if self.voting_base_time_end(config) < current_unix_timestamp { Err(GovernanceError::VoteNotAllowedInCoolOffTime.into()) } else { Ok(()) @@ -291,27 +291,27 @@ impl ProposalV2 { } /// Expected base vote end time determined by the configured base_voting_time and actual voting start time - pub fn base_voting_end_time(&self, config: &GovernanceConfig) -> UnixTimestamp { + pub fn voting_base_time_end(&self, config: &GovernanceConfig) -> UnixTimestamp { self.voting_at .unwrap() - .checked_add(config.base_voting_time as i64) + .checked_add(config.voting_base_time as i64) .unwrap() } /// Expected max vote end time determined by the configured base_voting_time, optional voting_cool_off_time and actual voting start time - pub fn max_voting_end_time(&self, config: &GovernanceConfig) -> UnixTimestamp { - self.base_voting_end_time(config) + pub fn voting_max_time_end(&self, config: &GovernanceConfig) -> UnixTimestamp { + self.voting_base_time_end(config) .checked_add(config.voting_cool_off_time as i64) .unwrap() } /// Checks whether the max voting time has ended for the proposal - pub fn has_max_voting_time_ended( + pub fn has_voting_max_time_ended( &self, config: &GovernanceConfig, current_unix_timestamp: UnixTimestamp, ) -> bool { // Check if we passed the max vote end time - self.max_voting_end_time(config) < current_unix_timestamp + self.voting_max_time_end(config) < current_unix_timestamp } /// Checks if Proposal can be finalized @@ -324,7 +324,7 @@ impl ProposalV2 { .map_err(|_| GovernanceError::InvalidStateCannotFinalize)?; // We can only finalize the vote after the configured max_voting_time has expired and vote time ended - if !self.has_max_voting_time_ended(config, current_unix_timestamp) { + if !self.has_voting_max_time_ended(config, current_unix_timestamp) { return Err(GovernanceError::CannotFinalizeVotingInProgress.into()); } @@ -343,7 +343,7 @@ impl ProposalV2 { self.assert_can_finalize_vote(config, current_unix_timestamp)?; self.state = self.resolve_final_vote_state(max_voter_weight, vote_threshold)?; - self.voting_completed_at = Some(self.max_voting_end_time(config)); + self.voting_completed_at = Some(self.voting_max_time_end(config)); // Capture vote params to correctly display historical results self.max_vote_weight = Some(max_voter_weight); @@ -678,7 +678,7 @@ impl ProposalV2 { ProposalState::Voting => { // Note: If there is no tipping point the proposal can be still in Voting state but already past the configured max_voting_time // In that case we treat the proposal as finalized and it's no longer allowed to be canceled - if self.has_max_voting_time_ended(config, current_unix_timestamp) { + if self.has_voting_max_time_ended(config, current_unix_timestamp) { return Err(GovernanceError::ProposalVotingTimeExpired.into()); } Ok(()) @@ -1189,7 +1189,7 @@ mod test { community_vote_threshold: VoteThreshold::YesVotePercentage(60), min_community_weight_to_create_proposal: 5, min_transaction_hold_up_time: 10, - base_voting_time: 5, + voting_base_time: 5, community_vote_tipping: VoteTipping::Strict, council_vote_threshold: VoteThreshold::YesVotePercentage(60), council_veto_vote_threshold: VoteThreshold::YesVotePercentage(50), @@ -1672,7 +1672,7 @@ mod test { // Assert assert_eq!(proposal.state,test_case.expected_finalized_state,"CASE: {:?}",test_case); assert_eq!( - Some(proposal.max_voting_end_time(&governance_config)), + Some(proposal.voting_max_time_end(&governance_config)), proposal.voting_completed_at ); @@ -2178,7 +2178,7 @@ mod test { let governance_config = create_test_governance_config(); let current_timestamp = - proposal.voting_at.unwrap() + governance_config.base_voting_time as i64; + proposal.voting_at.unwrap() + governance_config.voting_base_time as i64; let realm = create_test_realm(); let governing_token_mint = proposal.governing_token_mint; @@ -2213,7 +2213,7 @@ mod test { let governance_config = create_test_governance_config(); let current_timestamp = - proposal.voting_at.unwrap() + governance_config.base_voting_time as i64 + 1; + proposal.voting_at.unwrap() + governance_config.voting_base_time as i64 + 1; let realm = create_test_realm(); let governing_token_mint = proposal.governing_token_mint; @@ -2245,7 +2245,7 @@ mod test { let governance_config = create_test_governance_config(); let current_timestamp = - proposal.voting_at.unwrap() + governance_config.base_voting_time as i64 + 1; + proposal.voting_at.unwrap() + governance_config.voting_base_time as i64 + 1; let vote = Vote::Approve(vec![]); @@ -2267,7 +2267,7 @@ mod test { let governance_config = create_test_governance_config(); let current_timestamp = - proposal.voting_at.unwrap() + governance_config.base_voting_time as i64; + proposal.voting_at.unwrap() + governance_config.voting_base_time as i64; let vote = Vote::Approve(vec![]); @@ -2288,7 +2288,7 @@ mod test { governance_config.voting_cool_off_time = 2; let current_timestamp = - proposal.voting_at.unwrap() + governance_config.base_voting_time as i64 - 1; + proposal.voting_at.unwrap() + governance_config.voting_base_time as i64 - 1; let vote = Vote::Approve(vec![]); @@ -2309,7 +2309,7 @@ mod test { governance_config.voting_cool_off_time = 2; let current_timestamp = - proposal.voting_at.unwrap() + governance_config.base_voting_time as i64 + 1; + proposal.voting_at.unwrap() + governance_config.voting_base_time as i64 + 1; let vote = Vote::Approve(vec![]); @@ -2333,7 +2333,7 @@ mod test { governance_config.voting_cool_off_time = 2; let current_timestamp = - proposal.voting_at.unwrap() + governance_config.base_voting_time as i64 + 1; + proposal.voting_at.unwrap() + governance_config.voting_base_time as i64 + 1; let vote = Vote::Veto; @@ -2354,7 +2354,7 @@ mod test { governance_config.voting_cool_off_time = 1; let current_timestamp = - proposal.voting_at.unwrap() + governance_config.base_voting_time as i64 + 1; + proposal.voting_at.unwrap() + governance_config.voting_base_time as i64 + 1; let vote = Vote::Deny; diff --git a/governance/program/tests/process_cancel_proposal.rs b/governance/program/tests/process_cancel_proposal.rs index 30482193c73..dcf31fc92b9 100644 --- a/governance/program/tests/process_cancel_proposal.rs +++ b/governance/program/tests/process_cancel_proposal.rs @@ -190,7 +190,7 @@ async fn test_cancel_proposal_with_vote_time_expired_error() { // Advance timestamp past max_voting_time governance_test .advance_clock_past_timestamp( - governance_cookie.account.config.base_voting_time as i64 + clock.unix_timestamp, + governance_cookie.account.config.voting_base_time as i64 + clock.unix_timestamp, ) .await; @@ -244,7 +244,7 @@ async fn test_cancel_proposal_after_voting_cool_off_with_vote_time_expired_error // Advance timestamp past max_voting_time governance_test .advance_clock_past_timestamp( - (governance_cookie.account.config.base_voting_time + (governance_cookie.account.config.voting_base_time + governance_cookie.account.config.voting_cool_off_time) as i64 + clock.unix_timestamp, ) diff --git a/governance/program/tests/process_cast_vote.rs b/governance/program/tests/process_cast_vote.rs index 85e1332bc17..8ab42c30d73 100644 --- a/governance/program/tests/process_cast_vote.rs +++ b/governance/program/tests/process_cast_vote.rs @@ -1017,7 +1017,7 @@ async fn test_cast_vote_with_voting_time_expired_error() { .await; let vote_expired_at = proposal_account.voting_at.unwrap() - + governance_cookie.account.config.base_voting_time as i64; + + governance_cookie.account.config.voting_base_time as i64; governance_test .advance_clock_past_timestamp(vote_expired_at) @@ -1588,7 +1588,7 @@ async fn test_cast_approve_vote_with_cannot_vote_in_cool_off_time_error() { governance_test .advance_clock_past_timestamp( - clock.unix_timestamp + governance_cookie.account.config.base_voting_time as i64, + clock.unix_timestamp + governance_cookie.account.config.voting_base_time as i64, ) .await; diff --git a/governance/program/tests/process_finalize_vote.rs b/governance/program/tests/process_finalize_vote.rs index aa4f8930070..6f78f599b28 100644 --- a/governance/program/tests/process_finalize_vote.rs +++ b/governance/program/tests/process_finalize_vote.rs @@ -62,7 +62,7 @@ async fn test_finalize_vote_to_succeeded() { // Advance timestamp past max_voting_time governance_test .advance_clock_past_timestamp( - governance_cookie.account.config.base_voting_time as i64 + governance_cookie.account.config.voting_base_time as i64 + proposal_account.voting_at.unwrap(), ) .await; @@ -82,7 +82,7 @@ async fn test_finalize_vote_to_succeeded() { assert_eq!(proposal_account.state, ProposalState::Succeeded); assert_eq!( - Some(proposal_account.max_voting_end_time(&governance_cookie.account.config)), + Some(proposal_account.voting_max_time_end(&governance_cookie.account.config)), proposal_account.voting_completed_at ); @@ -153,7 +153,7 @@ async fn test_finalize_vote_to_defeated() { // Advance clock past max_voting_time governance_test .advance_clock_past_timestamp( - governance_cookie.account.config.base_voting_time as i64 + governance_cookie.account.config.voting_base_time as i64 + proposal_account.voting_at.unwrap(), ) .await; @@ -359,7 +359,7 @@ async fn test_finalize_council_vote() { // Advance timestamp past max_voting_time governance_test .advance_clock_past_timestamp( - governance_cookie.account.config.base_voting_time as i64 + governance_cookie.account.config.voting_base_time as i64 + proposal_account.voting_at.unwrap(), ) .await; @@ -379,7 +379,7 @@ async fn test_finalize_council_vote() { assert_eq!(proposal_account.state, ProposalState::Succeeded); assert_eq!( - Some(proposal_account.max_voting_end_time(&governance_cookie.account.config)), + Some(proposal_account.voting_max_time_end(&governance_cookie.account.config)), proposal_account.voting_completed_at ); @@ -490,7 +490,7 @@ async fn test_finalize_vote_with_cannot_finalize_during_cool_off_time_error() { governance_test .advance_clock_past_timestamp( - clock.unix_timestamp + governance_cookie.account.config.base_voting_time as i64, + clock.unix_timestamp + governance_cookie.account.config.voting_base_time as i64, ) .await; diff --git a/governance/program/tests/process_relinquish_vote.rs b/governance/program/tests/process_relinquish_vote.rs index 46f487afeb1..4303625e0ce 100644 --- a/governance/program/tests/process_relinquish_vote.rs +++ b/governance/program/tests/process_relinquish_vote.rs @@ -497,7 +497,7 @@ async fn test_relinquish_proposal_with_cannot_relinquish_in_finalizing_state_err // Advance timestamp past max_voting_time governance_test .advance_clock_past_timestamp( - governance_cookie.account.config.base_voting_time as i64 + clock.unix_timestamp, + governance_cookie.account.config.voting_base_time as i64 + clock.unix_timestamp, ) .await; @@ -646,7 +646,7 @@ async fn test_change_yes_vote_to_no_within_cool_off_time() { governance_test .advance_clock_past_timestamp( - clock.unix_timestamp + governance_cookie.account.config.base_voting_time as i64, + clock.unix_timestamp + governance_cookie.account.config.voting_base_time as i64, ) .await; diff --git a/governance/program/tests/program_test/mod.rs b/governance/program/tests/program_test/mod.rs index 617a614aa7b..2468f8655a7 100644 --- a/governance/program/tests/program_test/mod.rs +++ b/governance/program/tests/program_test/mod.rs @@ -1408,7 +1408,7 @@ impl GovernanceProgramTest { community_vote_threshold: VoteThreshold::YesVotePercentage(60), min_community_weight_to_create_proposal: 5, min_transaction_hold_up_time: 10, - base_voting_time: 10, + voting_base_time: 10, community_vote_tipping: spl_governance::state::enums::VoteTipping::Strict, council_vote_threshold: VoteThreshold::YesVotePercentage(80), council_veto_vote_threshold: VoteThreshold::YesVotePercentage(55), @@ -2898,7 +2898,7 @@ impl GovernanceProgramTest { let clock = self.bench.get_clock().await; self.advance_clock_past_timestamp( - clock.unix_timestamp + governance_cookie.account.config.base_voting_time as i64, + clock.unix_timestamp + governance_cookie.account.config.voting_base_time as i64, ) .await; } diff --git a/governance/program/tests/use_proposals_with_multiple_options.rs b/governance/program/tests/use_proposals_with_multiple_options.rs index 91eb7a8f22a..30b5ea4b21b 100644 --- a/governance/program/tests/use_proposals_with_multiple_options.rs +++ b/governance/program/tests/use_proposals_with_multiple_options.rs @@ -298,7 +298,7 @@ async fn test_vote_on_none_executable_single_choice_proposal_with_multiple_optio // Advance timestamp past max_voting_time governance_test .advance_clock_past_timestamp( - governance_cookie.account.config.base_voting_time as i64 + clock.unix_timestamp, + governance_cookie.account.config.voting_base_time as i64 + clock.unix_timestamp, ) .await; @@ -403,7 +403,7 @@ async fn test_vote_on_none_executable_multi_choice_proposal_with_multiple_option // Advance timestamp past max_voting_time governance_test .advance_clock_past_timestamp( - governance_cookie.account.config.base_voting_time as i64 + clock.unix_timestamp, + governance_cookie.account.config.voting_base_time as i64 + clock.unix_timestamp, ) .await; @@ -563,7 +563,7 @@ async fn test_vote_on_executable_proposal_with_multiple_options_and_partial_succ // Advance timestamp past max_voting_time governance_test .advance_clock_past_timestamp( - governance_cookie.account.config.base_voting_time as i64 + clock.unix_timestamp, + governance_cookie.account.config.voting_base_time as i64 + clock.unix_timestamp, ) .await; @@ -755,7 +755,7 @@ async fn test_execute_proposal_with_multiple_options_and_partial_success() { // Advance timestamp past max_voting_time governance_test - .advance_clock_by_min_timespan(governance_cookie.account.config.base_voting_time as u64) + .advance_clock_by_min_timespan(governance_cookie.account.config.voting_base_time as u64) .await; governance_test @@ -921,7 +921,7 @@ async fn test_try_execute_proposal_with_multiple_options_and_full_deny() { // Advance timestamp past max_voting_time governance_test - .advance_clock_by_min_timespan(governance_cookie.account.config.base_voting_time as u64) + .advance_clock_by_min_timespan(governance_cookie.account.config.voting_base_time as u64) .await; governance_test @@ -1053,7 +1053,7 @@ async fn test_create_proposal_with_10_options_and_cast_vote() { governance_test .advance_clock_past_timestamp( - governance_cookie.account.config.base_voting_time as i64 + clock.unix_timestamp, + governance_cookie.account.config.voting_base_time as i64 + clock.unix_timestamp, ) .await; diff --git a/governance/program/tests/use_veto_vote.rs b/governance/program/tests/use_veto_vote.rs index 48507367cc1..6a242bd0ecf 100644 --- a/governance/program/tests/use_veto_vote.rs +++ b/governance/program/tests/use_veto_vote.rs @@ -1008,7 +1008,7 @@ async fn test_cast_council_veto_vote_within_cool_off_time() { governance_test .advance_clock_past_timestamp( - clock.unix_timestamp + governance_cookie.account.config.base_voting_time as i64, + clock.unix_timestamp + governance_cookie.account.config.voting_base_time as i64, ) .await; From 27647ac3a2f55d18b9b71444eaeb850decefa22e Mon Sep 17 00:00:00 2001 From: "Sebastian.Bor" Date: Fri, 25 Nov 2022 11:17:23 +0000 Subject: [PATCH 16/16] chore: Create has_voting_base_time_ended --- governance/program/src/state/proposal.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/governance/program/src/state/proposal.rs b/governance/program/src/state/proposal.rs index 36c94aea394..6abccf68bd3 100644 --- a/governance/program/src/state/proposal.rs +++ b/governance/program/src/state/proposal.rs @@ -279,7 +279,7 @@ impl ProposalV2 { Vote::Approve(_) | Vote::Abstain => { // Once the base voting time passes and we are in the voting cool off time approving votes are no longer accepted // Abstain is considered as positive vote because when attendance quorum is used it can tip the scales - if self.voting_base_time_end(config) < current_unix_timestamp { + if self.has_voting_base_time_ended(config, current_unix_timestamp) { Err(GovernanceError::VoteNotAllowedInCoolOffTime.into()) } else { Ok(()) @@ -297,6 +297,17 @@ impl ProposalV2 { .checked_add(config.voting_base_time as i64) .unwrap() } + + /// Checks whether the base voting time has ended for the proposal + pub fn has_voting_base_time_ended( + &self, + config: &GovernanceConfig, + current_unix_timestamp: UnixTimestamp, + ) -> bool { + // Check if we passed the configured base vote end time + self.voting_base_time_end(config) < current_unix_timestamp + } + /// Expected max vote end time determined by the configured base_voting_time, optional voting_cool_off_time and actual voting start time pub fn voting_max_time_end(&self, config: &GovernanceConfig) -> UnixTimestamp { self.voting_base_time_end(config)