Skip to content

Commit

Permalink
Merge branch 'solana-labs:master' into master
Browse files Browse the repository at this point in the history
  • Loading branch information
nope-finance authored Jul 1, 2021
2 parents de1210f + 8f2c8fa commit c30897f
Show file tree
Hide file tree
Showing 41 changed files with 1,629 additions and 893 deletions.
23 changes: 22 additions & 1 deletion governance/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ a voting population to vote on disbursement of access or funds collectively.

## Architecture

### Accounts diagram
### Accounts diagram (Program Governance use case)

![Accounts diagram](./resources/governance-accounts.jpg)

Expand All @@ -31,6 +31,22 @@ The governance program validates at creation time of the Governance account that
taken under governance signed the transaction. Optionally `CreateProgramGovernance` instruction can also transfer `upgrade_authority`
of the governed program to the Governance PDA at the creation time of the Governance account.

### Mint Governance account

A mint governance account allows a mint authority to setup governance over an SPL Mint account.
The Governance program validates at creation time that the current mint authority signed the transaction to
create the governance and optionally can transfer the authority to the Governance account.
Once setup the Mint Governance allows governance participants to create Proposals which execute mint instructions for
the governed Mint.

### Token Governance account

A token governance account allows a token account owner to setup governance over an SPL Token account.
The Governance program validates at creation time the current owner signed the transaction to
create the governance and optionally can transfer the owner to the Governance account.
Once setup the Token Governance allows participants to create Proposals to execute transfer instructions
from the governed token account.

### How does the authority work?

Governance can handle arbitrary executions of code, but it's real power lies in the power to upgrade programs.
Expand All @@ -42,6 +58,11 @@ the new program data and overwriting of the existing Program account's data with
by the Solana program deploy cli command.
However, in order for Governance to be useful, Governance now needs this authority.

In similar fashion for Mint and Token governances the relevant authorities to mint and transfer tokens
are transferred to the Governance account. It in turn allows participants to create and vote on Proposals
which can then execute
mint and transfer instructions for the governed accounts.

### Proposal accounts

A Proposal is an instance of a Governance created to vote on and execute given set of changes.
Expand Down
8 changes: 8 additions & 0 deletions governance/program/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,14 @@ pub enum GovernanceError {
/// Given program is not upgradable
#[error("Given program is not upgradable")]
ProgramNotUpgradable,

/// Invalid token owner
#[error("Invalid token owner")]
InvalidTokenOwner,

/// Current token owner must sign transaction
#[error("Current token owner must sign transaction")]
TokenOwnerMustSign,
}

