Skip to content
This repository was archived by the owner on Mar 11, 2025. It is now read-only.

Commit aee41fc

Browse files
authored
Governance: Make realm authority check optional (#2827)
* feat: support unchecked SetRealmAuthority * chore: add test for unchecked set realm authority Co-authored-by: Jon Cinque
1 parent 0094255 commit aee41fc

File tree

7 files changed

+118
-36
lines changed

7 files changed

+118
-36
lines changed

governance/program/src/instruction.rs

Lines changed: 18 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@ use crate::{
1111
program_metadata::get_program_metadata_address,
1212
proposal::{get_proposal_address, VoteType},
1313
proposal_instruction::{get_proposal_instruction_address, InstructionData},
14-
realm::{get_governing_token_holding_address, get_realm_address, RealmConfigArgs},
14+
realm::{
15+
get_governing_token_holding_address, get_realm_address, RealmConfigArgs,
16+
SetRealmAuthorityAction,
17+
},
1518
realm_config::get_realm_config_address,
1619
signatory_record::get_signatory_record_address,
1720
token_owner_record::get_token_owner_record_address,
@@ -417,8 +420,8 @@ pub enum GovernanceInstruction {
417420
/// 2. `[]` New realm authority. Must be one of the realm governances when set
418421
SetRealmAuthority {
419422
#[allow(dead_code)]
420-
/// Indicates whether the realm authority should be removed and set to None
421-
remove_authority: bool,
423+
/// Set action ( SetUnchecked, SetChecked, Remove)
424+
action: SetRealmAuthorityAction,
422425
},
423426

424427
/// Sets realm config
@@ -1306,22 +1309,26 @@ pub fn set_realm_authority(
13061309
// Accounts
13071310
realm: &Pubkey,
13081311
realm_authority: &Pubkey,
1309-
new_realm_authority: &Option<Pubkey>,
1312+
new_realm_authority: Option<&Pubkey>,
13101313
// Args
1314+
action: SetRealmAuthorityAction,
13111315
) -> Instruction {
13121316
let mut accounts = vec![
13131317
AccountMeta::new(*realm, false),
13141318
AccountMeta::new_readonly(*realm_authority, true),
13151319
];
13161320

1317-
let remove_authority = if let Some(new_realm_authority) = new_realm_authority {
1318-
accounts.push(AccountMeta::new_readonly(*new_realm_authority, false));
1319-
false
1320-
} else {
1321-
true
1322-
};
1321+
match action {
1322+
SetRealmAuthorityAction::SetChecked | SetRealmAuthorityAction::SetUnchecked => {
1323+
accounts.push(AccountMeta::new_readonly(
1324+
*new_realm_authority.unwrap(),
1325+
false,
1326+
));
1327+
}
1328+
SetRealmAuthorityAction::Remove => {}
1329+
}
13231330

1324-
let instruction = GovernanceInstruction::SetRealmAuthority { remove_authority };
1331+
let instruction = GovernanceInstruction::SetRealmAuthority { action };
13251332

13261333
Instruction {
13271334
program_id: *program_id,

governance/program/src/processor/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -198,8 +198,8 @@ pub fn process_instruction(
198198
GovernanceInstruction::FlagInstructionError {} => {
199199
process_flag_instruction_error(program_id, accounts)
200200
}
201-
GovernanceInstruction::SetRealmAuthority { remove_authority } => {
202-
process_set_realm_authority(program_id, accounts, remove_authority)
201+
GovernanceInstruction::SetRealmAuthority { action } => {
202+
process_set_realm_authority(program_id, accounts, action)
203203
}
204204
GovernanceInstruction::SetRealmConfig { config_args } => {
205205
process_set_realm_config(program_id, accounts, config_args)

governance/program/src/processor/process_set_realm_authority.rs

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,17 @@ use solana_program::{
99

1010
use crate::{
1111
error::GovernanceError,
12-
state::{governance::assert_governance_for_realm, realm::get_realm_data_for_authority},
12+
state::{
13+
governance::assert_governance_for_realm,
14+
realm::{get_realm_data_for_authority, SetRealmAuthorityAction},
15+
},
1316
};
1417

1518
/// Processes SetRealmAuthority instruction
1619
pub fn process_set_realm_authority(
1720
program_id: &Pubkey,
1821
accounts: &[AccountInfo],
19-
remove_authority: bool,
22+
action: SetRealmAuthorityAction,
2023
) -> ProgramResult {
2124
let account_info_iter = &mut accounts.iter();
2225

@@ -30,16 +33,18 @@ pub fn process_set_realm_authority(
3033
return Err(GovernanceError::RealmAuthorityMustSign.into());
3134
}
3235

33-
let new_realm_authority = if remove_authority {
34-
None
35-
} else {
36-
// Ensure the new realm authority is one of the governances from the realm
37-
// Note: This is not a security feature because governance creation is only gated with min_community_tokens_to_create_governance
38-
// The check is done to prevent scenarios where the authority could be accidentally set to a wrong or none existing account
39-
let new_realm_authority_info = next_account_info(account_info_iter)?; // 2
40-
assert_governance_for_realm(program_id, new_realm_authority_info, realm_info.key)?;
36+
let new_realm_authority = match action {
37+
SetRealmAuthorityAction::SetUnchecked | SetRealmAuthorityAction::SetChecked => {
38+
let new_realm_authority_info = next_account_info(account_info_iter)?; // 2
4139

42-
Some(*new_realm_authority_info.key)
40+
if action == SetRealmAuthorityAction::SetChecked {
41+
// Ensure the new realm authority is one of the governances from the realm
42+
assert_governance_for_realm(program_id, new_realm_authority_info, realm_info.key)?;
43+
}
44+
45+
Some(*new_realm_authority_info.key)
46+
}
47+
SetRealmAuthorityAction::Remove => None,
4348
};
4449

4550
realm_data.authority = new_realm_authority;

governance/program/src/state/realm.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,22 @@ pub struct RealmConfigArgs {
3636
pub use_max_community_voter_weight_addin: bool,
3737
}
3838

39+
/// SetRealmAuthority instruction action
40+
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)]
41+
pub enum SetRealmAuthorityAction {
42+
/// Sets realm authority without any checks
43+
/// Uncheck option allows to set the realm authority to non governance accounts
44+
SetUnchecked,
45+
46+
/// Sets realm authority and checks the new new authority is one of the realm's governances
47+
// Note: This is not a security feature because governance creation is only gated with min_community_tokens_to_create_governance
48+
// The check is done to prevent scenarios where the authority could be accidentally set to a wrong or none existing account
49+
SetChecked,
50+
51+
/// Removes realm authority
52+
Remove,
53+
}
54+
3955
/// Realm Config defining Realm parameters.
4056
#[repr(C)]
4157
#[derive(Clone, Debug, PartialEq, BorshDeserialize, BorshSerialize, BorshSchema)]

governance/program/tests/process_set_realm_authority.rs

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ async fn test_set_realm_authority() {
3636

3737
// Act
3838
governance_test
39-
.set_realm_authority(&realm_cookie, &Some(new_realm_authority))
39+
.set_realm_authority(&realm_cookie, Some(&new_realm_authority))
4040
.await
4141
.unwrap();
4242

@@ -59,7 +59,7 @@ async fn test_set_realm_authority_with_non_existing_new_authority_error() {
5959

6060
// Act
6161
let err = governance_test
62-
.set_realm_authority(&realm_cookie, &Some(new_realm_authority))
62+
.set_realm_authority(&realm_cookie, Some(&new_realm_authority))
6363
.await
6464
.err()
6565
.unwrap();
@@ -77,7 +77,7 @@ async fn test_set_realm_authority_to_none() {
7777

7878
// Act
7979
governance_test
80-
.set_realm_authority(&realm_cookie, &None)
80+
.set_realm_authority(&realm_cookie, None)
8181
.await
8282
.unwrap();
8383

@@ -89,6 +89,29 @@ async fn test_set_realm_authority_to_none() {
8989
assert_eq!(realm_account.authority, None);
9090
}
9191

92+
#[tokio::test]
93+
async fn test_set_realm_authority_unchecked() {
94+
// Arrange
95+
let mut governance_test = GovernanceProgramTest::start_new().await;
96+
97+
let realm_cookie = governance_test.with_realm().await;
98+
99+
let new_realm_authority = Pubkey::new_unique();
100+
101+
// Act
102+
governance_test
103+
.set_realm_authority_impl(&realm_cookie, Some(&new_realm_authority), false)
104+
.await
105+
.unwrap();
106+
107+
// Assert
108+
let realm_account = governance_test
109+
.get_realm_account(&realm_cookie.address)
110+
.await;
111+
112+
assert_eq!(realm_account.authority, Some(new_realm_authority));
113+
}
114+
92115
#[tokio::test]
93116
async fn test_set_realm_authority_with_no_authority_error() {
94117
// Arrange
@@ -97,15 +120,15 @@ async fn test_set_realm_authority_with_no_authority_error() {
97120
let realm_cookie = governance_test.with_realm().await;
98121

99122
governance_test
100-
.set_realm_authority(&realm_cookie, &None)
123+
.set_realm_authority(&realm_cookie, None)
101124
.await
102125
.unwrap();
103126

104127
let new_realm_authority = Pubkey::new_unique();
105128

106129
// Act
107130
let err = governance_test
108-
.set_realm_authority(&realm_cookie, &Some(new_realm_authority))
131+
.set_realm_authority(&realm_cookie, Some(&new_realm_authority))
109132
.await
110133
.err()
111134
.unwrap();
@@ -129,7 +152,7 @@ async fn test_set_realm_authority_with_invalid_authority_error() {
129152

130153
// Act
131154
let err = governance_test
132-
.set_realm_authority(&realm_cookie, &Some(new_realm_authority))
155+
.set_realm_authority(&realm_cookie, Some(&new_realm_authority))
133156
.await
134157
.err()
135158
.unwrap();
@@ -151,7 +174,8 @@ async fn test_set_realm_authority_with_authority_must_sign_error() {
151174
let err = governance_test
152175
.set_realm_authority_using_instruction(
153176
&realm_cookie,
154-
&Some(new_realm_authority),
177+
Some(&new_realm_authority),
178+
true,
155179
|i| i.accounts[1].is_signer = false, // realm_authority
156180
Some(&[]),
157181
)
@@ -193,7 +217,7 @@ async fn test_set_realm_authority_with_governance_from_other_realm_error() {
193217

194218
// Act
195219
let err = governance_test
196-
.set_realm_authority(&realm_cookie, &Some(new_realm_authority))
220+
.set_realm_authority(&realm_cookie, Some(&new_realm_authority))
197221
.await
198222
.err()
199223
.unwrap();

governance/program/tests/process_set_realm_config.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ async fn test_set_realm_config_with_no_authority_error() {
112112
};
113113

114114
governance_test
115-
.set_realm_authority(&realm_cookie, &None)
115+
.set_realm_authority(&realm_cookie, None)
116116
.await
117117
.unwrap();
118118

governance/program/tests/program_test/mod.rs

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ use spl_governance::{
4343
},
4444
realm::{
4545
get_governing_token_holding_address, get_realm_address, Realm, RealmConfig,
46-
RealmConfigArgs,
46+
RealmConfigArgs, SetRealmAuthorityAction,
4747
},
4848
realm_config::{get_realm_config_address, RealmConfigAccount},
4949
signatory_record::{get_signatory_record_address, SignatoryRecord},
@@ -857,11 +857,29 @@ impl GovernanceProgramTest {
857857
pub async fn set_realm_authority(
858858
&mut self,
859859
realm_cookie: &RealmCookie,
860-
new_realm_authority: &Option<Pubkey>,
860+
new_realm_authority: Option<&Pubkey>,
861861
) -> Result<(), ProgramError> {
862862
self.set_realm_authority_using_instruction(
863863
realm_cookie,
864864
new_realm_authority,
865+
true,
866+
NopOverride,
867+
None,
868+
)
869+
.await
870+
}
871+
872+
#[allow(dead_code)]
873+
pub async fn set_realm_authority_impl(
874+
&mut self,
875+
realm_cookie: &RealmCookie,
876+
new_realm_authority: Option<&Pubkey>,
877+
check_authority: bool,
878+
) -> Result<(), ProgramError> {
879+
self.set_realm_authority_using_instruction(
880+
realm_cookie,
881+
new_realm_authority,
882+
check_authority,
865883
NopOverride,
866884
None,
867885
)
@@ -872,15 +890,27 @@ impl GovernanceProgramTest {
872890
pub async fn set_realm_authority_using_instruction<F: Fn(&mut Instruction)>(
873891
&mut self,
874892
realm_cookie: &RealmCookie,
875-
new_realm_authority: &Option<Pubkey>,
893+
new_realm_authority: Option<&Pubkey>,
894+
check_authority: bool,
876895
instruction_override: F,
877896
signers_override: Option<&[&Keypair]>,
878897
) -> Result<(), ProgramError> {
898+
let action = if new_realm_authority.is_some() {
899+
if check_authority {
900+
SetRealmAuthorityAction::SetChecked
901+
} else {
902+
SetRealmAuthorityAction::SetUnchecked
903+
}
904+
} else {
905+
SetRealmAuthorityAction::Remove
906+
};
907+
879908
let mut set_realm_authority_ix = set_realm_authority(
880909
&self.program_id,
881910
&realm_cookie.address,
882911
&realm_cookie.realm_authority.as_ref().unwrap().pubkey(),
883912
new_realm_authority,
913+
action,
884914
);
885915

886916
instruction_override(&mut set_realm_authority_ix);

0 commit comments

Comments
 (0)