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

Commit bc00009

Browse files
Governance: Realm authority validation (#2787)
* feat: Ensure realm authority can be set to realm's governances only * chore: update comments for governance account check * chore: update realm authority comments * chore: update comments * chore: update comments Co-authored-by: Jon Cinque <[email protected]>
1 parent a98977f commit bc00009

File tree

7 files changed

+120
-13
lines changed

7 files changed

+120
-13
lines changed

governance/program/src/instruction.rs

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -410,10 +410,11 @@ pub enum GovernanceInstruction {
410410
///
411411
/// 0. `[writable]` Realm account
412412
/// 1. `[signer]` Current Realm authority
413+
/// 2. `[]` New realm authority. Must be one of the realm governances when set
413414
SetRealmAuthority {
414415
#[allow(dead_code)]
415-
/// New realm authority or None to remove authority
416-
new_realm_authority: Option<Pubkey>,
416+
/// Indicates whether the realm authority should be removed and set to None
417+
remove_authority: bool,
417418
},
418419

419420
/// Sets realm config
@@ -1271,15 +1272,20 @@ pub fn set_realm_authority(
12711272
new_realm_authority: &Option<Pubkey>,
12721273
// Args
12731274
) -> Instruction {
1274-
let accounts = vec![
1275+
let mut accounts = vec![
12751276
AccountMeta::new(*realm, false),
12761277
AccountMeta::new_readonly(*realm_authority, true),
12771278
];
12781279

1279-
let instruction = GovernanceInstruction::SetRealmAuthority {
1280-
new_realm_authority: *new_realm_authority,
1280+
let remove_authority = if let Some(new_realm_authority) = new_realm_authority {
1281+
accounts.push(AccountMeta::new_readonly(*new_realm_authority, false));
1282+
false
1283+
} else {
1284+
true
12811285
};
12821286

1287+
let instruction = GovernanceInstruction::SetRealmAuthority { remove_authority };
1288+
12831289
Instruction {
12841290
program_id: *program_id,
12851291
accounts,

governance/program/src/processor/mod.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -191,9 +191,9 @@ pub fn process_instruction(
191191
GovernanceInstruction::FlagInstructionError {} => {
192192
process_flag_instruction_error(program_id, accounts)
193193
}
194-
GovernanceInstruction::SetRealmAuthority {
195-
new_realm_authority,
196-
} => process_set_realm_authority(program_id, accounts, new_realm_authority),
194+
GovernanceInstruction::SetRealmAuthority { remove_authority } => {
195+
process_set_realm_authority(program_id, accounts, remove_authority)
196+
}
197197
GovernanceInstruction::SetRealmConfig { config_args } => {
198198
process_set_realm_config(program_id, accounts, config_args)
199199
}

governance/program/src/processor/process_set_realm_authority.rs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,16 @@ use solana_program::{
77
pubkey::Pubkey,
88
};
99

10-
use crate::{error::GovernanceError, state::realm::get_realm_data_for_authority};
10+
use crate::{
11+
error::GovernanceError,
12+
state::{governance::assert_governance_for_realm, realm::get_realm_data_for_authority},
13+
};
1114

1215
/// Processes SetRealmAuthority instruction
1316
pub fn process_set_realm_authority(
1417
program_id: &Pubkey,
1518
accounts: &[AccountInfo],
16-
new_realm_authority: Option<Pubkey>,
19+
remove_authority: bool,
1720
) -> ProgramResult {
1821
let account_info_iter = &mut accounts.iter();
1922

@@ -27,6 +30,18 @@ pub fn process_set_realm_authority(
2730
return Err(GovernanceError::RealmAuthorityMustSign.into());
2831
}
2932

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)?;
41+
42+
Some(*new_realm_authority_info.key)
43+
};
44+
3045
realm_data.authority = new_realm_authority;
3146

3247
realm_data.serialize(&mut *realm_info.data.borrow_mut())?;

governance/program/src/state/governance.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,16 @@ pub fn get_governance_data_for_realm(
127127
Ok(governance_data)
128128
}
129129

130+
/// Checks the given account is a governance account and belongs to the given realm
131+
pub fn assert_governance_for_realm(
132+
program_id: &Pubkey,
133+
governance_info: &AccountInfo,
134+
realm: &Pubkey,
135+
) -> Result<(), ProgramError> {
136+
get_governance_data_for_realm(program_id, governance_info, realm)?;
137+
Ok(())
138+
}
139+
130140
/// Returns ProgramGovernance PDA seeds
131141
pub fn get_program_governance_address_seeds<'a>(
132142
realm: &'a Pubkey,

governance/program/src/state/legacy.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ pub struct RealmV1 {
8383
pub reserved: [u8; 8],
8484

8585
/// Realm authority. The authority must sign transactions which update the realm config
86-
/// The authority can be transferer to Realm Governance and hence make the Realm self governed through proposals
86+
/// The authority should be transferred to Realm Governance to make the Realm self governed through proposals
8787
pub authority: Option<Pubkey>,
8888

8989
/// Governance Realm name

governance/program/src/state/realm.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ pub struct Realm {
7070
pub reserved: [u8; 8],
7171

7272
/// Realm authority. The authority must sign transactions which update the realm config
73-
/// The authority can be transferer to Realm Governance and hence make the Realm self governed through proposals
73+
/// The authority should be transferred to Realm Governance to make the Realm self governed through proposals
7474
pub authority: Option<Pubkey>,
7575

7676
/// Governance Realm name

governance/program/tests/process_set_realm_authority.rs

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ mod program_test;
77

88
use program_test::*;
99
use spl_governance::error::GovernanceError;
10+
use spl_governance_tools::error::GovernanceToolsError;
1011

1112
#[tokio::test]
1213
async fn test_set_realm_authority() {
@@ -15,7 +16,23 @@ async fn test_set_realm_authority() {
1516

1617
let realm_cookie = governance_test.with_realm().await;
1718

18-
let new_realm_authority = Pubkey::new_unique();
19+
let governed_account_cookie = governance_test.with_governed_account().await;
20+
21+
let token_owner_record_cookie = governance_test
22+
.with_community_token_deposit(&realm_cookie)
23+
.await
24+
.unwrap();
25+
26+
let account_governance_cookie = governance_test
27+
.with_account_governance(
28+
&realm_cookie,
29+
&governed_account_cookie,
30+
&token_owner_record_cookie,
31+
)
32+
.await
33+
.unwrap();
34+
35+
let new_realm_authority = account_governance_cookie.address;
1936

2037
// Act
2138
governance_test
@@ -31,6 +48,26 @@ async fn test_set_realm_authority() {
3148
assert_eq!(realm_account.authority, Some(new_realm_authority));
3249
}
3350

51+
#[tokio::test]
52+
async fn test_set_realm_authority_with_non_existing_new_authority_error() {
53+
// Arrange
54+
let mut governance_test = GovernanceProgramTest::start_new().await;
55+
56+
let realm_cookie = governance_test.with_realm().await;
57+
58+
let new_realm_authority = Pubkey::new_unique();
59+
60+
// Act
61+
let err = governance_test
62+
.set_realm_authority(&realm_cookie, &Some(new_realm_authority))
63+
.await
64+
.err()
65+
.unwrap();
66+
67+
// Assert
68+
assert_eq!(err, GovernanceToolsError::AccountDoesNotExist.into());
69+
}
70+
3471
#[tokio::test]
3572
async fn test_set_realm_authority_to_none() {
3673
// Arrange
@@ -125,3 +162,42 @@ async fn test_set_realm_authority_with_authority_must_sign_error() {
125162
// Assert
126163
assert_eq!(err, GovernanceError::RealmAuthorityMustSign.into());
127164
}
165+
166+
#[tokio::test]
167+
async fn test_set_realm_authority_with_governance_from_other_realm_error() {
168+
// Arrange
169+
let mut governance_test = GovernanceProgramTest::start_new().await;
170+
171+
let realm_cookie = governance_test.with_realm().await;
172+
173+
// Setup other realm
174+
let realm_cookie2 = governance_test.with_realm().await;
175+
176+
let governed_account_cookie2 = governance_test.with_governed_account().await;
177+
178+
let token_owner_record_cookie2 = governance_test
179+
.with_community_token_deposit(&realm_cookie2)
180+
.await
181+
.unwrap();
182+
183+
let account_governance_cookie2 = governance_test
184+
.with_account_governance(
185+
&realm_cookie2,
186+
&governed_account_cookie2,
187+
&token_owner_record_cookie2,
188+
)
189+
.await
190+
.unwrap();
191+
192+
let new_realm_authority = account_governance_cookie2.address;
193+
194+
// Act
195+
let err = governance_test
196+
.set_realm_authority(&realm_cookie, &Some(new_realm_authority))
197+
.await
198+
.err()
199+
.unwrap();
200+
201+
// Assert
202+
assert_eq!(err, GovernanceError::InvalidRealmForGovernance.into());
203+
}

0 commit comments

Comments
 (0)