Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Governance: TokenOwnerRecord locks #6215

Merged
merged 51 commits into from
Mar 4, 2024
Merged
Show file tree
Hide file tree
Changes from 40 commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
659eb97
feat: Add TokenOwnerRecordLocks to TokenOwnerRecord
SebastianBor Feb 1, 2024
de5f781
chore: Make fmt happy
SebastianBor Feb 1, 2024
5956174
wip: Stub SetTokenOwnerRecordLock and RemoveTokenOwnerRecordLock
SebastianBor Feb 1, 2024
5cace15
wip: Store TOR locks
SebastianBor Feb 2, 2024
da51e32
chore: fmt
SebastianBor Feb 2, 2024
eba85f4
wip: Trim existing locks
SebastianBor Feb 2, 2024
bdb9013
chore: Make Clippy happy
SebastianBor Feb 2, 2024
222fd5c
feat: Reject expired locks
SebastianBor Feb 2, 2024
af98daa
wip: Read RealmConfig and assert TOR belong to the same Realm
SebastianBor Feb 2, 2024
30b06b7
feat: Remove TOR lock
SebastianBor Feb 2, 2024
6157df5
feat: Assert tokens can't be withdrawn when active TOR locks
SebastianBor Feb 2, 2024
38c5fe7
feat: Check configured lock authorities
SebastianBor Feb 2, 2024
36a4f1d
fix: Fix get_max_size to include lock_authorities
SebastianBor Feb 2, 2024
1e57dae
feat: Implement SetRealmConfigItem
SebastianBor Feb 5, 2024
629e587
chore: Make Clippy happy
SebastianBor Feb 5, 2024
c57f915
chore: Update comments
SebastianBor Feb 6, 2024
b5551b4
chore: SetRealmConfigItem tests
SebastianBor Feb 6, 2024
a13df7c
chore: test_set_realm_config_item_without_realm_config
SebastianBor Feb 6, 2024
634daa8
chore: remove unused var
SebastianBor Feb 6, 2024
427b295
chore: test_set_realm_config_item_with_extended_account_size
SebastianBor Feb 6, 2024
5ab1b52
fix: Accept None as non expiring lock
SebastianBor Feb 6, 2024
ecee172
chore: SetTokenOwnerRecord tests
SebastianBor Feb 8, 2024
471efb2
chore: SetTokenOwnerLock tests
SebastianBor Feb 8, 2024
52fa943
chore: test_set_token_owner_record_lock_for_v1_account
SebastianBor Feb 8, 2024
7205578
chore: RemoveTokenOwnerRecordLock tests
SebastianBor Feb 8, 2024
16c74a1
chore: test_withdraw_governing_tokens_with_token_owner_record_lock_error
SebastianBor Feb 8, 2024
16ddc54
chore: Refactor TOR resize_and_serialize
SebastianBor Feb 8, 2024
5324270
chore: Make Clippy happy
SebastianBor Feb 8, 2024
9e3e117
chore: Refactor RealmConfigAccount serialisation with resizing
SebastianBor Feb 8, 2024
c7d523f
chore: Refactor trim_locks
SebastianBor Feb 8, 2024
5406217
chore: test_set_realm_config_without_existing_realm_config
SebastianBor Feb 8, 2024
a846cdd
chore: Make fmt happy
SebastianBor Feb 8, 2024
6632d99
wip: Roundtrip lock authorities on SetRealmConfig
SebastianBor Feb 22, 2024
7b9b410
chore: test_set_realm_config_with_token_owner_record_lock_authorities
SebastianBor Feb 22, 2024
02d0613
chore: Update comment
SebastianBor Feb 22, 2024
b321f75
chore: Rename lock_type to lock_id
SebastianBor Feb 22, 2024
86cfce9
Merge branch 'governance-tor-locks' of https://github.com/solana-labs…
SebastianBor Feb 22, 2024
eb45796
fix: Do not trim non expiring locks set as expiry=None
SebastianBor Feb 22, 2024
f2a2ace
chore: Update comments
SebastianBor Feb 22, 2024
3212a27
chore: Fix test_add_token_owner_record_lock_authority_with_authority_…
SebastianBor Feb 22, 2024
9fb9751
chore: Split trim_locks into remove_lock and remove_expired_locks
SebastianBor Feb 23, 2024
dbd0d55
chore: Rename RemoveTokenOwnerLocks to Relinquish and make ids optional
SebastianBor Feb 23, 2024
a57c56e
chore: Clippy
SebastianBor Feb 23, 2024
d591136
fix: Remove lock only if type specified
SebastianBor Feb 23, 2024
3c8df73
chore: comments and cleanup
SebastianBor Feb 23, 2024
cb18531
feat: Throw error when removing TOR lock authority and it doesn't exist
SebastianBor Feb 23, 2024
4edeb7c
feat: Support relinquishing multiple locks and for revoked authority …
SebastianBor Feb 23, 2024
64889f9
chore: Clippy
SebastianBor Feb 23, 2024
3c6fa1a
Merge branch 'master' into governance-tor-locks
SebastianBor Mar 4, 2024
6d06deb
chore: Fix merge issues
SebastianBor Mar 4, 2024
d80af1b
chore: Fix build warnings
SebastianBor Mar 4, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 24 additions & 4 deletions governance/program/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -507,19 +507,39 @@ pub enum GovernanceError {

/// Invalid Governance for RequiredSignatory
#[error("Invalid Governance for RequiredSignatory")]
InvalidGovernanceForRequiredSignatory,
InvalidGovernanceForRequiredSignatory, // 621

/// SignatoryRecord already exists
#[error("Signatory Record has already been created")]
SignatoryRecordAlreadyExists,
SignatoryRecordAlreadyExists, // 622

/// Instruction has been removed
#[error("Instruction has been removed")]
InstructionDeprecated,
InstructionDeprecated, // 623

/// Proposal is missing signatories required by its governance
#[error("Proposal is missing required signatories")]
MissingRequiredSignatories,
MissingRequiredSignatories, // 624

/// TokenOwnerRecordLock authority must sign
#[error("TokenOwnerRecordLock authority must sign")]
TokenOwnerRecordLockAuthorityMustSign, // 625

/// TokenOwnerRecordLock is expired
#[error("TokenOwnerRecordLock is expired ")]
ExpiredTokenOwnerRecordLock, // 626

/// TokenOwnerRecord locked
#[error("TokenOwnerRecord locked")]
TokenOwnerRecordLocked, // 627

/// Invalid TokenOwnerRecordLockAuthority
#[error("Invalid TokenOwnerRecordLockAuthority")]
InvalidTokenOwnerRecordLockAuthority, // 628

/// TokenOwnerRecordLock authority already exists
#[error("TokenOwnerRecordLock authority already exists")]
TokenOwnerRecordLockAuthorityAlreadyExists, // 628
}

