Skip to content

Commit be4d5be

Browse files
feat: add validator whitelisting (#39)
* feat: add validator whitelisting * chore: code refactoring * chore: code refactoring
1 parent f27ee18 commit be4d5be

15 files changed

+197
-45
lines changed

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,3 +58,9 @@ or:
5858
```
5959
cd tests/integration && anchor test
6060
```
61+
62+
## Build for Mainnet
63+
64+
```
65+
RUSTFLAGS="--cfg mainnet" cargo build-sbf
66+
```

src/consts_whitelist.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/// Whitelisted identities
2+
/// TODO: Hardcoded for now, should use a dynamic list
3+
use solana_program::pubkey;
4+
use solana_program::pubkey::Pubkey;
5+
6+
use std::collections::HashMap;
7+
8+
#[cfg(not(mainnet))]
9+
const WHITELISTED_IDENTITIES: &[(Pubkey, Vec<Pubkey>)] = &[
10+
(
11+
pubkey!("mAGicPQYBMvcYveUZA5F5UNNwyHvfYh5xkLS2Fr1mev"),
12+
vec![],
13+
),
14+
(
15+
pubkey!("zbitnhqG6MLu3E6XBJGEd7WarnKDeqzriB14hr74Fjb"),
16+
vec![],
17+
),
18+
(
19+
pubkey!("sups7xRrKcWsVoGEsuoYp7o4dAdwDPEMpHH1sxYKEm4"),
20+
vec![],
21+
),
22+
(
23+
pubkey!("tEsT3eV6RFCWs1BZ7AXTzasHqTtMnMLCB2tjQ42TDXD"),
24+
vec![],
25+
),
26+
];
27+
28+
#[cfg(mainnet)]
29+
const WHITELISTED_IDENTITIES: &[(Pubkey, Vec<Pubkey>)] = &[
30+
(
31+
pubkey!("zbitnhqG6MLu3E6XBJGEd7WarnKDeqzriB14hr74Fjb"),
32+
vec![],
33+
),
34+
(
35+
pubkey!("sups7xRrKcWsVoGEsuoYp7o4dAdwDPEMpHH1sxYKEm4"),
36+
vec![],
37+
),
38+
];
39+
40+
/// Get the whitelisted identities
41+
pub fn get_whitelisted_identities() -> HashMap<Pubkey, Vec<Pubkey>> {
42+
WHITELISTED_IDENTITIES.iter().cloned().collect()
43+
}

src/error.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ pub enum DlpError {
1111
"Account cannot be undelegated, is_delegatable is false and valid_until isn't reached"
1212
)]
1313
Undelegatable = 1,
14+
#[error("Invalid Authority for the current target program")]
15+
InvalidAuthorityForProgram = 2,
16+
#[error("Delegated account does not match the expected account")]
17+
InvalidDelegatedAccount = 3,
18+
#[error("Reimbursement account does not match the expected account")]
19+
InvalidReimbursementAccount = 4,
1420
}
1521