impl PrintProgramError for GovernanceError {
Expand Down
68 changes: 63 additions & 5 deletions governance/program/src/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::{
state::{
governance::{
get_account_governance_address, get_mint_governance_address,
get_program_governance_address, GovernanceConfig,
get_program_governance_address, get_token_governance_address, GovernanceConfig,
},
proposal::get_proposal_address,
proposal_instruction::{get_proposal_instruction_address, InstructionData},
Expand Down Expand Up @@ -133,7 +133,7 @@ pub enum GovernanceInstruction {
transfer_upgrade_authority: bool,
},

/// Creates Proposal account for Instructions that will be executed at various slots in the future
/// Creates Proposal account for Instructions that will be executed at some point in the future
///
/// 0. `[writable]` Proposal account. PDA seeds ['governance',governance, governing_token_mint, proposal_index]
/// 1. `[writable]` Governance account
Expand Down Expand Up @@ -202,8 +202,8 @@ pub enum GovernanceInstruction {
/// Instruction index to be inserted at.
index: u16,
#[allow(dead_code)]
/// Slot waiting time between vote period ending and this being eligible for execution
hold_up_time: u64,
/// Waiting time (in seconds) between vote period ending and this being eligible for execution
hold_up_time: u32,

#[allow(dead_code)]
/// Instruction Data
Expand Down Expand Up @@ -313,6 +313,28 @@ pub enum GovernanceInstruction {
/// However the instruction would validate the current mint authority signed the transaction nonetheless
transfer_mint_authority: bool,
},

/// Creates Token Governance account which governs a token account
///
/// 0. `[]` Realm account the created Governance belongs to
/// 1. `[writable]` Token Governance account. PDA seeds: ['token-governance', realm, governed_token]
/// 2. `[writable]` Token account governed by this Governance account
/// 3. `[signer]` Current Token account owner
/// 4. `[signer]` Payer
/// 5. `[]` SPL Token program
/// 6. `[]` System program
/// 7. `[]` Sysvar Rent
CreateTokenGovernance {
#[allow(dead_code)]
/// Governance config
config: GovernanceConfig,

#[allow(dead_code)]
/// Indicates whether token owner should be transferred to the Governance PDA
/// If it's set to false then it can be done at a later time
/// However the instruction would validate the current token owner signed the transaction nonetheless
transfer_token_owner: bool,
},
}

/// Creates CreateRealm instruction
Expand Down Expand Up @@ -573,6 +595,42 @@ pub fn create_mint_governance(
}
}

/// Creates CreateTokenGovernance instruction
pub fn create_token_governance(
program_id: &Pubkey,
// Accounts
governed_token_owner: &Pubkey,
payer: &Pubkey,
// Args
config: GovernanceConfig,
transfer_token_owner: bool,
) -> Instruction {
let token_governance_address =
get_token_governance_address(program_id, &config.realm, &config.governed_account);

let accounts = vec![
AccountMeta::new_readonly(config.realm, false),
AccountMeta::new(token_governance_address, false),
AccountMeta::new(config.governed_account, false),
AccountMeta::new_readonly(*governed_token_owner, true),
AccountMeta::new_readonly(*payer, true),
AccountMeta::new_readonly(spl_token::id(), false),
AccountMeta::new_readonly(system_program::id(), false),
AccountMeta::new_readonly(sysvar::rent::id(), false),
];

let instruction = GovernanceInstruction::CreateTokenGovernance {
config,
transfer_token_owner,
};

Instruction {
program_id: *program_id,
accounts,
data: instruction.try_to_vec().unwrap(),
}
}

/// Creates CreateProposal instruction
#[allow(clippy::too_many_arguments)]
pub fn create_proposal(
Expand Down Expand Up @@ -849,7 +907,7 @@ pub fn insert_instruction(
payer: &Pubkey,
// Args
index: u16,
hold_up_time: u64,
hold_up_time: u32,
instruction: InstructionData,
) -> Instruction {
let proposal_instruction_address =
Expand Down
7 changes: 7 additions & 0 deletions governance/program/src/processor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ mod process_create_mint_governance;
mod process_create_program_governance;
mod process_create_proposal;
mod process_create_realm;
mod process_create_token_governance;
mod process_deposit_governing_tokens;
mod process_execute_instruction;
mod process_finalize_vote;
Expand All @@ -30,6 +31,7 @@ use process_create_mint_governance::*;
use process_create_program_governance::*;
use process_create_proposal::*;
use process_create_realm::*;
use process_create_token_governance::*;
use process_deposit_governing_tokens::*;
use process_execute_instruction::*;
use process_finalize_vote::*;
Expand Down Expand Up @@ -103,6 +105,11 @@ pub fn process_instruction(
transfer_mint_authority,
} => process_create_mint_governance(program_id, accounts, config, transfer_mint_authority),

GovernanceInstruction::CreateTokenGovernance {
config,
transfer_token_owner,
} => process_create_token_governance(program_id, accounts, config, transfer_token_owner),

GovernanceInstruction::CreateAccountGovernance { config } => {
process_create_account_governance(program_id, accounts, config)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ pub fn process_cancel_proposal(program_id: &Pubkey, accounts: &[AccountInfo]) ->
token_owner_record_data.assert_token_owner_or_delegate_is_signer(governance_authority_info)?;

proposal_data.state = ProposalState::Cancelled;
proposal_data.closed_at = Some(clock.slot);
proposal_data.closed_at = Some(clock.unix_timestamp);

proposal_data.serialize(&mut *proposal_info.data.borrow_mut())?;

Expand Down
8 changes: 6 additions & 2 deletions governance/program/src/processor/process_cast_vote.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ pub fn process_cast_vote(
governance_info.key,
governing_token_mint_info.key,
)?;
proposal_data.assert_can_cast_vote(&governance_data.config, clock.slot)?;
proposal_data.assert_can_cast_vote(&governance_data.config, clock.unix_timestamp)?;

let mut token_owner_record_data = get_token_owner_record_data_for_realm_and_governing_mint(
program_id,
Expand Down Expand Up @@ -105,7 +105,11 @@ pub fn process_cast_vote(
};

let governing_token_supply = get_spl_token_mint_supply(governing_token_mint_info)?;
proposal_data.try_tip_vote(governing_token_supply, &governance_data.config, clock.slot);
proposal_data.try_tip_vote(
governing_token_supply,
&governance_data.config,
clock.unix_timestamp,
);

proposal_data.serialize(&mut *proposal_info.data.borrow_mut())?;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ pub fn process_create_proposal(
name,
description_link,

draft_at: clock.slot,
draft_at: clock.unix_timestamp,
signing_off_at: None,
voting_at: None,
voting_completed_at: None,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
//! Program state processor
use crate::{
state::{
enums::GovernanceAccountType,
governance::{
assert_is_valid_governance_config, get_token_governance_address_seeds, Governance,
GovernanceConfig,
},
},
tools::{
account::create_and_serialize_account_signed,
spl_token::{assert_spl_token_owner_is_signer, set_spl_token_owner},
},
};
use solana_program::{
account_info::{next_account_info, AccountInfo},
entrypoint::ProgramResult,
pubkey::Pubkey,
rent::Rent,
sysvar::Sysvar,
};

/// Processes CreateTokenGovernance instruction
pub fn process_create_token_governance(
program_id: &Pubkey,
accounts: &[AccountInfo],
config: GovernanceConfig,
transfer_token_owner: bool,
) -> ProgramResult {
let account_info_iter = &mut accounts.iter();

let realm_info = next_account_info(account_info_iter)?; // 0
let token_governance_info = next_account_info(account_info_iter)?; // 1

let governed_token_info = next_account_info(account_info_iter)?; // 2
let governed_token_owner_info = next_account_info(account_info_iter)?; // 3

let payer_info = next_account_info(account_info_iter)?; // 4
let spl_token_info = next_account_info(account_info_iter)?; // 5

let system_info = next_account_info(account_info_iter)?; // 6

let rent_sysvar_info = next_account_info(account_info_iter)?; // 7
let rent = &Rent::from_account_info(rent_sysvar_info)?;

assert_is_valid_governance_config(program_id, &config, realm_info)?;

let token_governance_data = Governance {
account_type: GovernanceAccountType::TokenGovernance,
config: config.clone(),
proposals_count: 0,
};

create_and_serialize_account_signed::<Governance>(
payer_info,
token_governance_info,
&token_governance_data,
&get_token_governance_address_seeds(&config.realm, &config.governed_account),
program_id,
system_info,
rent,
)?;

if transfer_token_owner {
set_spl_token_owner(
governed_token_info,
governed_token_owner_info,
token_governance_info.key,
spl_token_info,
)?;
} else {
assert_spl_token_owner_is_signer(governed_token_info, governed_token_owner_info)?;
}

Ok(())
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@ pub fn process_execute_instruction(program_id: &Pubkey, accounts: &[AccountInfo]
proposal_info.key,
)?;

proposal_data.assert_can_execute_instruction(&proposal_instruction_data, clock.slot)?;
proposal_data
.assert_can_execute_instruction(&proposal_instruction_data, clock.unix_timestamp)?;

// Execute instruction with Governance PDA as signer
let instruction = Instruction::from(&proposal_instruction_data.instruction);
Expand All @@ -59,7 +60,7 @@ pub fn process_execute_instruction(program_id: &Pubkey, accounts: &[AccountInfo]

// Update proposal and instruction accounts
if proposal_data.state == ProposalState::Succeeded {
proposal_data.executing_at = Some(clock.slot);
proposal_data.executing_at = Some(clock.unix_timestamp);
proposal_data.state = ProposalState::Executing;
}

Expand All @@ -71,13 +72,13 @@ pub fn process_execute_instruction(program_id: &Pubkey, accounts: &[AccountInfo]
if proposal_data.state == ProposalState::Executing
&& proposal_data.instructions_executed_count == proposal_data.instructions_count
{
proposal_data.closed_at = Some(clock.slot);
proposal_data.closed_at = Some(clock.unix_timestamp);
proposal_data.state = ProposalState::Completed;
}

proposal_data.serialize(&mut *proposal_info.data.borrow_mut())?;

proposal_instruction_data.executed_at = Some(clock.slot);
proposal_instruction_data.executed_at = Some(clock.unix_timestamp);
proposal_instruction_data.serialize(&mut *proposal_instruction_info.data.borrow_mut())?;

Ok(())
Expand Down
6 changes: 5 additions & 1 deletion governance/program/src/processor/process_finalize_vote.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,11 @@ pub fn process_finalize_vote(program_id: &Pubkey, accounts: &[AccountInfo]) -> P

let governing_token_supply = get_spl_token_mint_supply(governing_token_mint_info)?;

proposal_data.finalize_vote(governing_token_supply, &governance_data.config, clock.slot)?;
proposal_data.finalize_vote(
governing_token_supply,
&governance_data.config,
clock.unix_timestamp,
)?;

proposal_data.serialize(&mut *proposal_info.data.borrow_mut())?;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ pub fn process_insert_instruction(
program_id: &Pubkey,
accounts: &[AccountInfo],
index: u16,
hold_up_time: u64,
hold_up_time: u32,
instruction: InstructionData,
) -> ProgramResult {
let account_info_iter = &mut accounts.iter();
Expand Down Expand Up @@ -73,7 +73,7 @@ pub fn process_insert_instruction(
match index.cmp(&proposal_data.instructions_next_index) {
Ordering::Greater => return Err(GovernanceError::InvalidInstructionIndex.into()),
// If the index is the same as instructions_next_index then we are adding a new instruction
// If the index is below instructions_next_index then we are inserting into an existing empty slot
// If the index is below instructions_next_index then we are inserting into an existing empty space
Ordering::Equal => {
proposal_data.instructions_next_index = proposal_data
.instructions_next_index
Expand Down
4 changes: 2 additions & 2 deletions governance/program/src/processor/process_sign_off_proposal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ pub fn process_sign_off_proposal(program_id: &Pubkey, accounts: &[AccountInfo])
signatory_record_data.serialize(&mut *signatory_record_info.data.borrow_mut())?;

if proposal_data.signatories_signed_off_count == 0 {
proposal_data.signing_off_at = Some(clock.slot);
proposal_data.signing_off_at = Some(clock.unix_timestamp);
proposal_data.state = ProposalState::SigningOff;
}

Expand All @@ -52,7 +52,7 @@ pub fn process_sign_off_proposal(program_id: &Pubkey, accounts: &[AccountInfo])

// If all Signatories signed off we can start voting
if proposal_data.signatories_signed_off_count == proposal_data.signatories_count {
proposal_data.voting_at = Some(clock.slot);
proposal_data.voting_at = Some(clock.unix_timestamp);
proposal_data.state = ProposalState::Voting;
}

Expand Down
Loading

0 comments on commit c30897f

Please sign in to comment.