impl PrintProgramError for GovernanceError {
Expand Down
135 changes: 134 additions & 1 deletion governance/program/src/instruction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ use {
realm::{
get_governing_token_holding_address, get_realm_address,
GoverningTokenConfigAccountArgs, GoverningTokenConfigArgs, RealmConfigArgs,
SetRealmAuthorityAction,
SetRealmAuthorityAction, SetRealmConfigItemArgs,
},
realm_config::get_realm_config_address,
required_signatory::get_required_signatory_address,
Expand All @@ -29,6 +29,7 @@ use {
borsh::{BorshDeserialize, BorshSchema, BorshSerialize},
solana_program::{
bpf_loader_upgradeable,
clock::UnixTimestamp,
instruction::{AccountMeta, Instruction},
pubkey::Pubkey,
system_program, sysvar,
Expand Down Expand Up @@ -663,6 +664,53 @@ pub enum GovernanceInstruction {
/// 2. `[writable]` Beneficiary Account which would receive lamports from
/// the disposed RequiredSignatory Account
RemoveRequiredSignatory,

/// Sets TokenOwnerRecord lock for the given authority and lock id
///
/// 0. `[]` Realm
/// 1. `[]` RealmConfig
/// 2. `[writable]` TokenOwnerRecord the lock is set for
/// 3. `[signer]` Lock authority issuing the lock
/// 4. `[signer]` Payer
/// 5. `[]` System
SetTokenOwnerRecordLock {
/// Custom lock id which can be used by the authority to issue
/// different locks
#[allow(dead_code)]
lock_id: u8,

/// The timestamp when the lock expires or None if it never expires
#[allow(dead_code)]
expiry: Option<UnixTimestamp>,
},

/// Removes TokenOwnerRecord lock for the given authority and lock id
///
/// 0. `[writable]` TokenOwnerRecord the lock is removed from
/// 1. `[signer]` Lock authority which issued the lock
RemoveTokenOwnerRecordLock {
/// Custom lock id identifying the lock to remove
#[allow(dead_code)]
lock_id: u8,
},

/// Sets Realm config item
/// Note:
/// This instruction is used to set a single RealmConfig item at a time
/// In the current version it only supports TokenOwnerRecordLockAuthority
/// however eventually all Realm configuration items should be set using
/// this instruction and SetRealmConfig instruction should be deprecated
///
/// 0. `[writable]` Realm account
/// 1. `[writable]` RealmConfig account
/// 2. `[signer]` Realm authority
/// 3. `[signer]` Payer
/// 4. `[]` System
SetRealmConfigItem {
#[allow(dead_code)]
/// Config args
args: SetRealmConfigItemArgs,
},
}

/// Creates CreateRealm instruction
Expand Down Expand Up @@ -1883,3 +1931,88 @@ pub fn complete_proposal(
data: instruction.try_to_vec().unwrap(),
}
}

/// Creates SetTokenOwnerRecordLock instruction to issue TokenOwnerRecord lock
pub fn set_token_owner_record_lock(
program_id: &Pubkey,
// Accounts
realm: &Pubkey,
token_owner_record: &Pubkey,
token_owner_record_lock_authority: &Pubkey,
payer: &Pubkey,
// Args
lock_id: u8,
expiry: Option<UnixTimestamp>,
) -> Instruction {
let realm_config_address = get_realm_config_address(program_id, realm);

let accounts = vec![
AccountMeta::new_readonly(*realm, false),
AccountMeta::new_readonly(realm_config_address, false),
AccountMeta::new(*token_owner_record, false),
AccountMeta::new_readonly(*token_owner_record_lock_authority, true),
AccountMeta::new(*payer, true),
AccountMeta::new_readonly(system_program::id(), false),
];

let instruction = GovernanceInstruction::SetTokenOwnerRecordLock { lock_id, expiry };

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

/// Creates RemoveTokenOwnerRecordLock instruction to remove TokenOwnerRecord
/// lock
pub fn remove_token_owner_record_lock(
program_id: &Pubkey,
// Accounts
token_owner_record: &Pubkey,
token_owner_record_lock_authority: &Pubkey,
// Args
lock_id: u8,
) -> Instruction {
let accounts = vec![
AccountMeta::new(*token_owner_record, false),
AccountMeta::new_readonly(*token_owner_record_lock_authority, true),
];

let instruction = GovernanceInstruction::RemoveTokenOwnerRecordLock { lock_id };

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

/// Creates SetRealmConfigItem instruction to set realm config
pub fn set_realm_config_item(
program_id: &Pubkey,
// Accounts
realm: &Pubkey,
realm_authority: &Pubkey,
payer: &Pubkey,
// Args
args: SetRealmConfigItemArgs,
) -> Instruction {
let realm_config_address = get_realm_config_address(program_id, realm);

let accounts = vec![
AccountMeta::new(*realm, false),
AccountMeta::new(realm_config_address, false),
AccountMeta::new_readonly(*realm_authority, true),
AccountMeta::new(*payer, true),
AccountMeta::new_readonly(system_program::id(), false),
];

let instruction = GovernanceInstruction::SetRealmConfigItem { args };

Instruction {
program_id: *program_id,
accounts,
data: instruction.try_to_vec().unwrap(),
}
}
18 changes: 18 additions & 0 deletions governance/program/src/processor/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,15 @@ mod process_insert_transaction;
mod process_refund_proposal_deposit;
mod process_relinquish_vote;
mod process_remove_required_signatory;
mod process_remove_token_owner_record_lock;
mod process_remove_transaction;
mod process_revoke_governing_tokens;
mod process_set_governance_config;
mod process_set_governance_delegate;
mod process_set_realm_authority;
mod process_set_realm_config;
mod process_set_realm_config_item;
mod process_set_token_owner_record_lock;
mod process_sign_off_proposal;
mod process_update_program_metadata;
mod process_withdraw_governing_tokens;
Expand Down Expand Up @@ -54,12 +57,15 @@ use {
process_refund_proposal_deposit::*,
process_relinquish_vote::*,
process_remove_required_signatory::*,
process_remove_token_owner_record_lock::*,
process_remove_transaction::*,
process_revoke_governing_tokens::*,
process_set_governance_config::*,
process_set_governance_delegate::*,
process_set_realm_authority::*,
process_set_realm_config::*,
process_set_realm_config_item::*,
process_set_token_owner_record_lock::*,
process_sign_off_proposal::*,
process_update_program_metadata::*,
process_withdraw_governing_tokens::*,
Expand Down Expand Up @@ -243,5 +249,17 @@ pub fn process_instruction(
GovernanceInstruction::RemoveRequiredSignatory => {
process_remove_required_signatory(program_id, accounts)
}

GovernanceInstruction::SetTokenOwnerRecordLock { lock_id, expiry } => {
process_set_token_owner_record_lock(program_id, accounts, lock_id, expiry)
}

GovernanceInstruction::RemoveTokenOwnerRecordLock { lock_id } => {
process_remove_token_owner_record_lock(program_id, accounts, lock_id)
}

GovernanceInstruction::SetRealmConfigItem { args } => {
process_set_realm_config_item(program_id, accounts, args)
}
}
}
2 changes: 2 additions & 0 deletions governance/program/src/processor/process_create_realm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,12 +95,14 @@ pub fn process_create_realm(
let community_token_config = resolve_governing_token_config(
account_info_iter,
&realm_config_args.community_token_config_args,
None,
)?;

// 13, 14
let council_token_config = resolve_governing_token_config(
account_info_iter,
&realm_config_args.council_token_config_args,
None,
)?;

let realm_config_data = RealmConfigAccount {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,8 @@ pub fn process_create_token_owner_record(
outstanding_proposal_count: 0,
version: TOKEN_OWNER_RECORD_LAYOUT_VERSION,
reserved: [0; 6],
reserved_v2: [0; 128],
reserved_v2: [0; 124],
locks: vec![],
};

create_and_serialize_account_signed(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,8 @@ pub fn process_deposit_governing_tokens(
outstanding_proposal_count: 0,
version: TOKEN_OWNER_RECORD_LAYOUT_VERSION,
reserved: [0; 6],
reserved_v2: [0; 128],
reserved_v2: [0; 124],
locks: vec![],
};

create_and_serialize_account_signed(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
//! Program state processor

use {
crate::{error::GovernanceError, state::token_owner_record::get_token_owner_record_data},
solana_program::{
account_info::{next_account_info, AccountInfo},
clock::Clock,
entrypoint::ProgramResult,
pubkey::Pubkey,
sysvar::Sysvar,
},
};

/// Processes RemoveTokenOwnerRecordLock instruction
pub fn process_remove_token_owner_record_lock(
program_id: &Pubkey,
accounts: &[AccountInfo],
lock_id: u8,
) -> ProgramResult {
let account_info_iter = &mut accounts.iter();

let token_owner_record_info = next_account_info(account_info_iter)?; // 0
let token_owner_record_lock_authority_info = next_account_info(account_info_iter)?; // 1

let clock = Clock::get()?;

if !token_owner_record_lock_authority_info.is_signer {
return Err(GovernanceError::TokenOwnerRecordLockAuthorityMustSign.into());
}

let mut token_owner_record_data =
get_token_owner_record_data(program_id, token_owner_record_info)?;

// Remove expired locks and matching the authority and lock type to remove
token_owner_record_data.trim_locks(
clock.unix_timestamp,
lock_id,
token_owner_record_lock_authority_info.key,
);

token_owner_record_data.serialize(&mut token_owner_record_info.data.borrow_mut()[..])?;

Ok(())
}
Loading
Loading