-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathdelegate.rs
167 lines (151 loc) · 6.32 KB
/
delegate.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
use borsh::BorshDeserialize;
use solana_program::msg;
use solana_program::program_error::ProgramError;
use solana_program::sysvar::Sysvar;
use solana_program::{
account_info::AccountInfo, entrypoint::ProgramResult, pubkey::Pubkey, system_program,
};
use crate::args::DelegateArgs;
use crate::processor::utils::curve::is_on_curve;
use crate::processor::utils::loaders::{
load_owned_pda, load_pda, load_program, load_signer, load_uninitialized_pda,
};
use crate::processor::utils::pda::create_pda;
use crate::state::{DelegationMetadata, DelegationRecord};
use crate::{
delegate_buffer_seeds_from_delegated_account, delegation_metadata_seeds_from_delegated_account,
delegation_record_seeds_from_delegated_account,
};
/// Delegates an account
///
/// Accounts:
/// 0: `[signer]` the account paying for the transaction
/// 1: `[signer]` the account to delegate
/// 2: `[]` the owner of the account to delegate
/// 3: `[writable]` the buffer account we use to temporarily store the account data
/// during owner change
/// 4: `[writable]` the delegation record account
/// 5: `[writable]` the delegation metadata account
/// 6: `[]` the system program
///
/// Requirements:
///
/// - delegation buffer is initialized
/// - delegation record is uninitialized
/// - delegation metadata is uninitialized
///
/// Steps:
/// 1. Checks that the account is owned by the delegation program, that the buffer is initialized and derived correctly from the PDA
/// - Also checks that the delegated_account is a signer (enforcing that the instruction is being called from CPI) & other constraints
/// 2. Copies the data from the buffer into the original account
/// 3. Creates a Delegation Record to store useful information about the delegation event
/// 4. Creates a Delegated Account Seeds to store the seeds used to derive the delegate account. Needed for undelegation.
///
/// Usage:
///
/// This instruction is meant to be called via CPI with the owning program signing for the
/// delegated account.
pub fn process_delegate(
_program_id: &Pubkey,
accounts: &[AccountInfo],
data: &[u8],
) -> ProgramResult {
let [payer, delegated_account, owner_program, delegate_buffer_account, delegation_record_account, delegation_metadata_account, system_program] =
accounts
else {
return Err(ProgramError::NotEnoughAccountKeys);
};
let args = DelegateArgs::try_from_slice(data)?;
load_owned_pda(delegated_account, &crate::id(), "delegated account")?;
load_program(system_program, system_program::id(), "system program")?;
// Validate seeds if the delegate account is not on curve, i.e. is a PDA
if !is_on_curve(delegated_account.key) {
let seeds_to_validate: Vec<&[u8]> = args.seeds.iter().map(|v| v.as_slice()).collect();
let (derived_pda, _) =
Pubkey::find_program_address(seeds_to_validate.as_ref(), owner_program.key);
if derived_pda.ne(delegated_account.key) {
msg!(
"Expected delegated PDA to be {}, but got {}",
derived_pda,
delegated_account.key
);
return Err(ProgramError::InvalidSeeds);
}
}
// Check that the buffer PDA is initialized and derived correctly from the PDA
load_pda(
delegate_buffer_account,
delegate_buffer_seeds_from_delegated_account!(delegated_account.key),
owner_program.key,
true,
"delegate buffer",
)?;
// Check that the delegation record PDA is uninitialized
let delegation_record_bump = load_uninitialized_pda(
delegation_record_account,
delegation_record_seeds_from_delegated_account!(delegated_account.key),
&crate::id(),
true,
"delegation record",
)?;
// Check that the delegation metadata PDA is uninitialized
let delegation_metadata_bump = load_uninitialized_pda(
delegation_metadata_account,
delegation_metadata_seeds_from_delegated_account!(delegated_account.key),
&crate::id(),
true,
"delegation metadata",
)?;
// Check that payer and delegated_account are signers, this ensures the instruction is being called from CPI
load_signer(payer, "payer")?;
load_signer(delegated_account, "delegated account")?;
// Initialize the delegation record PDA
create_pda(
delegation_record_account,
&crate::id(),
DelegationRecord::size_with_discriminator(),
delegation_record_seeds_from_delegated_account!(delegated_account.key),
delegation_record_bump,
system_program,
payer,
)?;
// Initialize the delegation record
let delegation_record = DelegationRecord {
owner: *owner_program.key,
authority: args.validator.unwrap_or(Pubkey::default()),
commit_frequency_ms: args.commit_frequency_ms as u64,
delegation_slot: solana_program::clock::Clock::get()?.slot,
lamports: delegated_account.lamports(),
};
let mut delegation_record_data = delegation_record_account.try_borrow_mut_data()?;
delegation_record.to_bytes_with_discriminator(&mut delegation_record_data)?;
// Initialize the account seeds PDA
let mut delegation_metadata_bytes = vec![];
let delegation_metadata = DelegationMetadata {
seeds: args.seeds,
last_update_external_slot: 0,
is_undelegatable: false,
rent_payer: *payer.key,
};
delegation_metadata.to_bytes_with_discriminator(&mut delegation_metadata_bytes)?;
// Initialize the delegation metadata PDA
create_pda(
delegation_metadata_account,
&crate::id(),
delegation_metadata_bytes.len(),
delegation_metadata_seeds_from_delegated_account!(delegated_account.key),
delegation_metadata_bump,
system_program,
payer,
)?;
// Copy the seeds to the delegated metadata PDA
let mut delegation_metadata_data = delegation_metadata_account.try_borrow_mut_data()?;
delegation_metadata_data.copy_from_slice(&delegation_metadata_bytes);
// Copy the data from the buffer into the original account
if !delegate_buffer_account.data_is_empty() {
let mut delegated_data = delegated_account.try_borrow_mut_data()?;
let delegate_buffer_data = delegate_buffer_account.try_borrow_data()?;
(*delegated_data).copy_from_slice(&delegate_buffer_data);
}
Ok(())
}