1622
impl From<DlpError> for ProgramError {

src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use instruction::*;
77
use processor::*;
88

99
pub mod consts;
10+
pub mod consts_whitelist;
1011
pub mod error;
1112
pub mod instruction;
1213
mod loaders;
@@ -15,6 +16,7 @@ mod processor;
1516
pub mod state;
1617
pub mod utils;
1718
pub mod utils_account;
19+
pub mod verify_commitment;
1820
pub mod verify_state;
1921

2022
declare_id!("DELeGGvXpWV2fqJUhqcF5ZSYMS4JTLjteaAMARRSaeSh");

src/processor/commit_state.rs

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,17 +5,17 @@ use solana_program::program_error::ProgramError;
55
use solana_program::{
66
account_info::AccountInfo,
77
entrypoint::ProgramResult,
8-
msg,
98
pubkey::Pubkey,
109
{self},
1110
};
1211

1312
use crate::consts::{COMMIT_RECORD, COMMIT_STATE, DELEGATION_METADATA, DELEGATION_RECORD};
1413
use crate::instruction::CommitAccountArgs;
1514
use crate::loaders::{load_initialized_pda, load_owned_pda, load_signer, load_uninitialized_pda};
16-
use crate::state::{CommitRecord, DelegationMetadata};
15+
use crate::state::{CommitRecord, DelegationMetadata, DelegationRecord};
1716
use crate::utils::create_pda;
1817
use crate::utils_account::{AccountDeserialize, Discriminator};
18+
use crate::verify_commitment::verify_commitment;
1919

2020
/// Commit a new state of a delegated Pda
2121
///
@@ -30,7 +30,6 @@ pub fn process_commit_state(
3030
accounts: &[AccountInfo],
3131
data: &[u8],
3232
) -> ProgramResult {
33-
msg!("Processing CommitState");
3433
let args = CommitAccountArgs::try_from_slice(data)?;
3534
let data: &[u8] = args.data.as_ref();
3635

@@ -57,6 +56,11 @@ pub fn process_commit_state(
5756
&crate::id(),
5857
true,
5958
)?;
59+
60+
// Load delegation record
61+
let mut delegation_data = delegation_record.try_borrow_mut_data()?;
62+
let delegation = DelegationRecord::try_from_bytes_mut(&mut delegation_data)?;
63+
6064
let mut delegation_metadata_data = delegation_metadata.try_borrow_mut_data()?;
6165
let mut delegation_metadata = DelegationMetadata::try_from_slice(&delegation_metadata_data)?;
6266

@@ -114,5 +118,7 @@ pub fn process_commit_state(
114118
let mut buffer_data = commit_state_account.try_borrow_mut_data()?;
115119
(*buffer_data).copy_from_slice(data);
116120

121+
verify_commitment(authority, delegation, commit_record, commit_state_account)?;
122+
117123
Ok(())
118124
}

src/processor/finalize.rs

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1+
use crate::error::DlpError;
12
use borsh::{BorshDeserialize, BorshSerialize};
23
use solana_program::program_error::ProgramError;
34
use solana_program::rent::Rent;
45
use solana_program::sysvar::Sysvar;
56
use solana_program::{
67
account_info::AccountInfo,
78
entrypoint::ProgramResult,
8-
msg,
99
pubkey::Pubkey,
1010
system_program, {self},
1111
};
@@ -30,13 +30,13 @@ pub fn process_finalize(
3030
accounts: &[AccountInfo],
3131
_data: &[u8],
3232
) -> ProgramResult {
33-
let [payer, delegated_account, committed_state_account, committed_state_record, delegation_record, delegation_metadata, reimbursement, system_program] =
33+
let [authority, delegated_account, committed_state_account, committed_state_record, delegation_record, delegation_metadata, reimbursement, system_program] =
3434
accounts
3535
else {
3636
return Err(ProgramError::NotEnoughAccountKeys);
3737
};
3838

39-
load_signer(payer)?;
39+
load_signer(authority)?;
4040
load_owned_pda(delegated_account, &crate::id())?;
4141
load_owned_pda(committed_state_account, &crate::id())?;
4242
load_owned_pda(committed_state_record, &crate::id())?;
@@ -56,16 +56,19 @@ pub fn process_finalize(
5656
let commit_record_data = committed_state_record.try_borrow_data()?;
5757
let commit_record = CommitRecord::try_from_bytes(&commit_record_data)?;
5858

59-
verify_state(delegation, commit_record, committed_state_account)?;
59+
verify_state(
60+
authority,
61+
delegation,
62+
commit_record,
63+
committed_state_account,
64+
)?;
6065

6166
if !commit_record.account.eq(delegated_account.key) {
62-
msg!("Delegated account does not match the expected account");
63-
return Err(ProgramError::InvalidAccountData);
67+
return Err(DlpError::InvalidDelegatedAccount.into());
6468
}
6569

6670
if !commit_record.identity.eq(reimbursement.key) {
67-
msg!("Reimbursement account does not match the expected account");
68-
return Err(ProgramError::InvalidAccountData);
71+
return Err(DlpError::InvalidReimbursementAccount.into());
6972
}
7073

7174
let new_data = committed_state_account.try_borrow_data()?;

src/processor/undelegate.rs

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use crate::loaders::{load_owned_pda, load_program, load_signer, load_uninitializ
1515
use crate::state::{CommitRecord, DelegationMetadata, DelegationRecord};
1616
use crate::utils::{close_pda, create_pda, ValidateEdwards};
1717
use crate::utils_account::AccountDeserialize;
18+
use crate::verify_state::verify_state;
1819
use solana_program::sysvar::Sysvar;
1920

2021
/// Undelegate a delegated Pda
@@ -38,13 +39,13 @@ pub fn process_undelegate(
3839
accounts: &[AccountInfo],
3940
_data: &[u8],
4041
) -> ProgramResult {
41-
let [payer, delegated_account, owner_program, buffer, committed_state_account, committed_state_record, delegation_record, delegation_metadata, reimbursement, system_program] =
42+
let [authority, delegated_account, owner_program, buffer, committed_state_account, committed_state_record, delegation_record, delegation_metadata, reimbursement, system_program] =
4243
accounts
4344
else {
4445
return Err(ProgramError::NotEnoughAccountKeys);
4546
};
4647

47-
load_signer(payer)?;
48+
load_signer(authority)?;
4849
load_owned_pda(delegated_account, &crate::id())?;
4950
load_owned_pda(delegation_record, &crate::id())?;
5051
load_owned_pda(delegation_metadata, &crate::id())?;
@@ -83,6 +84,16 @@ pub fn process_undelegate(
8384
None
8485
};
8586

87+
// If there is a committed state, verify the state
88+
if commit_record.is_some() {
89+
verify_state(
90+
authority,
91+
delegation,
92+
commit_record.unwrap(),
93+
committed_state_account,
94+
)?;
95+
}
96+
8697
// Load delegated account metadata
8798
let metadata = DelegationMetadata::deserialize(&mut &**delegation_metadata.data.borrow())?;
8899

@@ -108,7 +119,7 @@ pub fn process_undelegate(
108119
},
109120
&[BUFFER, &delegated_account.key.to_bytes(), &[buffer_bump]],
110121
system_program,
111-
payer,
122+
authority,
112123
)?;
113124

114125
if !delegation.owner.eq(owner_program.key) {
@@ -146,7 +157,7 @@ pub fn process_undelegate(
146157
let signer_seeds: &[&[&[u8]]] =
147158
&[&[BUFFER, &delegated_account.key.to_bytes(), &[buffer_bump]]];
148159
cpi_external_undelegate(
149-
payer,
160+
authority,
150161
delegated_account,
151162
buffer,
152163
system_program,
@@ -171,7 +182,7 @@ pub fn process_undelegate(
171182
close_pda(committed_state_record, reimbursement)?;
172183
close_pda(delegation_record, reimbursement)?;
173184
close_pda(committed_state_account, reimbursement)?;
174-
close_pda(buffer, payer)?;
185+
close_pda(buffer, authority)?;
175186
Ok(())
176187
}
177188

src/verify_commitment.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
use solana_program::account_info::AccountInfo;
2+
use solana_program::entrypoint::ProgramResult;
3+
4+
use crate::state::{CommitRecord, DelegationRecord};
5+
use crate::verify_state::verify_state;
6+
7+
/// Verify the committed state
8+
pub(crate) fn verify_commitment(
9+
authority: &AccountInfo,
10+
delegation_record: &DelegationRecord,
11+
committed_record: &CommitRecord,
12+
committed_state: &AccountInfo,
13+
) -> ProgramResult {
14+
verify_state(
15+
authority,
16+
delegation_record,
17+
committed_record,
18+
committed_state,
19+
)
20+
}

src/verify_state.rs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,26 @@
1+
// TODO: Temporary whitelist check. Add the logic to check the state diff, Authority and/or Fraud proofs
2+
3+
use crate::consts_whitelist::get_whitelisted_identities;
4+
use crate::error::DlpError;
15
use crate::state::{CommitRecord, DelegationRecord};
26
use solana_program::account_info::AccountInfo;
37
use solana_program::entrypoint::ProgramResult;
8+
use solana_program::pubkey::Pubkey;
49

510
/// Verify the committed state
611
#[inline(always)]
712
pub(crate) fn verify_state(
8-
_delegation_record: &DelegationRecord,
9-
_committed_state: &CommitRecord,
10-
_new_state: &AccountInfo,
13+
authority: &AccountInfo,
14+
delegation_record: &DelegationRecord,
15+
_committed_record: &CommitRecord,
16+
_committed_state: &AccountInfo,
1117
) -> ProgramResult {
12-
// TODO: Add the logic to check the state diff, Authority and/or Fraud proofs
18+
let whitelisted_identities = get_whitelisted_identities();
19+
let allowed_programs: &Vec<Pubkey> = whitelisted_identities
20+
.get(authority.key)
21+
.ok_or(DlpError::InvalidAuthority)?;
22+
if !allowed_programs.is_empty() && !allowed_programs.contains(&delegation_record.owner) {
23+
return Err(DlpError::InvalidAuthorityForProgram.into());
24+
}
1325
Ok(())
1426
}

tests/fixtures/accounts.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,3 +70,11 @@ pub const ON_CURVE_ACCOUNT_BYTES: [u8; 64] = [
7070
241, 163, 185, 198, 228, 172, 200, 220, 225, 192, 149, 94, 106, 209, 65, 79, 210, 54, 191, 49,
7171
115, 159,
7272
];
73+
74+
#[allow(dead_code)]
75+
pub const TEST_AUTHORITY: [u8; 64] = [
76+
251, 62, 129, 184, 107, 49, 62, 184, 1, 147, 178, 128, 185, 157, 247, 92, 56, 158, 145, 53, 51,
77+
226, 202, 96, 178, 248, 195, 133, 133, 237, 237, 146, 13, 32, 77, 204, 244, 56, 166, 172, 66,
78+
113, 150, 218, 112, 42, 110, 181, 98, 158, 222, 194, 130, 93, 175, 100, 190, 106, 9, 69, 156,
79+
80, 96, 72,
80+
];

tests/integration/Anchor.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ url = "https://api.apr.dev"
1212

1313
[provider]
1414
cluster = "Localnet"
15-
wallet = "~/.config/solana/id.json"
15+
wallet = "./tests/fixtures/provider.json"
1616

1717
[workspace]
1818
members = ["programs/*"]
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
[251,62,129,184,107,49,62,184,1,147,178,128,185,157,247,92,56,158,145,53,51,226,202,96,178,248,195,133,133,237,237,146,13,32,77,204,244,56,166,172,66,113,150,218,112,42,110,181,98,158,222,194,130,93,175,100,190,106,9,69,156,80,96,72]

0 commit comments

Comments
 (0)