Governance: Assert tokens to create governance (#2206)

* feat: assert owner has enough tokens to create proposal

* chore: test min tokens to create governance

* chore: bump version
This commit is contained in:
Sebastian Bor 2021-08-03 11:01:25 +01:00 committed by GitHub
parent 7a593f5da2
commit 2e4b49b72c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 138 additions and 13 deletions

2
Cargo.lock generated
View File

@ -3776,7 +3776,7 @@ dependencies = [
[[package]]
name = "spl-governance"
version = "1.0.8"
version = "1.0.9"
dependencies = [
"arrayref",
"assert_matches",

View File

@ -1,6 +1,6 @@
[package]
name = "spl-governance"
version = "1.0.8"
version = "1.0.9"
description = "Solana Program Library Governance Program"
authors = ["Solana Maintainers <maintainers@solana.foundation>"]
repository = "https://github.com/solana-labs/solana-program-library"

View File

@ -311,6 +311,10 @@ pub enum GovernanceError {
/// Invalid max vote weight supply fraction
#[error("Invalid max vote weight supply fraction")]
InvalidMaxVoteWeightSupplyFraction,
/// Owner doesn't have enough governing tokens to create Governance
#[error("Owner doesn't have enough governing tokens to create Governance")]
NotEnoughTokensToCreateGovernance,
}
impl PrintProgramError for GovernanceError {

View File

@ -7,6 +7,8 @@ use crate::{
assert_valid_create_governance_args, get_account_governance_address_seeds, Governance,
GovernanceConfig,
},
realm::get_realm_data,
token_owner_record::get_token_owner_record_data_for_realm,
},
tools::account::create_and_serialize_account_signed,
};
@ -30,7 +32,7 @@ pub fn process_create_account_governance(
let account_governance_info = next_account_info(account_info_iter)?; // 1
let governed_account_info = next_account_info(account_info_iter)?; // 2
let _token_owner_record_info = next_account_info(account_info_iter)?; // 3
let token_owner_record_info = next_account_info(account_info_iter)?; // 3
let payer_info = next_account_info(account_info_iter)?; // 4
let system_info = next_account_info(account_info_iter)?; // 5
@ -40,6 +42,12 @@ pub fn process_create_account_governance(
assert_valid_create_governance_args(program_id, &config, realm_info)?;
let realm_data = get_realm_data(program_id, realm_info)?;
let token_owner_record_data =
get_token_owner_record_data_for_realm(program_id, token_owner_record_info, realm_info.key)?;
token_owner_record_data.assert_can_create_governance(&realm_data)?;
let account_governance_data = Governance {
account_type: GovernanceAccountType::AccountGovernance,
realm: *realm_info.key,

View File

@ -7,6 +7,8 @@ use crate::{
assert_valid_create_governance_args, get_mint_governance_address_seeds, Governance,
GovernanceConfig,
},
realm::get_realm_data,
token_owner_record::get_token_owner_record_data_for_realm,
},
tools::{
account::create_and_serialize_account_signed,
@ -36,7 +38,7 @@ pub fn process_create_mint_governance(
let governed_mint_info = next_account_info(account_info_iter)?; // 2
let governed_mint_authority_info = next_account_info(account_info_iter)?; // 3
let _token_owner_record_info = next_account_info(account_info_iter)?; // 4
let token_owner_record_info = next_account_info(account_info_iter)?; // 4
let payer_info = next_account_info(account_info_iter)?; // 5
let spl_token_info = next_account_info(account_info_iter)?; // 6
@ -48,6 +50,12 @@ pub fn process_create_mint_governance(
assert_valid_create_governance_args(program_id, &config, realm_info)?;
let realm_data = get_realm_data(program_id, realm_info)?;
let token_owner_record_data =
get_token_owner_record_data_for_realm(program_id, token_owner_record_info, realm_info.key)?;
token_owner_record_data.assert_can_create_governance(&realm_data)?;
let mint_governance_data = Governance {
account_type: GovernanceAccountType::MintGovernance,
realm: *realm_info.key,

View File

@ -8,6 +8,8 @@ use crate::{
assert_valid_create_governance_args, get_program_governance_address_seeds,
GovernanceConfig,
},
realm::get_realm_data,
token_owner_record::get_token_owner_record_data_for_realm,
},
tools::{
account::create_and_serialize_account_signed,
@ -40,7 +42,7 @@ pub fn process_create_program_governance(
let governed_program_data_info = next_account_info(account_info_iter)?; // 2
let governed_program_upgrade_authority_info = next_account_info(account_info_iter)?; // 3
let _token_owner_record_info = next_account_info(account_info_iter)?; // 4
let token_owner_record_info = next_account_info(account_info_iter)?; // 4
let payer_info = next_account_info(account_info_iter)?; // 5
let bpf_upgrade_loader_info = next_account_info(account_info_iter)?; // 6
@ -52,6 +54,12 @@ pub fn process_create_program_governance(
assert_valid_create_governance_args(program_id, &config, realm_info)?;
let realm_data = get_realm_data(program_id, realm_info)?;
let token_owner_record_data =
get_token_owner_record_data_for_realm(program_id, token_owner_record_info, realm_info.key)?;
token_owner_record_data.assert_can_create_governance(&realm_data)?;
let program_governance_data = Governance {
account_type: GovernanceAccountType::ProgramGovernance,
realm: *realm_info.key,

View File

@ -7,6 +7,8 @@ use crate::{
assert_valid_create_governance_args, get_token_governance_address_seeds, Governance,
GovernanceConfig,
},
realm::get_realm_data,
token_owner_record::get_token_owner_record_data_for_realm,
},
tools::{
account::create_and_serialize_account_signed,
@ -36,7 +38,7 @@ pub fn process_create_token_governance(
let governed_token_info = next_account_info(account_info_iter)?; // 2
let governed_token_owner_info = next_account_info(account_info_iter)?; // 3
let _token_owner_record_info = next_account_info(account_info_iter)?; // 4
let token_owner_record_info = next_account_info(account_info_iter)?; // 4
let payer_info = next_account_info(account_info_iter)?; // 5
let spl_token_info = next_account_info(account_info_iter)?; // 6
@ -48,6 +50,12 @@ pub fn process_create_token_governance(
assert_valid_create_governance_args(program_id, &config, realm_info)?;
let realm_data = get_realm_data(program_id, realm_info)?;
let token_owner_record_data =
get_token_owner_record_data_for_realm(program_id, token_owner_record_info, realm_info.key)?;
token_owner_record_data.assert_can_create_governance(&realm_data)?;
let token_governance_data = Governance {
account_type: GovernanceAccountType::TokenGovernance,
realm: *realm_info.key,

View File

@ -105,6 +105,25 @@ impl TokenOwnerRecord {
Ok(())
}
/// Asserts TokenOwner has enough tokens to be allowed to create governance
pub fn assert_can_create_governance(&self, realm_data: &Realm) -> Result<(), ProgramError> {
let min_tokens_to_create_governance =
if self.governing_token_mint == realm_data.community_mint {
realm_data.config.min_community_tokens_to_create_governance
} else if Some(self.governing_token_mint) == realm_data.config.council_mint {
// For council tokens it's enough to be in possession of any number of tokens
1
} else {
return Err(GovernanceError::InvalidGoverningTokenMint.into());
};
if self.governing_token_deposit_amount < min_tokens_to_create_governance {
return Err(GovernanceError::NotEnoughTokensToCreateGovernance.into());
}
Ok(())
}
}
/// Returns TokenOwnerRecord PDA address

View File

@ -130,3 +130,69 @@ async fn test_create_account_governance_with_invalid_config_error() {
assert_eq!(err, GovernanceError::InvalidVoteThresholdPercentage.into());
}
#[tokio::test]
async fn test_create_account_governance_with_not_enough_community_tokens_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;
// Set token deposit amount below the required threshold
let token_amount = 4;
let token_owner_record_cookie = governance_test
.with_community_token_deposit_amount(&realm_cookie, token_amount)
.await;
// Act
let err = governance_test
.with_account_governance(
&realm_cookie,
&governed_account_cookie,
&token_owner_record_cookie,
)
.await
.err()
.unwrap();
// Assert
assert_eq!(
err,
GovernanceError::NotEnoughTokensToCreateGovernance.into()
);
}
#[tokio::test]
async fn test_create_account_governance_with_not_enough_council_tokens_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;
// Set token deposit amount below the required threshold
let token_amount: u64 = 0;
let token_owner_record_cookie = governance_test
.with_council_token_deposit_amount(&realm_cookie, token_amount)
.await;
// Act
let err = governance_test
.with_account_governance(
&realm_cookie,
&governed_account_cookie,
&token_owner_record_cookie,
)
.await
.err()
.unwrap();
// Assert
assert_eq!(
err,
GovernanceError::NotEnoughTokensToCreateGovernance.into()
);
}

View File

@ -202,25 +202,29 @@ async fn test_create_proposal_with_not_enough_community_tokens_error() {
let realm_cookie = governance_test.with_realm().await;
let governed_account_cookie = governance_test.with_governed_account().await;
// Set token deposit amount below the required threshold
let token_amount = 4;
let token_owner_record_cookie = governance_test
.with_community_token_deposit_amount(&realm_cookie, token_amount)
let token_owner_record_cookie1 = governance_test
.with_community_token_deposit(&realm_cookie)
.await;
let mut account_governance_cookie = governance_test
.with_account_governance(
&realm_cookie,
&governed_account_cookie,
&token_owner_record_cookie,
&token_owner_record_cookie1,
)
.await
.unwrap();
// Set token deposit amount below the required threshold
let token_amount = 4;
let token_owner_record_cookie2 = governance_test
.with_community_token_deposit_amount(&realm_cookie, token_amount)
.await;
// Act
let err = governance_test
.with_proposal(&token_owner_record_cookie, &mut account_governance_cookie)
.with_proposal(&token_owner_record_cookie2, &mut account_governance_cookie)
.await
.err()
.unwrap();