From ebc91e871a0b39bf76fbce52c6da87c9b674cf4e Mon Sep 17 00:00:00 2001 From: Sebastian Bor Date: Mon, 31 Jan 2022 03:58:28 +0000 Subject: [PATCH] Governance: add v2 account padding (#2842) * chore: create RealmV2 account type * feat: add padding to Realm account * chore: move RealmConfigArgsV1 and GovernanceInstructionV1 to tests * chore: Add RealmV2 comments * chore: rename TokenOwnerRecord to V2 and add reserved space * feat: translate TokenOwnerRecordV1 * chore: rename Governance to GovernanceV2 * feat: add reserved_v2 to Governance * chore: make clippy happy * feat: translate GovernanceV1 accounts * chore: fix chat build * fix: update seeds logic for governance V1 accounts * fix: add check for RealmV1 and GovernanceV1 * chore: add padding to ProposalTransaction * chore: add padding to VoteRecordV2 * chore: add padding to SignatoryRecord V2 * feat: translate SignatoryRecordV1 account --- governance/chat/program/src/state.rs | 4 +- .../chat/program/tests/program_test/mod.rs | 2 +- governance/program/src/addins/voter_weight.rs | 4 +- .../src/processor/process_add_signatory.rs | 9 +- .../src/processor/process_cancel_proposal.rs | 1 - .../src/processor/process_cast_vote.rs | 7 +- .../processor/process_create_governance.rs | 11 +- .../process_create_mint_governance.rs | 11 +- .../process_create_program_governance.rs | 9 +- .../src/processor/process_create_proposal.rs | 1 - .../src/processor/process_create_realm.rs | 9 +- .../process_create_token_governance.rs | 11 +- .../process_create_token_owner_record.rs | 7 +- .../process_deposit_governing_tokens.rs | 8 +- .../src/processor/process_finalize_vote.rs | 2 - .../processor/process_insert_transaction.rs | 1 + .../src/processor/process_relinquish_vote.rs | 2 - .../process_set_governance_config.rs | 1 - .../process_set_governance_delegate.rs | 1 - .../processor/process_set_realm_authority.rs | 1 - .../processor/process_sign_off_proposal.rs | 1 - .../process_withdraw_governing_tokens.rs | 1 - governance/program/src/state/enums.rs | 50 ++++- governance/program/src/state/governance.rs | 115 ++++++++--- governance/program/src/state/legacy.rs | 190 ++++++++++++------ .../program/src/state/program_metadata.rs | 2 +- governance/program/src/state/proposal.rs | 17 +- .../program/src/state/proposal_transaction.rs | 14 +- governance/program/src/state/realm.rs | 157 ++++++++++----- governance/program/src/state/realm_config.rs | 2 +- .../program/src/state/signatory_record.rs | 70 ++++++- .../program/src/state/token_owner_record.rs | 163 ++++++++------- governance/program/src/state/vote_record.rs | 11 + .../program/tests/program_test/cookies.rs | 16 +- governance/program/tests/program_test/mod.rs | 73 ++++--- governance/tools/src/account.rs | 6 +- 36 files changed, 665 insertions(+), 325 deletions(-) diff --git a/governance/chat/program/src/state.rs b/governance/chat/program/src/state.rs index 88d7efe5..6e56e695 100644 --- a/governance/chat/program/src/state.rs +++ b/governance/chat/program/src/state.rs @@ -5,7 +5,7 @@ use solana_program::{ account_info::AccountInfo, clock::UnixTimestamp, program_error::ProgramError, pubkey::Pubkey, }; -use spl_governance_tools::account::{assert_is_valid_account, AccountMaxSize}; +use spl_governance_tools::account::{assert_is_valid_account_of_type, AccountMaxSize}; /// Defines all GovernanceChat accounts types #[repr(C)] @@ -67,7 +67,7 @@ pub fn assert_is_valid_chat_message( program_id: &Pubkey, chat_message_info: &AccountInfo, ) -> Result<(), ProgramError> { - assert_is_valid_account( + assert_is_valid_account_of_type( chat_message_info, GovernanceChatAccountType::ChatMessage, program_id, diff --git a/governance/chat/program/tests/program_test/mod.rs b/governance/chat/program/tests/program_test/mod.rs index b66db7a9..138ae7fe 100644 --- a/governance/chat/program/tests/program_test/mod.rs +++ b/governance/chat/program/tests/program_test/mod.rs @@ -223,7 +223,7 @@ impl GovernanceChatProgramTest { let create_governance_ix = create_governance( &self.governance_program_id, &realm_address, - &governed_account_address, + Some(&governed_account_address), &token_owner_record_address, &self.bench.payer.pubkey(), &token_owner.pubkey(), diff --git a/governance/program/src/addins/voter_weight.rs b/governance/program/src/addins/voter_weight.rs index e9c939f3..fddc9317 100644 --- a/governance/program/src/addins/voter_weight.rs +++ b/governance/program/src/addins/voter_weight.rs @@ -7,7 +7,7 @@ use solana_program::{ use spl_governance_addin_api::voter_weight::{VoterWeightAction, VoterWeightRecord}; use spl_governance_tools::account::get_account_data; -use crate::{error::GovernanceError, state::token_owner_record::TokenOwnerRecord}; +use crate::{error::GovernanceError, state::token_owner_record::TokenOwnerRecordV2}; /// Asserts the VoterWeightRecord hasn't expired and matches the given action and target if specified pub fn assert_is_valid_voter_weight( @@ -53,7 +53,7 @@ pub fn get_voter_weight_record_data( pub fn get_voter_weight_record_data_for_token_owner_record( program_id: &Pubkey, voter_weight_record_info: &AccountInfo, - token_owner_record: &TokenOwnerRecord, + token_owner_record: &TokenOwnerRecordV2, ) -> Result { let voter_weight_record_data = get_voter_weight_record_data(program_id, voter_weight_record_info)?; diff --git a/governance/program/src/processor/process_add_signatory.rs b/governance/program/src/processor/process_add_signatory.rs index 962e3a49..e441821e 100644 --- a/governance/program/src/processor/process_add_signatory.rs +++ b/governance/program/src/processor/process_add_signatory.rs @@ -12,7 +12,7 @@ use spl_governance_tools::account::create_and_serialize_account_signed; use crate::state::{ enums::GovernanceAccountType, proposal::get_proposal_data, - signatory_record::{get_signatory_record_address_seeds, SignatoryRecord}, + signatory_record::{get_signatory_record_address_seeds, SignatoryRecordV2}, token_owner_record::get_token_owner_record_data_for_proposal_owner, }; @@ -46,14 +46,15 @@ pub fn process_add_signatory( token_owner_record_data.assert_token_owner_or_delegate_is_signer(governance_authority_info)?; - let signatory_record_data = SignatoryRecord { - account_type: GovernanceAccountType::SignatoryRecord, + let signatory_record_data = SignatoryRecordV2 { + account_type: GovernanceAccountType::SignatoryRecordV2, proposal: *proposal_info.key, signatory, signed_off: false, + reserved_v2: [0; 8], }; - create_and_serialize_account_signed::( + create_and_serialize_account_signed::( payer_info, signatory_record_info, &signatory_record_data, diff --git a/governance/program/src/processor/process_cancel_proposal.rs b/governance/program/src/processor/process_cancel_proposal.rs index 04fc464b..046cfc27 100644 --- a/governance/program/src/processor/process_cancel_proposal.rs +++ b/governance/program/src/processor/process_cancel_proposal.rs @@ -1,6 +1,5 @@ //! Program state processor -use borsh::BorshSerialize; use solana_program::{ account_info::{next_account_info, AccountInfo}, clock::Clock, diff --git a/governance/program/src/processor/process_cast_vote.rs b/governance/program/src/processor/process_cast_vote.rs index 3f362ae8..c6d1cb96 100644 --- a/governance/program/src/processor/process_cast_vote.rs +++ b/governance/program/src/processor/process_cast_vote.rs @@ -26,8 +26,6 @@ use crate::{ }, }; -use borsh::BorshSerialize; - /// Processes CastVote instruction pub fn process_cast_vote( program_id: &Pubkey, @@ -176,6 +174,8 @@ pub fn process_cast_vote( governance_data.serialize(&mut *governance_info.data.borrow_mut())?; } + let governing_token_owner = voter_token_owner_record_data.governing_token_owner; + voter_token_owner_record_data .serialize(&mut *voter_token_owner_record_info.data.borrow_mut())?; @@ -185,10 +185,11 @@ pub fn process_cast_vote( let vote_record_data = VoteRecordV2 { account_type: GovernanceAccountType::VoteRecordV2, proposal: *proposal_info.key, - governing_token_owner: voter_token_owner_record_data.governing_token_owner, + governing_token_owner, voter_weight, vote, is_relinquished: false, + reserved_v2: [0; 8], }; create_and_serialize_account_signed::( diff --git a/governance/program/src/processor/process_create_governance.rs b/governance/program/src/processor/process_create_governance.rs index 977d1117..49d39032 100644 --- a/governance/program/src/processor/process_create_governance.rs +++ b/governance/program/src/processor/process_create_governance.rs @@ -3,8 +3,8 @@ use crate::state::{ enums::GovernanceAccountType, governance::{ - assert_valid_create_governance_args, get_governance_address_seeds, Governance, - GovernanceConfig, + assert_valid_create_governance_args, get_governance_address_seeds, GovernanceConfig, + GovernanceV2, }, realm::get_realm_data, }; @@ -51,17 +51,18 @@ pub fn process_create_governance( account_info_iter, // realm_config_info 7, voter_weight_record_info 8 )?; - let governance_data = Governance { - account_type: GovernanceAccountType::Governance, + let governance_data = GovernanceV2 { + account_type: GovernanceAccountType::GovernanceV2, realm: *realm_info.key, governed_account: *governed_account_info.key, config, proposals_count: 0, reserved: [0; 6], voting_proposal_count: 0, + reserved_v2: [0; 128], }; - create_and_serialize_account_signed::( + create_and_serialize_account_signed::( payer_info, governance_info, &governance_data, diff --git a/governance/program/src/processor/process_create_mint_governance.rs b/governance/program/src/processor/process_create_mint_governance.rs index 2fe8ba27..37ba7eb4 100644 --- a/governance/program/src/processor/process_create_mint_governance.rs +++ b/governance/program/src/processor/process_create_mint_governance.rs @@ -4,8 +4,8 @@ use crate::{ state::{ enums::GovernanceAccountType, governance::{ - assert_valid_create_governance_args, get_mint_governance_address_seeds, Governance, - GovernanceConfig, + assert_valid_create_governance_args, get_mint_governance_address_seeds, + GovernanceConfig, GovernanceV2, }, realm::get_realm_data, }, @@ -63,17 +63,18 @@ pub fn process_create_mint_governance( account_info_iter, // realm_config_info 9, voter_weight_record_info 10 )?; - let mint_governance_data = Governance { - account_type: GovernanceAccountType::MintGovernance, + let mint_governance_data = GovernanceV2 { + account_type: GovernanceAccountType::MintGovernanceV2, realm: *realm_info.key, governed_account: *governed_mint_info.key, config, proposals_count: 0, reserved: [0; 6], voting_proposal_count: 0, + reserved_v2: [0; 128], }; - create_and_serialize_account_signed::( + create_and_serialize_account_signed::( payer_info, mint_governance_info, &mint_governance_data, diff --git a/governance/program/src/processor/process_create_program_governance.rs b/governance/program/src/processor/process_create_program_governance.rs index 5439ebff..26554931 100644 --- a/governance/program/src/processor/process_create_program_governance.rs +++ b/governance/program/src/processor/process_create_program_governance.rs @@ -1,7 +1,7 @@ //! Program state processor use crate::{ - state::governance::Governance, + state::governance::GovernanceV2, state::{ enums::GovernanceAccountType, governance::{ @@ -63,17 +63,18 @@ pub fn process_create_program_governance( account_info_iter, // realm_config_info 10, voter_weight_record_info 11 )?; - let program_governance_data = Governance { - account_type: GovernanceAccountType::ProgramGovernance, + let program_governance_data = GovernanceV2 { + account_type: GovernanceAccountType::ProgramGovernanceV2, realm: *realm_info.key, governed_account: *governed_program_info.key, config, proposals_count: 0, reserved: [0; 6], voting_proposal_count: 0, + reserved_v2: [0; 128], }; - create_and_serialize_account_signed::( + create_and_serialize_account_signed::( payer_info, program_governance_info, &program_governance_data, diff --git a/governance/program/src/processor/process_create_proposal.rs b/governance/program/src/processor/process_create_proposal.rs index 9b6d4907..fb269ca2 100644 --- a/governance/program/src/processor/process_create_proposal.rs +++ b/governance/program/src/processor/process_create_proposal.rs @@ -1,6 +1,5 @@ //! Program state processor -use borsh::BorshSerialize; use solana_program::{ account_info::{next_account_info, AccountInfo}, clock::Clock, diff --git a/governance/program/src/processor/process_create_realm.rs b/governance/program/src/processor/process_create_realm.rs index db6d4788..03d212b9 100644 --- a/governance/program/src/processor/process_create_realm.rs +++ b/governance/program/src/processor/process_create_realm.rs @@ -15,7 +15,7 @@ use crate::{ enums::GovernanceAccountType, realm::{ assert_valid_realm_config_args, get_governing_token_holding_address_seeds, - get_realm_address_seeds, Realm, RealmConfig, RealmConfigArgs, + get_realm_address_seeds, RealmConfig, RealmConfigArgs, RealmV2, }, realm_config::{get_realm_config_address_seeds, RealmConfigAccount}, }, @@ -125,8 +125,8 @@ pub fn process_create_realm( )?; } - let realm_data = Realm { - account_type: GovernanceAccountType::Realm, + let realm_data = RealmV2 { + account_type: GovernanceAccountType::RealmV2, community_mint: *governance_token_mint_info.key, name: name.clone(), @@ -143,9 +143,10 @@ pub fn process_create_realm( use_max_community_voter_weight_addin: config_args.use_max_community_voter_weight_addin, }, voting_proposal_count: 0, + reserved_v2: [0; 128], }; - create_and_serialize_account_signed::( + create_and_serialize_account_signed::( payer_info, realm_info, &realm_data, diff --git a/governance/program/src/processor/process_create_token_governance.rs b/governance/program/src/processor/process_create_token_governance.rs index 443dc33c..370ee5c8 100644 --- a/governance/program/src/processor/process_create_token_governance.rs +++ b/governance/program/src/processor/process_create_token_governance.rs @@ -4,8 +4,8 @@ use crate::{ state::{ enums::GovernanceAccountType, governance::{ - assert_valid_create_governance_args, get_token_governance_address_seeds, Governance, - GovernanceConfig, + assert_valid_create_governance_args, get_token_governance_address_seeds, + GovernanceConfig, GovernanceV2, }, realm::get_realm_data, }, @@ -61,17 +61,18 @@ pub fn process_create_token_governance( account_info_iter, // realm_config_info 9, voter_weight_record_info 10 )?; - let token_governance_data = Governance { - account_type: GovernanceAccountType::TokenGovernance, + let token_governance_data = GovernanceV2 { + account_type: GovernanceAccountType::TokenGovernanceV2, realm: *realm_info.key, governed_account: *governed_token_info.key, config, proposals_count: 0, reserved: [0; 6], voting_proposal_count: 0, + reserved_v2: [0; 128], }; - create_and_serialize_account_signed::( + create_and_serialize_account_signed::( payer_info, token_governance_info, &token_governance_data, diff --git a/governance/program/src/processor/process_create_token_owner_record.rs b/governance/program/src/processor/process_create_token_owner_record.rs index 0a09b606..4b7b7531 100644 --- a/governance/program/src/processor/process_create_token_owner_record.rs +++ b/governance/program/src/processor/process_create_token_owner_record.rs @@ -14,7 +14,7 @@ use crate::{ state::{ enums::GovernanceAccountType, realm::get_realm_data, - token_owner_record::{get_token_owner_record_address_seeds, TokenOwnerRecord}, + token_owner_record::{get_token_owner_record_address_seeds, TokenOwnerRecordV2}, }, }; @@ -40,8 +40,8 @@ pub fn process_create_token_owner_record( return Err(GovernanceError::TokenOwnerRecordAlreadyExists.into()); } - let token_owner_record_data = TokenOwnerRecord { - account_type: GovernanceAccountType::TokenOwnerRecord, + let token_owner_record_data = TokenOwnerRecordV2 { + account_type: GovernanceAccountType::TokenOwnerRecordV2, realm: *realm_info.key, governing_token_owner: *governing_token_owner_info.key, governing_token_deposit_amount: 0, @@ -51,6 +51,7 @@ pub fn process_create_token_owner_record( total_votes_count: 0, outstanding_proposal_count: 0, reserved: [0; 7], + reserved_v2: [0; 128], }; create_and_serialize_account_signed( diff --git a/governance/program/src/processor/process_deposit_governing_tokens.rs b/governance/program/src/processor/process_deposit_governing_tokens.rs index 58e88d83..edaf4f38 100644 --- a/governance/program/src/processor/process_deposit_governing_tokens.rs +++ b/governance/program/src/processor/process_deposit_governing_tokens.rs @@ -1,6 +1,5 @@ //! Program state processor -use borsh::BorshSerialize; use solana_program::{ account_info::{next_account_info, AccountInfo}, entrypoint::ProgramResult, @@ -17,7 +16,7 @@ use crate::{ realm::get_realm_data, token_owner_record::{ get_token_owner_record_address_seeds, get_token_owner_record_data_for_seeds, - TokenOwnerRecord, + TokenOwnerRecordV2, }, }, tools::spl_token::{get_spl_token_mint, get_spl_token_owner, transfer_spl_tokens}, @@ -79,8 +78,8 @@ pub fn process_deposit_governing_tokens( return Err(GovernanceError::GoverningTokenOwnerMustSign.into()); } - let token_owner_record_data = TokenOwnerRecord { - account_type: GovernanceAccountType::TokenOwnerRecord, + let token_owner_record_data = TokenOwnerRecordV2 { + account_type: GovernanceAccountType::TokenOwnerRecordV2, realm: *realm_info.key, governing_token_owner: *governing_token_owner_info.key, governing_token_deposit_amount: amount, @@ -90,6 +89,7 @@ pub fn process_deposit_governing_tokens( total_votes_count: 0, outstanding_proposal_count: 0, reserved: [0; 7], + reserved_v2: [0; 128], }; create_and_serialize_account_signed( diff --git a/governance/program/src/processor/process_finalize_vote.rs b/governance/program/src/processor/process_finalize_vote.rs index 308239c2..5f2673f4 100644 --- a/governance/program/src/processor/process_finalize_vote.rs +++ b/governance/program/src/processor/process_finalize_vote.rs @@ -15,8 +15,6 @@ use crate::state::{ token_owner_record::get_token_owner_record_data_for_proposal_owner, }; -use borsh::BorshSerialize; - /// Processes FinalizeVote instruction pub fn process_finalize_vote(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult { let account_info_iter = &mut accounts.iter(); diff --git a/governance/program/src/processor/process_insert_transaction.rs b/governance/program/src/processor/process_insert_transaction.rs index f71d298e..972dace0 100644 --- a/governance/program/src/processor/process_insert_transaction.rs +++ b/governance/program/src/processor/process_insert_transaction.rs @@ -94,6 +94,7 @@ pub fn process_insert_transaction( executed_at: None, execution_status: TransactionExecutionStatus::None, proposal: *proposal_info.key, + reserved_v2: [0; 8], }; create_and_serialize_account_signed::( diff --git a/governance/program/src/processor/process_relinquish_vote.rs b/governance/program/src/processor/process_relinquish_vote.rs index cd61f7cc..a4a27ec9 100644 --- a/governance/program/src/processor/process_relinquish_vote.rs +++ b/governance/program/src/processor/process_relinquish_vote.rs @@ -20,8 +20,6 @@ use crate::{ }, }; -use borsh::BorshSerialize; - /// Processes RelinquishVote instruction pub fn process_relinquish_vote(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult { let account_info_iter = &mut accounts.iter(); diff --git a/governance/program/src/processor/process_set_governance_config.rs b/governance/program/src/processor/process_set_governance_config.rs index 05f62e30..02cc8458 100644 --- a/governance/program/src/processor/process_set_governance_config.rs +++ b/governance/program/src/processor/process_set_governance_config.rs @@ -1,6 +1,5 @@ //! Program state processor -use borsh::BorshSerialize; use solana_program::{ account_info::{next_account_info, AccountInfo}, entrypoint::ProgramResult, diff --git a/governance/program/src/processor/process_set_governance_delegate.rs b/governance/program/src/processor/process_set_governance_delegate.rs index 797b05da..321d9590 100644 --- a/governance/program/src/processor/process_set_governance_delegate.rs +++ b/governance/program/src/processor/process_set_governance_delegate.rs @@ -1,6 +1,5 @@ //! Program state processor -use borsh::BorshSerialize; use solana_program::{ account_info::{next_account_info, AccountInfo}, entrypoint::ProgramResult, diff --git a/governance/program/src/processor/process_set_realm_authority.rs b/governance/program/src/processor/process_set_realm_authority.rs index dc9eb57c..1d2e83af 100644 --- a/governance/program/src/processor/process_set_realm_authority.rs +++ b/governance/program/src/processor/process_set_realm_authority.rs @@ -1,6 +1,5 @@ //! Program state processor -use borsh::BorshSerialize; use solana_program::{ account_info::{next_account_info, AccountInfo}, entrypoint::ProgramResult, diff --git a/governance/program/src/processor/process_sign_off_proposal.rs b/governance/program/src/processor/process_sign_off_proposal.rs index bb50d32c..bf15e2e8 100644 --- a/governance/program/src/processor/process_sign_off_proposal.rs +++ b/governance/program/src/processor/process_sign_off_proposal.rs @@ -1,6 +1,5 @@ //! Program state processor -use borsh::BorshSerialize; use solana_program::{ account_info::{next_account_info, AccountInfo}, clock::Clock, diff --git a/governance/program/src/processor/process_withdraw_governing_tokens.rs b/governance/program/src/processor/process_withdraw_governing_tokens.rs index 6d8b12e6..fca2d792 100644 --- a/governance/program/src/processor/process_withdraw_governing_tokens.rs +++ b/governance/program/src/processor/process_withdraw_governing_tokens.rs @@ -1,6 +1,5 @@ //! Program state processor -use borsh::BorshSerialize; use solana_program::{ account_info::{next_account_info, AccountInfo}, entrypoint::ProgramResult, diff --git a/governance/program/src/state/enums.rs b/governance/program/src/state/enums.rs index c615c310..a93dfad3 100644 --- a/governance/program/src/state/enums.rs +++ b/governance/program/src/state/enums.rs @@ -10,22 +10,22 @@ pub enum GovernanceAccountType { Uninitialized, /// Top level aggregation for governances with Community Token (and optional Council Token) - Realm, + RealmV1, /// Token Owner Record for given governing token owner within a Realm - TokenOwnerRecord, + TokenOwnerRecordV1, /// Governance account - Governance, + GovernanceV1, /// Program Governance account - ProgramGovernance, + ProgramGovernanceV1, /// Proposal account for Governance account. A single Governance account can have multiple Proposal accounts ProposalV1, /// Proposal Signatory account - SignatoryRecord, + SignatoryRecordV1, /// Vote record account for a given Proposal. Proposal can have 0..n voting records VoteRecordV1, @@ -34,12 +34,12 @@ pub enum GovernanceAccountType { ProposalInstructionV1, /// Mint Governance account - MintGovernance, + MintGovernanceV1, /// Token Governance account - TokenGovernance, + TokenGovernanceV1, - /// Realm config account + /// Realm config account (introduced in V2) RealmConfig, /// Vote record account for a given Proposal. Proposal can have 0..n voting records @@ -54,8 +54,40 @@ pub enum GovernanceAccountType { /// V2 adds support for multiple vote options ProposalV2, - /// Program metadata account. It stores information about the particular SPL-Governance program instance + /// Program metadata account (introduced in V2) + /// It stores information about the particular SPL-Governance program instance ProgramMetadata, + + /// Top level aggregation for governances with Community Token (and optional Council Token) + /// V2 adds the following fields: + /// 1) use_community_voter_weight_addin and use_max_community_voter_weight_addin to RealmConfig + /// 2) voting_proposal_count + /// 3) extra reserved space reserved_v2 + RealmV2, + + /// Token Owner Record for given governing token owner within a Realm + /// V2 adds extra reserved space reserved_v2 + TokenOwnerRecordV2, + + /// Governance account + /// V2 adds extra reserved space reserved_v2 + GovernanceV2, + + /// Program Governance account + /// V2 adds extra reserved space reserved_v2 + ProgramGovernanceV2, + + /// Mint Governance account + /// V2 adds extra reserved space reserved_v2 + MintGovernanceV2, + + /// Token Governance account + /// V2 adds extra reserved space reserved_v2 + TokenGovernanceV2, + + /// Proposal Signatory account + /// V2 adds extra reserved space reserved_v2 + SignatoryRecordV2, } impl Default for GovernanceAccountType { diff --git a/governance/program/src/state/governance.rs b/governance/program/src/state/governance.rs index 3cd37e8c..a452cfbd 100644 --- a/governance/program/src/state/governance.rs +++ b/governance/program/src/state/governance.rs @@ -1,19 +1,21 @@ //! Governance Account +use borsh::maybestd::io::Write; use crate::{ error::GovernanceError, state::{ enums::{GovernanceAccountType, VoteThresholdPercentage, VoteTipping}, + legacy::{is_governance_v1_account_type, GovernanceV1}, realm::assert_is_valid_realm, }, }; use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; use solana_program::{ - account_info::AccountInfo, program_error::ProgramError, program_pack::IsInitialized, - pubkey::Pubkey, + account_info::AccountInfo, borsh::try_from_slice_unchecked, program_error::ProgramError, + program_pack::IsInitialized, pubkey::Pubkey, }; use spl_governance_tools::{ - account::{assert_is_valid_account2, get_account_data, AccountMaxSize}, + account::{assert_is_valid_account_of_types, get_account_data, AccountMaxSize}, error::GovernanceToolsError, }; @@ -49,7 +51,7 @@ pub struct GovernanceConfig { /// Governance Account #[repr(C)] #[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] -pub struct Governance { +pub struct GovernanceV2 { /// Account type. It can be Uninitialized, Governance, ProgramGovernance, TokenGovernance or MintGovernance pub account_type: GovernanceAccountType, @@ -78,33 +80,43 @@ pub struct Governance { /// The number of proposals in voting state in the Governance pub voting_proposal_count: u16, + + /// Reserved space for versions v2 and onwards + /// Note: This space won't be available to v1 accounts until runtime supports resizing + pub reserved_v2: [u8; 128], } -impl AccountMaxSize for Governance {} +impl AccountMaxSize for GovernanceV2 {} -impl IsInitialized for Governance { +/// Checks if the given account type is one of the Governance account types +pub fn is_governance_v2_account_type(account_type: &GovernanceAccountType) -> bool { + *account_type == GovernanceAccountType::GovernanceV2 + || *account_type == GovernanceAccountType::ProgramGovernanceV2 + || *account_type == GovernanceAccountType::MintGovernanceV2 + || *account_type == GovernanceAccountType::TokenGovernanceV2 +} + +impl IsInitialized for GovernanceV2 { fn is_initialized(&self) -> bool { - self.account_type == GovernanceAccountType::Governance - || self.account_type == GovernanceAccountType::ProgramGovernance - || self.account_type == GovernanceAccountType::MintGovernance - || self.account_type == GovernanceAccountType::TokenGovernance + is_governance_v2_account_type(&self.account_type) } } -impl Governance { +impl GovernanceV2 { /// Returns Governance PDA seeds pub fn get_governance_address_seeds(&self) -> Result<[&[u8]; 3], ProgramError> { let seeds = match self.account_type { - GovernanceAccountType::Governance => { + GovernanceAccountType::GovernanceV1 | GovernanceAccountType::GovernanceV2 => { get_governance_address_seeds(&self.realm, &self.governed_account) } - GovernanceAccountType::ProgramGovernance => { + GovernanceAccountType::ProgramGovernanceV1 + | GovernanceAccountType::ProgramGovernanceV2 => { get_program_governance_address_seeds(&self.realm, &self.governed_account) } - GovernanceAccountType::MintGovernance => { + GovernanceAccountType::MintGovernanceV1 | GovernanceAccountType::MintGovernanceV2 => { get_mint_governance_address_seeds(&self.realm, &self.governed_account) } - GovernanceAccountType::TokenGovernance => { + GovernanceAccountType::TokenGovernanceV1 | GovernanceAccountType::TokenGovernanceV2 => { get_token_governance_address_seeds(&self.realm, &self.governed_account) } _ => return Err(GovernanceToolsError::InvalidAccountType.into()), @@ -112,14 +124,67 @@ impl Governance { Ok(seeds) } + + /// Serializes account into the target buffer + pub fn serialize(self, writer: &mut W) -> Result<(), ProgramError> { + if is_governance_v2_account_type(&self.account_type) { + BorshSerialize::serialize(&self, writer)? + } 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 asses for v1 backward compatibility impact + if self.reserved_v2 != [0; 128] { + panic!("Extended data not supported by GovernanceV1") + } + + let governance_data_v1 = GovernanceV1 { + account_type: self.account_type, + realm: self.realm, + governed_account: self.governed_account, + proposals_count: self.proposals_count, + config: self.config, + reserved: self.reserved, + voting_proposal_count: self.voting_proposal_count, + }; + + BorshSerialize::serialize(&governance_data_v1, writer)?; + } + + Ok(()) + } } /// Deserializes Governance account and checks owner program pub fn get_governance_data( program_id: &Pubkey, governance_info: &AccountInfo, -) -> Result { - get_account_data::(program_id, governance_info) +) -> Result { + if governance_info.data_is_empty() { + return Err(GovernanceToolsError::AccountDoesNotExist.into()); + } + + let account_type: GovernanceAccountType = + try_from_slice_unchecked(&governance_info.data.borrow())?; + + // If the account is V1 version then translate to V2 + if is_governance_v1_account_type(&account_type) { + let governance_data_v1 = get_account_data::(program_id, governance_info)?; + + return Ok(GovernanceV2 { + account_type, + realm: governance_data_v1.realm, + governed_account: governance_data_v1.governed_account, + proposals_count: governance_data_v1.proposals_count, + config: governance_data_v1.config, + reserved: governance_data_v1.reserved, + voting_proposal_count: governance_data_v1.voting_proposal_count, + + // Add the extra reserved_v2 padding + reserved_v2: [0; 128], + }); + } + + get_account_data::(program_id, governance_info) } /// Deserializes Governance account, checks owner program and asserts governance belongs to the given ream @@ -127,7 +192,7 @@ pub fn get_governance_data_for_realm( program_id: &Pubkey, governance_info: &AccountInfo, realm: &Pubkey, -) -> Result { +) -> Result { let governance_data = get_governance_data(program_id, governance_info)?; if governance_data.realm != *realm { @@ -250,13 +315,17 @@ pub fn assert_is_valid_governance( program_id: &Pubkey, governance_info: &AccountInfo, ) -> Result<(), ProgramError> { - assert_is_valid_account2( + assert_is_valid_account_of_types( governance_info, &[ - GovernanceAccountType::Governance, - GovernanceAccountType::ProgramGovernance, - GovernanceAccountType::TokenGovernance, - GovernanceAccountType::MintGovernance, + GovernanceAccountType::GovernanceV1, + GovernanceAccountType::GovernanceV2, + GovernanceAccountType::ProgramGovernanceV1, + GovernanceAccountType::ProgramGovernanceV2, + GovernanceAccountType::TokenGovernanceV1, + GovernanceAccountType::TokenGovernanceV2, + GovernanceAccountType::MintGovernanceV1, + GovernanceAccountType::MintGovernanceV2, ], program_id, ) diff --git a/governance/program/src/state/legacy.rs b/governance/program/src/state/legacy.rs index 349ba393..454c30ca 100644 --- a/governance/program/src/state/legacy.rs +++ b/governance/program/src/state/legacy.rs @@ -2,10 +2,12 @@ use crate::state::{ enums::{ - GovernanceAccountType, InstructionExecutionFlags, MintMaxVoteWeightSource, ProposalState, + GovernanceAccountType, InstructionExecutionFlags, ProposalState, TransactionExecutionStatus, VoteThresholdPercentage, }, + governance::GovernanceConfig, proposal_transaction::InstructionData, + realm::RealmConfig, }; use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; use solana_program::{ @@ -14,58 +16,6 @@ use solana_program::{ pubkey::Pubkey, }; -/// Realm Config instruction args -#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] -pub struct RealmConfigArgsV1 { - /// Indicates whether council_mint should be used - /// If yes then council_mint account must also be passed to the instruction - pub use_council_mint: bool, - - /// Min number of community tokens required to create a governance - pub min_community_weight_to_create_governance: u64, - - /// The source used for community mint max vote weight source - pub community_mint_max_vote_weight_source: MintMaxVoteWeightSource, -} - -/// Instructions supported by the Governance program -#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] -pub enum GovernanceInstructionV1 { - /// Creates Governance Realm account which aggregates governances for given Community Mint and optional Council Mint - CreateRealm { - #[allow(dead_code)] - /// UTF-8 encoded Governance Realm name - name: String, - - #[allow(dead_code)] - /// Realm config args - config_args: RealmConfigArgsV1, - }, - - /// Deposits governing tokens (Community or Council) to Governance Realm and establishes your voter weight to be used for voting within the Realm - DepositGoverningTokens { - /// The amount to deposit into the realm - #[allow(dead_code)] - amount: u64, - }, -} - -/// Realm Config defining Realm parameters. -#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] -pub struct RealmConfigV1 { - /// Reserved space for future versions - pub reserved: [u8; 8], - - /// Min number of community tokens required to create a governance - pub min_community_weight_to_create_governance: u64, - - /// The source used for community mint max vote weight source - pub community_mint_max_vote_weight_source: MintMaxVoteWeightSource, - - /// Optional council mint - pub council_mint: Option, -} - /// Governance Realm Account /// Account PDA seeds" ['governance', name] #[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] @@ -77,10 +27,15 @@ pub struct RealmV1 { pub community_mint: Pubkey, /// Configuration of the Realm - pub config: RealmConfigV1, + pub config: RealmConfig, /// Reserved space for future versions - pub reserved: [u8; 8], + pub reserved: [u8; 6], + + /// The number of proposals in voting state in the Realm + /// Note: This is field introduced in V2 but it took space from reserved + /// and we have preserve it for V1 serialization roundtrip + pub voting_proposal_count: u16, /// Realm authority. The authority must sign transactions which update the realm config /// The authority should be transferred to Realm Governance to make the Realm self governed through proposals @@ -92,7 +47,107 @@ pub struct RealmV1 { impl IsInitialized for RealmV1 { fn is_initialized(&self) -> bool { - self.account_type == GovernanceAccountType::Realm + self.account_type == GovernanceAccountType::RealmV1 + } +} + +/// Governance Token Owner Record +/// Account PDA seeds: ['governance', realm, token_mint, token_owner ] +#[repr(C)] +#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] +pub struct TokenOwnerRecordV1 { + /// Governance account type + pub account_type: GovernanceAccountType, + + /// The Realm the TokenOwnerRecord belongs to + pub realm: Pubkey, + + /// Governing Token Mint the TokenOwnerRecord holds deposit for + pub governing_token_mint: Pubkey, + + /// The owner (either single or multisig) of the deposited governing SPL Tokens + /// This is who can authorize a withdrawal of the tokens + pub governing_token_owner: Pubkey, + + /// The amount of governing tokens deposited into the Realm + /// This amount is the voter weight used when voting on proposals + pub governing_token_deposit_amount: u64, + + /// The number of votes cast by TokenOwner but not relinquished yet + /// Every time a vote is cast this number is increased and it's always decreased when relinquishing a vote regardless of the vote state + pub unrelinquished_votes_count: u32, + + /// The total number of votes cast by the TokenOwner + /// If TokenOwner withdraws vote while voting is still in progress total_votes_count is decreased and the vote doesn't count towards the total + pub total_votes_count: u32, + + /// The number of outstanding proposals the TokenOwner currently owns + /// The count is increased when TokenOwner creates a proposal + /// and decreased once it's either voted on (Succeeded or Defeated) or Cancelled + /// By default it's restricted to 1 outstanding Proposal per token owner + pub outstanding_proposal_count: u8, + + /// Reserved space for future versions + pub reserved: [u8; 7], + + /// A single account that is allowed to operate governance with the deposited governing tokens + /// It can be delegated to by the governing_token_owner or current governance_delegate + pub governance_delegate: Option, +} + +impl IsInitialized for TokenOwnerRecordV1 { + fn is_initialized(&self) -> bool { + self.account_type == GovernanceAccountType::TokenOwnerRecordV1 + } +} + +/// Governance Account +#[repr(C)] +#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] +pub struct GovernanceV1 { + /// Account type. It can be Uninitialized, Governance, ProgramGovernance, TokenGovernance or MintGovernance + pub account_type: GovernanceAccountType, + + /// Governance Realm + pub realm: Pubkey, + + /// Account governed by this Governance and/or PDA identity seed + /// It can be Program account, Mint account, Token account or any other account + /// + /// Note: The account doesn't have to exist. In that case the field is only a PDA seed + /// + /// Note: Setting governed_account doesn't give any authority over the governed account + /// The relevant authorities for specific account types must still be transferred to the Governance PDA + /// Ex: mint_authority/freeze_authority for a Mint account + /// or upgrade_authority for a Program account should be transferred to the Governance PDA + pub governed_account: Pubkey, + + /// Running count of proposals + pub proposals_count: u32, + + /// Governance config + pub config: GovernanceConfig, + + /// Reserved space for future versions + pub reserved: [u8; 6], + + /// The number of proposals in voting state in the Governance + /// Note: This is field introduced in V2 but it took space from reserved + /// and we have preserve it for V1 serialization roundtrip + pub voting_proposal_count: u16, +} + +/// Checks if the given account type is one of the Governance account types +pub fn is_governance_v1_account_type(account_type: &GovernanceAccountType) -> bool { + *account_type == GovernanceAccountType::GovernanceV1 + || *account_type == GovernanceAccountType::ProgramGovernanceV1 + || *account_type == GovernanceAccountType::MintGovernanceV1 + || *account_type == GovernanceAccountType::TokenGovernanceV1 +} + +impl IsInitialized for GovernanceV1 { + fn is_initialized(&self) -> bool { + is_governance_v1_account_type(&self.account_type) } } @@ -186,6 +241,29 @@ impl IsInitialized for ProposalV1 { } } +/// Account PDA seeds: ['governance', proposal, signatory] +#[repr(C)] +#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] +pub struct SignatoryRecordV1 { + /// Governance account type + pub account_type: GovernanceAccountType, + + /// Proposal the signatory is assigned for + pub proposal: Pubkey, + + /// The account of the signatory who can sign off the proposal + pub signatory: Pubkey, + + /// Indicates whether the signatory signed off the proposal + pub signed_off: bool, +} + +impl IsInitialized for SignatoryRecordV1 { + fn is_initialized(&self) -> bool { + self.account_type == GovernanceAccountType::SignatoryRecordV1 + } +} + /// Proposal instruction V1 #[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] pub struct ProposalInstructionV1 { diff --git a/governance/program/src/state/program_metadata.rs b/governance/program/src/state/program_metadata.rs index e45cda09..d36c2061 100644 --- a/governance/program/src/state/program_metadata.rs +++ b/governance/program/src/state/program_metadata.rs @@ -65,7 +65,7 @@ mod test { #[test] fn test_max_size() { let program_metadata_data = ProgramMetadata { - account_type: GovernanceAccountType::TokenOwnerRecord, + account_type: GovernanceAccountType::TokenOwnerRecordV2, updated_at: 10, reserved: [0; 64], version: "111.122.155".to_string(), diff --git a/governance/program/src/state/proposal.rs b/governance/program/src/state/proposal.rs index 535f1ffe..21043fc6 100644 --- a/governance/program/src/state/proposal.rs +++ b/governance/program/src/state/proposal.rs @@ -29,7 +29,7 @@ use crate::{ }, governance::GovernanceConfig, proposal_transaction::ProposalTransactionV2, - realm::Realm, + realm::RealmV2, realm_config::get_realm_config_data_for_realm, vote_record::Vote, }, @@ -413,7 +413,7 @@ impl ProposalV2 { /// Calculates max voter weight for given mint supply and realm config fn get_max_voter_weight_from_mint_supply( &mut self, - realm_data: &Realm, + realm_data: &RealmV2, governing_token_mint_supply: u64, ) -> Result { // max vote weight fraction is only used for community mint @@ -464,7 +464,7 @@ impl ProposalV2 { governing_token_mint_info: &AccountInfo, account_info_iter: &mut Iter, realm: &Pubkey, - realm_data: &Realm, + realm_data: &RealmV2, ) -> Result { // if the realm uses addin for max community voter weight then use the externally provided max weight if realm_data.config.use_max_community_voter_weight_addin @@ -774,7 +774,7 @@ impl ProposalV2 { } if self.options.len() != 1 { - panic!("ProposalV1 doesn't multiple options") + panic!("ProposalV1 doesn't support multiple options") } let proposal_data_v1 = ProposalV1 { @@ -1011,7 +1011,7 @@ mod test { fn create_test_proposal() -> ProposalV2 { ProposalV2 { - account_type: GovernanceAccountType::TokenOwnerRecord, + account_type: GovernanceAccountType::TokenOwnerRecordV2, governance: Pubkey::new_unique(), governing_token_mint: Pubkey::new_unique(), max_vote_weight: Some(10), @@ -1087,9 +1087,9 @@ mod test { proposal } - fn create_test_realm() -> Realm { - Realm { - account_type: GovernanceAccountType::Realm, + fn create_test_realm() -> RealmV2 { + RealmV2 { + account_type: GovernanceAccountType::RealmV2, community_mint: Pubkey::new_unique(), reserved: [0; 6], @@ -1106,6 +1106,7 @@ mod test { min_community_weight_to_create_governance: 10, }, voting_proposal_count: 0, + reserved_v2: [0; 128], } } diff --git a/governance/program/src/state/proposal_transaction.rs b/governance/program/src/state/proposal_transaction.rs index 382550e2..b3037980 100644 --- a/governance/program/src/state/proposal_transaction.rs +++ b/governance/program/src/state/proposal_transaction.rs @@ -114,6 +114,10 @@ pub struct ProposalTransactionV2 { /// Instruction execution status pub execution_status: TransactionExecutionStatus, + + /// Reserved space for versions v2 and onwards + /// Note: This space won't be available to v1 accounts until runtime supports resizing + pub reserved_v2: [u8; 8], } impl AccountMaxSize for ProposalTransactionV2 { @@ -125,7 +129,7 @@ impl AccountMaxSize for ProposalTransactionV2 { .sum::() + 4; - Some(instructions_size + 90) + Some(instructions_size + 98) } } @@ -146,6 +150,12 @@ impl ProposalTransactionV2 { }; // 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 asses for v1 backward compatibility impact + if self.reserved_v2 != [0; 8] { + panic!("Extended data not supported by ProposalInstructionV1") + } + let proposal_transaction_data_v1 = ProposalInstructionV1 { account_type: self.account_type, proposal: self.proposal, @@ -217,6 +227,7 @@ pub fn get_proposal_transaction_data( instructions: vec![proposal_transaction_data_v1.instruction], executed_at: proposal_transaction_data_v1.executed_at, execution_status: proposal_transaction_data_v1.execution_status, + reserved_v2: [0; 8], }); } @@ -277,6 +288,7 @@ mod test { instructions: create_test_instruction_data(), executed_at: Some(100), execution_status: TransactionExecutionStatus::Success, + reserved_v2: [0; 8], } } diff --git a/governance/program/src/state/realm.rs b/governance/program/src/state/realm.rs index a2ae8973..30f1dd8f 100644 --- a/governance/program/src/state/realm.rs +++ b/governance/program/src/state/realm.rs @@ -1,21 +1,26 @@ //! Realm Account +use borsh::maybestd::io::Write; use std::slice::Iter; use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; use solana_program::{ account_info::{next_account_info, AccountInfo}, + borsh::try_from_slice_unchecked, program_error::ProgramError, program_pack::IsInitialized, pubkey::Pubkey, }; use spl_governance_addin_api::voter_weight::VoterWeightAction; -use spl_governance_tools::account::{assert_is_valid_account, get_account_data, AccountMaxSize}; +use spl_governance_tools::account::{ + assert_is_valid_account_of_types, get_account_data, AccountMaxSize, +}; use crate::{ error::GovernanceError, state::{ enums::{GovernanceAccountType, MintMaxVoteWeightSource}, + legacy::RealmV1, token_owner_record::get_token_owner_record_data_for_realm, }, PROGRAM_AUTHORITY_SEED, @@ -87,7 +92,7 @@ pub struct RealmConfig { /// Account PDA seeds" ['governance', name] #[repr(C)] #[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] -pub struct Realm { +pub struct RealmV2 { /// Governance account type pub account_type: GovernanceAccountType, @@ -109,21 +114,25 @@ pub struct Realm { /// Governance Realm name pub name: String, + + /// Reserved space for versions v2 and onwards + /// Note: This space won't be available to v1 accounts until runtime supports resizing + pub reserved_v2: [u8; 128], } -impl AccountMaxSize for Realm { +impl AccountMaxSize for RealmV2 { fn get_max_size(&self) -> Option { - Some(self.name.len() + 136) + Some(self.name.len() + 264) } } -impl IsInitialized for Realm { +impl IsInitialized for RealmV2 { fn is_initialized(&self) -> bool { - self.account_type == GovernanceAccountType::Realm + self.account_type == GovernanceAccountType::RealmV2 } } -impl Realm { +impl RealmV2 { /// Asserts the given mint is either Community or Council mint of the Realm pub fn assert_is_valid_governing_token_mint( &self, @@ -215,6 +224,34 @@ impl Realm { Ok(()) } + + /// Serializes account into the target buffer + pub fn serialize(self, writer: &mut W) -> Result<(), ProgramError> { + if self.account_type == GovernanceAccountType::RealmV2 { + BorshSerialize::serialize(&self, writer)? + } else if self.account_type == GovernanceAccountType::RealmV1 { + // 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 asses for v1 backward compatibility impact + if self.reserved_v2 != [0; 128] { + panic!("Extended data not supported by RealmV1") + } + + let realm_data_v1 = RealmV1 { + account_type: self.account_type, + community_mint: self.community_mint, + config: self.config, + reserved: self.reserved, + voting_proposal_count: self.voting_proposal_count, + authority: self.authority, + name: self.name, + }; + + BorshSerialize::serialize(&realm_data_v1, writer)?; + } + + Ok(()) + } } /// Checks whether realm account exists, is initialized and owned by Governance program @@ -222,15 +259,41 @@ pub fn assert_is_valid_realm( program_id: &Pubkey, realm_info: &AccountInfo, ) -> Result<(), ProgramError> { - assert_is_valid_account(realm_info, GovernanceAccountType::Realm, program_id) + assert_is_valid_account_of_types( + realm_info, + &[ + GovernanceAccountType::RealmV1, + GovernanceAccountType::RealmV2, + ], + program_id, + ) } /// Deserializes account and checks owner program pub fn get_realm_data( program_id: &Pubkey, realm_info: &AccountInfo, -) -> Result { - get_account_data::(program_id, realm_info) +) -> Result { + let account_type: GovernanceAccountType = try_from_slice_unchecked(&realm_info.data.borrow())?; + + // If the account is V1 version then translate to V2 + if account_type == GovernanceAccountType::ProposalV1 { + let realm_data_v1 = get_account_data::(program_id, realm_info)?; + + return Ok(RealmV2 { + account_type, + community_mint: realm_data_v1.community_mint, + config: realm_data_v1.config, + reserved: realm_data_v1.reserved, + voting_proposal_count: realm_data_v1.voting_proposal_count, + authority: realm_data_v1.authority, + name: realm_data_v1.name, + // Add the extra reserved_v2 padding + reserved_v2: [0; 128], + }); + } + + get_account_data::(program_id, realm_info) } /// Deserializes account and checks the given authority is Realm's authority @@ -238,8 +301,8 @@ pub fn get_realm_data_for_authority( program_id: &Pubkey, realm_info: &AccountInfo, realm_authority: &Pubkey, -) -> Result { - let realm_data = get_account_data::(program_id, realm_info)?; +) -> Result { + let realm_data = get_account_data::(program_id, realm_info)?; if realm_data.authority.is_none() { return Err(GovernanceError::RealmHasNoAuthority.into()); @@ -257,7 +320,7 @@ pub fn get_realm_data_for_governing_token_mint( program_id: &Pubkey, realm_info: &AccountInfo, governing_token_mint: &Pubkey, -) -> Result { +) -> Result { let realm_data = get_realm_data(program_id, realm_info)?; realm_data.assert_is_valid_governing_token_mint(governing_token_mint)?; @@ -319,18 +382,15 @@ pub fn assert_valid_realm_config_args(config_args: &RealmConfigArgs) -> Result<( #[cfg(test)] mod test { - use crate::{ - instruction::GovernanceInstruction, - state::legacy::{GovernanceInstructionV1, RealmConfigV1, RealmV1}, - }; + use crate::instruction::GovernanceInstruction; use solana_program::borsh::try_from_slice_unchecked; use super::*; #[test] fn test_max_size() { - let realm = Realm { - account_type: GovernanceAccountType::Realm, + let realm = RealmV2 { + account_type: GovernanceAccountType::RealmV2, community_mint: Pubkey::new_unique(), reserved: [0; 6], @@ -346,6 +406,7 @@ mod test { }, voting_proposal_count: 0, + reserved_v2: [0; 128], }; let size = realm.try_to_vec().unwrap().len(); @@ -353,36 +414,40 @@ mod test { assert_eq!(realm.get_max_size(), Some(size)); } - #[test] - fn test_deserialize_v2_realm_account_from_v1() { - // Arrange - let realm_v1 = RealmV1 { - account_type: GovernanceAccountType::Realm, - community_mint: Pubkey::new_unique(), - config: RealmConfigV1 { - council_mint: Some(Pubkey::new_unique()), - reserved: [0; 8], - community_mint_max_vote_weight_source: MintMaxVoteWeightSource::Absolute(100), - min_community_weight_to_create_governance: 10, - }, - reserved: [0; 8], - authority: Some(Pubkey::new_unique()), - name: "test-realm-v1".to_string(), - }; + /// Realm Config instruction args + #[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] + pub struct RealmConfigArgsV1 { + /// Indicates whether council_mint should be used + /// If yes then council_mint account must also be passed to the instruction + pub use_council_mint: bool, - let mut realm_v1_data = vec![]; - realm_v1.serialize(&mut realm_v1_data).unwrap(); + /// Min number of community tokens required to create a governance + pub min_community_weight_to_create_governance: u64, - // Act - let realm_v2: Realm = try_from_slice_unchecked(&realm_v1_data).unwrap(); + /// The source used for community mint max vote weight source + pub community_mint_max_vote_weight_source: MintMaxVoteWeightSource, + } - // Assert - assert!(!realm_v2.config.use_community_voter_weight_addin); - assert_eq!(realm_v2.account_type, GovernanceAccountType::Realm); - assert_eq!( - realm_v2.config.min_community_weight_to_create_governance, - realm_v1.config.min_community_weight_to_create_governance, - ); + /// Instructions supported by the Governance program + #[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] + pub enum GovernanceInstructionV1 { + /// Creates Governance Realm account which aggregates governances for given Community Mint and optional Council Mint + CreateRealm { + #[allow(dead_code)] + /// UTF-8 encoded Governance Realm name + name: String, + + #[allow(dead_code)] + /// Realm config args + config_args: RealmConfigArgsV1, + }, + + /// Deposits governing tokens (Community or Council) to Governance Realm and establishes your voter weight to be used for voting within the Realm + DepositGoverningTokens { + /// The amount to deposit into the realm + #[allow(dead_code)] + amount: u64, + }, } #[test] diff --git a/governance/program/src/state/realm_config.rs b/governance/program/src/state/realm_config.rs index a5b97805..3b6bec58 100644 --- a/governance/program/src/state/realm_config.rs +++ b/governance/program/src/state/realm_config.rs @@ -92,7 +92,7 @@ mod test { #[test] fn test_max_size() { let realm_config = RealmConfigAccount { - account_type: GovernanceAccountType::Realm, + account_type: GovernanceAccountType::RealmV2, realm: Pubkey::new_unique(), community_voter_weight_addin: Some(Pubkey::new_unique()), max_community_voter_weight_addin: Some(Pubkey::new_unique()), diff --git a/governance/program/src/state/signatory_record.rs b/governance/program/src/state/signatory_record.rs index 337b1efe..efeec258 100644 --- a/governance/program/src/state/signatory_record.rs +++ b/governance/program/src/state/signatory_record.rs @@ -1,6 +1,9 @@ //! Signatory Record +use borsh::maybestd::io::Write; + use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; +use solana_program::borsh::try_from_slice_unchecked; use solana_program::{ account_info::AccountInfo, program_error::ProgramError, program_pack::IsInitialized, pubkey::Pubkey, @@ -11,10 +14,12 @@ use crate::{error::GovernanceError, PROGRAM_AUTHORITY_SEED}; use crate::state::enums::GovernanceAccountType; +use super::legacy::SignatoryRecordV1; + /// Account PDA seeds: ['governance', proposal, signatory] #[repr(C)] #[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] -pub struct SignatoryRecord { +pub struct SignatoryRecordV2 { /// Governance account type pub account_type: GovernanceAccountType, @@ -26,17 +31,21 @@ pub struct SignatoryRecord { /// Indicates whether the signatory signed off the proposal pub signed_off: bool, + + /// Reserved space for versions v2 and onwards + /// Note: This space won't be available to v1 accounts until runtime supports resizing + pub reserved_v2: [u8; 8], } -impl AccountMaxSize for SignatoryRecord {} +impl AccountMaxSize for SignatoryRecordV2 {} -impl IsInitialized for SignatoryRecord { +impl IsInitialized for SignatoryRecordV2 { fn is_initialized(&self) -> bool { - self.account_type == GovernanceAccountType::SignatoryRecord + self.account_type == GovernanceAccountType::SignatoryRecordV2 } } -impl SignatoryRecord { +impl SignatoryRecordV2 { /// Checks signatory hasn't signed off yet and is transaction signer pub fn assert_can_sign_off(&self, signatory_info: &AccountInfo) -> Result<(), ProgramError> { if self.signed_off { @@ -58,6 +67,31 @@ impl SignatoryRecord { Ok(()) } + + /// Serializes account into the target buffer + pub fn serialize(self, writer: &mut W) -> Result<(), ProgramError> { + if self.account_type == GovernanceAccountType::SignatoryRecordV2 { + BorshSerialize::serialize(&self, writer)? + } else if self.account_type == GovernanceAccountType::SignatoryRecordV1 { + // 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 asses for v1 backward compatibility impact + if self.reserved_v2 != [0; 8] { + panic!("Extended data not supported by SignatoryRecordV1") + } + + let signatory_record_data_v1 = SignatoryRecordV1 { + account_type: self.account_type, + proposal: self.proposal, + signatory: self.signatory, + signed_off: self.signed_off, + }; + + BorshSerialize::serialize(&signatory_record_data_v1, writer)?; + } + + Ok(()) + } } /// Returns SignatoryRecord PDA seeds @@ -89,8 +123,28 @@ pub fn get_signatory_record_address<'a>( pub fn get_signatory_record_data( program_id: &Pubkey, signatory_record_info: &AccountInfo, -) -> Result { - get_account_data::(program_id, signatory_record_info) +) -> Result { + let account_type: GovernanceAccountType = + try_from_slice_unchecked(&signatory_record_info.data.borrow())?; + + // If the account is V1 version then translate to V2 + if account_type == GovernanceAccountType::SignatoryRecordV1 { + let signatory_record_data_v1 = + get_account_data::(program_id, signatory_record_info)?; + + return Ok(SignatoryRecordV2 { + account_type, + + proposal: signatory_record_data_v1.proposal, + signatory: signatory_record_data_v1.signatory, + signed_off: signatory_record_data_v1.signed_off, + + // Add the extra reserved_v2 padding + reserved_v2: [0; 8], + }); + } + + get_account_data::(program_id, signatory_record_info) } /// Deserializes SignatoryRecord and validates its PDA @@ -99,7 +153,7 @@ pub fn get_signatory_record_data_for_seeds( signatory_record_info: &AccountInfo, proposal: &Pubkey, signatory: &Pubkey, -) -> Result { +) -> Result { let (signatory_record_address, _) = Pubkey::find_program_address( &get_signatory_record_address_seeds(proposal, signatory), program_id, diff --git a/governance/program/src/state/token_owner_record.rs b/governance/program/src/state/token_owner_record.rs index ff90bb66..750da3ad 100644 --- a/governance/program/src/state/token_owner_record.rs +++ b/governance/program/src/state/token_owner_record.rs @@ -1,5 +1,6 @@ //! Token Owner Record Account +use borsh::maybestd::io::Write; use std::slice::Iter; use crate::{ @@ -8,8 +9,8 @@ use crate::{ }, error::GovernanceError, state::{ - enums::GovernanceAccountType, governance::GovernanceConfig, realm::Realm, - realm_config::get_realm_config_data_for_realm, + enums::GovernanceAccountType, governance::GovernanceConfig, legacy::TokenOwnerRecordV1, + realm::RealmV2, realm_config::get_realm_config_data_for_realm, }, PROGRAM_AUTHORITY_SEED, }; @@ -17,6 +18,7 @@ use crate::{ use borsh::{BorshDeserialize, BorshSchema, BorshSerialize}; use solana_program::{ account_info::{next_account_info, AccountInfo}, + borsh::try_from_slice_unchecked, program_error::ProgramError, program_pack::IsInitialized, pubkey::Pubkey, @@ -28,7 +30,7 @@ use spl_governance_tools::account::{get_account_data, AccountMaxSize}; /// Account PDA seeds: ['governance', realm, token_mint, token_owner ] #[repr(C)] #[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] -pub struct TokenOwnerRecord { +pub struct TokenOwnerRecordV2 { /// Governance account type pub account_type: GovernanceAccountType, @@ -66,21 +68,25 @@ pub struct TokenOwnerRecord { /// A single account that is allowed to operate governance with the deposited governing tokens /// It can be delegated to by the governing_token_owner or current governance_delegate pub governance_delegate: Option, + + /// Reserved space for versions v2 and onwards + /// Note: This space won't be available to v1 accounts until runtime supports resizing + pub reserved_v2: [u8; 128], } -impl AccountMaxSize for TokenOwnerRecord { +impl AccountMaxSize for TokenOwnerRecordV2 { fn get_max_size(&self) -> Option { - Some(154) + Some(282) } } -impl IsInitialized for TokenOwnerRecord { +impl IsInitialized for TokenOwnerRecordV2 { fn is_initialized(&self) -> bool { - self.account_type == GovernanceAccountType::TokenOwnerRecord + self.account_type == GovernanceAccountType::TokenOwnerRecordV2 } } -impl TokenOwnerRecord { +impl TokenOwnerRecordV2 { /// Checks whether the provided Governance Authority signed transaction pub fn assert_token_owner_or_delegate_is_signer( &self, @@ -104,7 +110,7 @@ impl TokenOwnerRecord { /// Asserts TokenOwner has enough tokens to be allowed to create proposal and doesn't have any outstanding proposals pub fn assert_can_create_proposal( &self, - realm_data: &Realm, + realm_data: &RealmV2, config: &GovernanceConfig, voter_weight: u64, ) -> Result<(), ProgramError> { @@ -133,7 +139,7 @@ impl TokenOwnerRecord { /// Asserts TokenOwner has enough tokens to be allowed to create governance pub fn assert_can_create_governance( &self, - realm_data: &Realm, + realm_data: &RealmV2, voter_weight: u64, ) -> Result<(), ProgramError> { let min_weight_to_create_governance = @@ -188,7 +194,7 @@ impl TokenOwnerRecord { realm_config_info: &AccountInfo, account_info_iter: &mut Iter, realm: &Pubkey, - realm_data: &Realm, + realm_data: &RealmV2, weight_action: VoterWeightAction, weight_action_target: &Pubkey, ) -> Result { @@ -218,6 +224,37 @@ impl TokenOwnerRecord { Ok(self.governing_token_deposit_amount) } } + + /// Serializes account into the target buffer + pub fn serialize(self, writer: &mut W) -> Result<(), ProgramError> { + if self.account_type == GovernanceAccountType::TokenOwnerRecordV2 { + BorshSerialize::serialize(&self, writer)? + } else if self.account_type == GovernanceAccountType::TokenOwnerRecordV1 { + // 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 asses for v1 backward compatibility impact + if self.reserved_v2 != [0; 128] { + panic!("Extended data not supported by TokenOwnerRecordV1") + } + + let token_owner_record_data_v1 = TokenOwnerRecordV1 { + account_type: self.account_type, + realm: self.realm, + governing_token_mint: self.governing_token_mint, + governing_token_owner: self.governing_token_owner, + governing_token_deposit_amount: self.governing_token_deposit_amount, + unrelinquished_votes_count: self.unrelinquished_votes_count, + total_votes_count: self.total_votes_count, + outstanding_proposal_count: self.outstanding_proposal_count, + reserved: self.reserved, + governance_delegate: self.governance_delegate, + }; + + BorshSerialize::serialize(&token_owner_record_data_v1, writer)?; + } + + Ok(()) + } } /// Returns TokenOwnerRecord PDA address @@ -252,8 +289,35 @@ pub fn get_token_owner_record_address_seeds<'a>( pub fn get_token_owner_record_data( program_id: &Pubkey, token_owner_record_info: &AccountInfo, -) -> Result { - get_account_data::(program_id, token_owner_record_info) +) -> Result { + let account_type: GovernanceAccountType = + try_from_slice_unchecked(&token_owner_record_info.data.borrow())?; + + // If the account is V1 version then translate to V2 + if account_type == GovernanceAccountType::TokenOwnerRecordV1 { + let token_owner_record_data_v1 = + get_account_data::(program_id, token_owner_record_info)?; + + return Ok(TokenOwnerRecordV2 { + account_type, + + realm: token_owner_record_data_v1.realm, + governing_token_mint: token_owner_record_data_v1.governing_token_mint, + governing_token_owner: token_owner_record_data_v1.governing_token_owner, + governing_token_deposit_amount: token_owner_record_data_v1 + .governing_token_deposit_amount, + unrelinquished_votes_count: token_owner_record_data_v1.unrelinquished_votes_count, + total_votes_count: token_owner_record_data_v1.total_votes_count, + outstanding_proposal_count: token_owner_record_data_v1.outstanding_proposal_count, + reserved: token_owner_record_data_v1.reserved, + governance_delegate: token_owner_record_data_v1.governance_delegate, + + // Add the extra reserved_v2 padding + reserved_v2: [0; 128], + }); + } + + get_account_data::(program_id, token_owner_record_info) } /// Deserializes TokenOwnerRecord account and checks its PDA against the provided seeds @@ -261,7 +325,7 @@ pub fn get_token_owner_record_data_for_seeds( program_id: &Pubkey, token_owner_record_info: &AccountInfo, token_owner_record_seeds: &[&[u8]], -) -> Result { +) -> Result { let (token_owner_record_address, _) = Pubkey::find_program_address(token_owner_record_seeds, program_id); @@ -277,7 +341,7 @@ pub fn get_token_owner_record_data_for_realm( program_id: &Pubkey, token_owner_record_info: &AccountInfo, realm: &Pubkey, -) -> Result { +) -> Result { let token_owner_record_data = get_token_owner_record_data(program_id, token_owner_record_info)?; if token_owner_record_data.realm != *realm { @@ -293,7 +357,7 @@ pub fn get_token_owner_record_data_for_realm_and_governing_mint( token_owner_record_info: &AccountInfo, realm: &Pubkey, governing_token_mint: &Pubkey, -) -> Result { +) -> Result { let token_owner_record_data = get_token_owner_record_data_for_realm(program_id, token_owner_record_info, realm)?; @@ -309,7 +373,7 @@ pub fn get_token_owner_record_data_for_proposal_owner( program_id: &Pubkey, token_owner_record_info: &AccountInfo, proposal_owner: &Pubkey, -) -> Result { +) -> Result { if token_owner_record_info.key != proposal_owner { return Err(GovernanceError::InvalidProposalOwnerAccount.into()); } @@ -319,14 +383,14 @@ pub fn get_token_owner_record_data_for_proposal_owner( #[cfg(test)] mod test { - use solana_program::borsh::{get_packed_len, try_from_slice_unchecked}; + use solana_program::borsh::get_packed_len; use super::*; #[test] fn test_max_size() { - let token_owner_record = TokenOwnerRecord { - account_type: GovernanceAccountType::TokenOwnerRecord, + let token_owner_record = TokenOwnerRecordV2 { + account_type: GovernanceAccountType::TokenOwnerRecordV2, realm: Pubkey::new_unique(), governing_token_mint: Pubkey::new_unique(), governing_token_owner: Pubkey::new_unique(), @@ -336,66 +400,11 @@ mod test { total_votes_count: 1, outstanding_proposal_count: 1, reserved: [0; 7], + reserved_v2: [0; 128], }; - let size = get_packed_len::(); + let size = get_packed_len::(); assert_eq!(token_owner_record.get_max_size(), Some(size)); } - - #[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)] - pub struct TokenOwnerRecordV1 { - pub account_type: GovernanceAccountType, - - pub realm: Pubkey, - - pub governing_token_mint: Pubkey, - - pub governing_token_owner: Pubkey, - - pub governing_token_deposit_amount: u64, - - pub unrelinquished_votes_count: u32, - - pub total_votes_count: u32, - - pub reserved: [u8; 8], - - pub governance_delegate: Option, - } - - #[test] - fn test_deserialize_v1_0_account() { - let token_owner_record_v1_0 = TokenOwnerRecordV1 { - account_type: GovernanceAccountType::TokenOwnerRecord, - realm: Pubkey::new_unique(), - governing_token_mint: Pubkey::new_unique(), - governing_token_owner: Pubkey::new_unique(), - governing_token_deposit_amount: 10, - unrelinquished_votes_count: 2, - total_votes_count: 5, - reserved: [0; 8], - governance_delegate: Some(Pubkey::new_unique()), - }; - - let mut token_owner_record_v1_0_data = vec![]; - token_owner_record_v1_0 - .serialize(&mut token_owner_record_v1_0_data) - .unwrap(); - - let token_owner_record_v1_1_data: TokenOwnerRecord = - try_from_slice_unchecked(&token_owner_record_v1_0_data).unwrap(); - - assert_eq!( - token_owner_record_v1_0.account_type, - token_owner_record_v1_1_data.account_type - ); - - assert_eq!(0, token_owner_record_v1_1_data.outstanding_proposal_count); - - assert_eq!( - token_owner_record_v1_0.governance_delegate, - token_owner_record_v1_1_data.governance_delegate - ); - } } diff --git a/governance/program/src/state/vote_record.rs b/governance/program/src/state/vote_record.rs index f35912d9..dfae8e5a 100644 --- a/governance/program/src/state/vote_record.rs +++ b/governance/program/src/state/vote_record.rs @@ -82,6 +82,10 @@ pub struct VoteRecordV2 { /// Voter's vote pub vote: Vote, + + /// Reserved space for versions v2 and onwards + /// Note: This space won't be available to v1 accounts until runtime supports resizing + pub reserved_v2: [u8; 8], } impl AccountMaxSize for VoteRecordV2 {} @@ -107,6 +111,12 @@ impl VoteRecordV2 { BorshSerialize::serialize(&self, writer)? } else if self.account_type == GovernanceAccountType::VoteRecordV1 { // 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 asses for v1 backward compatibility impact + if self.reserved_v2 != [0; 8] { + panic!("Extended data not supported by VoteRecordV1") + } + let vote_weight = match &self.vote { Vote::Approve(_options) => VoteWeightV1::Yes(self.voter_weight), Vote::Deny => VoteWeightV1::No(self.voter_weight), @@ -160,6 +170,7 @@ pub fn get_vote_record_data( is_relinquished: vote_record_data_v1.is_relinquished, voter_weight, vote, + reserved_v2: [0; 8], }); } diff --git a/governance/program/tests/program_test/cookies.rs b/governance/program/tests/program_test/cookies.rs index c4180152..5f3436fa 100644 --- a/governance/program/tests/program_test/cookies.rs +++ b/governance/program/tests/program_test/cookies.rs @@ -1,10 +1,10 @@ use solana_program::{instruction::Instruction, pubkey::Pubkey}; use solana_sdk::signature::Keypair; use spl_governance::state::{ - governance::Governance, native_treasury::NativeTreasury, program_metadata::ProgramMetadata, - proposal::ProposalV2, proposal_transaction::ProposalTransactionV2, realm::Realm, - realm_config::RealmConfigAccount, signatory_record::SignatoryRecord, - token_owner_record::TokenOwnerRecord, vote_record::VoteRecordV2, + governance::GovernanceV2, native_treasury::NativeTreasury, program_metadata::ProgramMetadata, + proposal::ProposalV2, proposal_transaction::ProposalTransactionV2, realm::RealmV2, + realm_config::RealmConfigAccount, signatory_record::SignatoryRecordV2, + token_owner_record::TokenOwnerRecordV2, vote_record::VoteRecordV2, }; use spl_governance_addin_api::{ @@ -20,7 +20,7 @@ pub trait AccountCookie { pub struct RealmCookie { pub address: Pubkey, - pub account: Realm, + pub account: RealmV2, pub community_mint_authority: Keypair, @@ -45,7 +45,7 @@ pub struct RealmConfigCookie { pub struct TokenOwnerRecordCookie { pub address: Pubkey, - pub account: TokenOwnerRecord, + pub account: TokenOwnerRecordV2, pub token_source: Pubkey, @@ -131,7 +131,7 @@ impl AccountCookie for GovernedAccountCookie { #[derive(Debug)] pub struct GovernanceCookie { pub address: Pubkey, - pub account: Governance, + pub account: GovernanceV2, pub next_proposal_index: u32, } @@ -147,7 +147,7 @@ pub struct ProposalCookie { #[derive(Debug)] pub struct SignatoryRecordCookie { pub address: Pubkey, - pub account: SignatoryRecord, + pub account: SignatoryRecordV2, pub signatory: Keypair, } diff --git a/governance/program/tests/program_test/mod.rs b/governance/program/tests/program_test/mod.rs index 6eb5e262..7ccc5a55 100644 --- a/governance/program/tests/program_test/mod.rs +++ b/governance/program/tests/program_test/mod.rs @@ -32,7 +32,7 @@ use spl_governance::{ }, governance::{ get_governance_address, get_mint_governance_address, get_program_governance_address, - get_token_governance_address, Governance, GovernanceConfig, + get_token_governance_address, GovernanceConfig, GovernanceV2, }, native_treasury::{get_native_treasury_address, NativeTreasury}, program_metadata::{get_program_metadata_address, ProgramMetadata}, @@ -41,12 +41,12 @@ use spl_governance::{ get_proposal_transaction_address, InstructionData, ProposalTransactionV2, }, realm::{ - get_governing_token_holding_address, get_realm_address, Realm, RealmConfig, - RealmConfigArgs, SetRealmAuthorityAction, + get_governing_token_holding_address, get_realm_address, RealmConfig, RealmConfigArgs, + RealmV2, SetRealmAuthorityAction, }, realm_config::{get_realm_config_address, RealmConfigAccount}, - signatory_record::{get_signatory_record_address, SignatoryRecord}, - token_owner_record::{get_token_owner_record_address, TokenOwnerRecord}, + signatory_record::{get_signatory_record_address, SignatoryRecordV2}, + token_owner_record::{get_token_owner_record_address, TokenOwnerRecordV2}, vote_record::{get_vote_record_address, Vote, VoteChoice, VoteRecordV2}, }, tools::bpf_loader_upgradeable::get_program_data_address, @@ -298,8 +298,8 @@ impl GovernanceProgramTest { .await .unwrap(); - let account = Realm { - account_type: GovernanceAccountType::Realm, + let account = RealmV2 { + account_type: GovernanceAccountType::RealmV2, community_mint: community_token_mint_keypair.pubkey(), name, @@ -320,6 +320,7 @@ impl GovernanceProgramTest { use_max_community_voter_weight_addin: false, }, voting_proposal_count: 0, + reserved_v2: [0; 128], }; let realm_config_cookie = if set_realm_config_args.community_voter_weight_addin.is_some() @@ -390,8 +391,8 @@ impl GovernanceProgramTest { .await .unwrap(); - let account = Realm { - account_type: GovernanceAccountType::Realm, + let account = RealmV2 { + account_type: GovernanceAccountType::RealmV2, community_mint: realm_cookie.account.community_mint, name, @@ -408,6 +409,7 @@ impl GovernanceProgramTest { use_max_community_voter_weight_addin: false, }, voting_proposal_count: 0, + reserved_v2: [0; 128], }; let community_token_holding_address = get_governing_token_holding_address( @@ -470,8 +472,8 @@ impl GovernanceProgramTest { .await .unwrap(); - let account = TokenOwnerRecord { - account_type: GovernanceAccountType::TokenOwnerRecord, + let account = TokenOwnerRecordV2 { + account_type: GovernanceAccountType::TokenOwnerRecordV2, realm: realm_cookie.address, governing_token_mint: realm_cookie.account.community_mint, governing_token_owner: token_owner.pubkey(), @@ -481,6 +483,7 @@ impl GovernanceProgramTest { total_votes_count: 0, outstanding_proposal_count: 0, reserved: [0; 7], + reserved_v2: [0; 128], }; let token_owner_record_address = get_token_owner_record_address( @@ -686,8 +689,8 @@ impl GovernanceProgramTest { &token_owner.pubkey(), ); - let account = TokenOwnerRecord { - account_type: GovernanceAccountType::TokenOwnerRecord, + let account = TokenOwnerRecordV2 { + account_type: GovernanceAccountType::TokenOwnerRecordV2, realm: *realm_address, governing_token_mint: *governing_mint, governing_token_owner: token_owner.pubkey(), @@ -697,6 +700,7 @@ impl GovernanceProgramTest { total_votes_count: 0, outstanding_proposal_count: 0, reserved: [0; 7], + reserved_v2: [0; 128], }; let governance_delegate = Keypair::from_base58_string(&token_owner.to_base58_string()); @@ -1236,14 +1240,15 @@ impl GovernanceProgramTest { governance_config.clone(), ); - let account = Governance { - account_type: GovernanceAccountType::Governance, + let account = GovernanceV2 { + account_type: GovernanceAccountType::GovernanceV2, realm: realm_cookie.address, governed_account: governed_account_cookie.address, config: governance_config.clone(), proposals_count: 0, reserved: [0; 6], voting_proposal_count: 0, + reserved_v2: [0; 128], }; let default_signers = &[create_authority]; @@ -1404,14 +1409,15 @@ impl GovernanceProgramTest { .process_transaction(&[create_program_governance_ix], Some(signers)) .await?; - let account = Governance { - account_type: GovernanceAccountType::ProgramGovernance, + let account = GovernanceV2 { + account_type: GovernanceAccountType::ProgramGovernanceV2, realm: realm_cookie.address, governed_account: governed_program_cookie.address, config, proposals_count: 0, reserved: [0; 6], voting_proposal_count: 0, + reserved_v2: [0; 128], }; let program_governance_address = get_program_governance_address( @@ -1527,14 +1533,15 @@ impl GovernanceProgramTest { .process_transaction(&[create_mint_governance_ix], Some(signers)) .await?; - let account = Governance { - account_type: GovernanceAccountType::MintGovernance, + let account = GovernanceV2 { + account_type: GovernanceAccountType::MintGovernanceV2, realm: realm_cookie.address, governed_account: governed_mint_cookie.address, config: governance_config.clone(), proposals_count: 0, reserved: [0; 6], voting_proposal_count: 0, + reserved_v2: [0; 128], }; let mint_governance_address = get_mint_governance_address( @@ -1610,14 +1617,15 @@ impl GovernanceProgramTest { .process_transaction(&[create_token_governance_ix], Some(signers)) .await?; - let account = Governance { - account_type: GovernanceAccountType::TokenGovernance, + let account = GovernanceV2 { + account_type: GovernanceAccountType::TokenGovernanceV2, realm: realm_cookie.address, governed_account: governed_token_cookie.address, config, proposals_count: 0, reserved: [0; 6], voting_proposal_count: 0, + reserved_v2: [0; 128], }; let token_governance_address = get_token_governance_address( @@ -1857,11 +1865,12 @@ impl GovernanceProgramTest { &signatory.pubkey(), ); - let signatory_record_data = SignatoryRecord { - account_type: GovernanceAccountType::SignatoryRecord, + let signatory_record_data = SignatoryRecordV2 { + account_type: GovernanceAccountType::SignatoryRecordV2, proposal: proposal_cookie.address, signatory: signatory.pubkey(), signed_off: false, + reserved_v2: [0; 8], }; let signatory_record_cookie = SignatoryRecordCookie { @@ -2154,6 +2163,7 @@ impl GovernanceProgramTest { vote, voter_weight: vote_amount, is_relinquished: false, + reserved_v2: [0; 8], }; let vote_record_cookie = VoteRecordCookie { @@ -2441,6 +2451,7 @@ impl GovernanceProgramTest { executed_at: None, execution_status: TransactionExecutionStatus::None, proposal: proposal_cookie.address, + reserved_v2: [0; 8], }; instruction.accounts = instruction @@ -2531,9 +2542,9 @@ impl GovernanceProgramTest { } #[allow(dead_code)] - pub async fn get_token_owner_record_account(&mut self, address: &Pubkey) -> TokenOwnerRecord { + pub async fn get_token_owner_record_account(&mut self, address: &Pubkey) -> TokenOwnerRecordV2 { self.bench - .get_borsh_account::(address) + .get_borsh_account::(address) .await } @@ -2552,8 +2563,8 @@ impl GovernanceProgramTest { } #[allow(dead_code)] - pub async fn get_realm_account(&mut self, realm_address: &Pubkey) -> Realm { - self.bench.get_borsh_account::(realm_address).await + pub async fn get_realm_account(&mut self, realm_address: &Pubkey) -> RealmV2 { + self.bench.get_borsh_account::(realm_address).await } #[allow(dead_code)] @@ -2567,9 +2578,9 @@ impl GovernanceProgramTest { } #[allow(dead_code)] - pub async fn get_governance_account(&mut self, governance_address: &Pubkey) -> Governance { + pub async fn get_governance_account(&mut self, governance_address: &Pubkey) -> GovernanceV2 { self.bench - .get_borsh_account::(governance_address) + .get_borsh_account::(governance_address) .await } @@ -2601,9 +2612,9 @@ impl GovernanceProgramTest { pub async fn get_signatory_record_account( &mut self, proposal_address: &Pubkey, - ) -> SignatoryRecord { + ) -> SignatoryRecordV2 { self.bench - .get_borsh_account::(proposal_address) + .get_borsh_account::(proposal_address) .await } diff --git a/governance/tools/src/account.rs b/governance/tools/src/account.rs index b4ec2edf..b4dae06e 100644 --- a/governance/tools/src/account.rs +++ b/governance/tools/src/account.rs @@ -224,16 +224,16 @@ pub fn get_account_data( /// Asserts the given account is not empty, owned by the given program and of the expected type /// Note: The function assumes the account type T is stored as the first element in the account data -pub fn assert_is_valid_account( +pub fn assert_is_valid_account_of_type( account_info: &AccountInfo, expected_account_type: T, owner_program_id: &Pubkey, ) -> Result<(), ProgramError> { - assert_is_valid_account2(account_info, &[expected_account_type], owner_program_id) + assert_is_valid_account_of_types(account_info, &[expected_account_type], owner_program_id) } /// Asserts the given account is not empty, owned by the given program and one of the expected types /// Note: The function assumes the account type T is stored as the first element in the account data -pub fn assert_is_valid_account2( +pub fn assert_is_valid_account_of_types( account_info: &AccountInfo, expected_account_types: &[T], owner_program_id: &Pubkey,