-
Notifications
You must be signed in to change notification settings - Fork 18
Expand file tree
/
Copy pathdelegate.rs
More file actions
287 lines (266 loc) · 9.65 KB
/
delegate.rs
File metadata and controls
287 lines (266 loc) · 9.65 KB
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
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
use borsh::BorshDeserialize;
use pinocchio::{
address::address_eq,
cpi::{Seed, Signer},
error::ProgramError,
sysvars::{clock::Clock, Sysvar},
AccountView, Address, ProgramResult,
};
use pinocchio_log::log;
use pinocchio_system::instructions as system;
use crate::{
args::DelegateArgs,
consts::{DEFAULT_VALIDATOR_IDENTITY, RENT_EXCEPTION_ZERO_BYTES_LAMPORTS},
error::DlpError,
pda,
processor::{
fast::{to_pinocchio_program_error, utils::pda::create_pda},
utils::curve::is_on_curve_fast,
},
requires::{
require_owned_pda, require_pda, require_signer,
require_uninitialized_pda, DelegationMetadataCtx, DelegationRecordCtx,
},
state::{DelegationMetadata, DelegationRecord},
};
/// 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
///
/// 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: &Address,
accounts: &[AccountView],
data: &[u8],
) -> ProgramResult {
process_delegate_inner(program_id, accounts, data, false)
}
/// Delegates an account while allowing any validator identity.
pub fn process_delegate_with_any_validator(
program_id: &Address,
accounts: &[AccountView],
data: &[u8],
) -> ProgramResult {
process_delegate_inner(program_id, accounts, data, true)
}
fn process_delegate_inner(
_program_id: &Address,
accounts: &[AccountView],
data: &[u8],
allow_system_program_validator: bool,
) -> ProgramResult {
let [payer, delegated_account, owner_program, delegate_buffer_account, delegation_record_account, delegation_metadata_account, _system_program] =
accounts
else {
return Err(ProgramError::NotEnoughAccountKeys);
};
require_owned_pda(
delegated_account,
&crate::fast::ID,
"delegated account",
)?;
// Check that payer and delegated_account are signers, this ensures the instruction is being called from CPI
require_signer(payer, "payer")?;
require_signer(delegated_account, "delegated account")?;
// Check that the buffer PDA is initialized and derived correctly from the PDA
require_pda(
delegate_buffer_account,
&[
pda::DELEGATE_BUFFER_TAG,
delegated_account.address().as_ref(),
],
owner_program.address(),
true,
"delegate buffer",
)?;
// Check that the delegation record PDA is uninitialized
// TODO (snawaz): This check could be safely avoided, as create_pda would anyway fail.
// Could save considerable CU, especially in the v2 version where we will pass the bumps
let delegation_record_bump = require_uninitialized_pda(
delegation_record_account,
&[
pda::DELEGATION_RECORD_TAG,
delegated_account.address().as_ref(),
],
&crate::fast::ID,
true,
DelegationRecordCtx,
)?;
// Check that the delegation metadata PDA is uninitialized
// TODO (snawaz): This check could be safely avoided, as create_pda would anyway fail.
// Could save considerable CU, especially in the v2 version where we will pass the bumps
let delegation_metadata_bump = require_uninitialized_pda(
delegation_metadata_account,
&[
pda::DELEGATION_METADATA_TAG,
delegated_account.address().as_ref(),
],
&crate::fast::ID,
true,
DelegationMetadataCtx,
)?;
let args = DelegateArgs::try_from_slice(data)
.map_err(|_| ProgramError::InvalidInstructionData)?;
if !allow_system_program_validator {
if let Some(validator) = args.validator {
if validator.to_bytes() == pinocchio_system::ID.to_bytes() {
return Err(
DlpError::DelegationToSystemProgramNotAllowed.into()
);
}
}
}
// Validate seeds if the delegate account is not on curve, i.e. is a PDA
// If the owner is the system program, we check if the account is derived from the delegation program,
// allowing delegation of escrow accounts
if !is_on_curve_fast(delegated_account.address()) {
let program_id =
if address_eq(owner_program.address(), &pinocchio_system::ID) {
&crate::fast::ID
} else {
owner_program.address()
};
let seeds_to_validate: &[&[u8]] = match args.seeds.len() {
1 => &[&args.seeds[0]],
2 => &[&args.seeds[0], &args.seeds[1]],
3 => &[&args.seeds[0], &args.seeds[1], &args.seeds[2]],
4 => &[
&args.seeds[0],
&args.seeds[1],
&args.seeds[2],
&args.seeds[3],
],
5 => &[
&args.seeds[0],
&args.seeds[1],
&args.seeds[2],
&args.seeds[3],
&args.seeds[4],
],
6 => &[
&args.seeds[0],
&args.seeds[1],
&args.seeds[2],
&args.seeds[3],
&args.seeds[4],
&args.seeds[5],
],
7 => &[
&args.seeds[0],
&args.seeds[1],
&args.seeds[2],
&args.seeds[3],
&args.seeds[4],
&args.seeds[5],
&args.seeds[6],
],
8 => &[
&args.seeds[0],
&args.seeds[1],
&args.seeds[2],
&args.seeds[3],
&args.seeds[4],
&args.seeds[5],
&args.seeds[6],
&args.seeds[7],
],
_ => return Err(DlpError::TooManySeeds.into()),
};
let derived_pda =
Address::find_program_address(seeds_to_validate, program_id).0;
if !address_eq(&derived_pda, delegated_account.address()) {
log!("Expected delegated PDA to be: ");
derived_pda.log();
log!("but got: ");
delegated_account.address().log();
return Err(ProgramError::InvalidSeeds);
}
}
create_pda(
delegation_record_account,
&crate::fast::ID,
DelegationRecord::size_with_discriminator(),
&[Signer::from(&[
Seed::from(pda::DELEGATION_RECORD_TAG),
Seed::from(delegated_account.address().as_ref()),
Seed::from(&[delegation_record_bump]),
])],
payer,
)?;
// Initialize the delegation record
let delegation_record = DelegationRecord {
owner: owner_program.address().to_bytes().into(),
authority: args.validator.unwrap_or(DEFAULT_VALIDATOR_IDENTITY),
commit_frequency_ms: args.commit_frequency_ms as u64,
delegation_slot: Clock::get()?.slot,
lamports: delegated_account.lamports(),
};
let mut delegation_record_data =
delegation_record_account.try_borrow_mut()?;
delegation_record
.to_bytes_with_discriminator(&mut delegation_record_data)
.map_err(to_pinocchio_program_error)?;
let delegation_metadata = DelegationMetadata {
seeds: args.seeds,
last_update_nonce: 0,
is_undelegatable: false,
rent_payer: payer.address().to_bytes().into(),
};
// Initialize the delegation metadata PDA
create_pda(
delegation_metadata_account,
&crate::fast::ID,
delegation_metadata.serialized_size(),
&[Signer::from(&[
Seed::from(pda::DELEGATION_METADATA_TAG),
Seed::from(delegated_account.address().as_ref()),
Seed::from(&[delegation_metadata_bump]),
])],
payer,
)?;
// Copy the seeds to the delegated metadata PDA
let mut delegation_metadata_data =
delegation_metadata_account.try_borrow_mut()?;
delegation_metadata
.to_bytes_with_discriminator(&mut delegation_metadata_data.as_mut())
.map_err(to_pinocchio_program_error)?;
// Copy the data from the buffer into the original account
if !delegate_buffer_account.is_data_empty() {
let mut delegated_data = delegated_account.try_borrow_mut()?;
let delegate_buffer_data = delegate_buffer_account.try_borrow()?;
(*delegated_data).copy_from_slice(&delegate_buffer_data);
}
// Make the account rent exempt if it's not
if delegated_account.lamports() == 0 && delegated_account.data_len() == 0 {
system::Transfer {
from: payer,
to: delegated_account,
lamports: RENT_EXCEPTION_ZERO_BYTES_LAMPORTS,
}
.invoke()?;
}
Ok(())
}