1use crate::{
19 instruction::{AccountMeta, Instruction, InstructionError},
20 loader_upgradeable_instruction::UpgradeableLoaderInstruction,
21 pubkey::Pubkey,
22 system_instruction, sysvar,
23};
24
25crate::declare_id!("BPFLoaderUpgradeab1e11111111111111111111111");
26
27#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, Clone, Copy, AbiExample)]
29pub enum UpgradeableLoaderState {
30 Uninitialized,
32 Buffer {
34 authority_address: Option<Pubkey>,
36 },
39 Program {
41 programdata_address: Pubkey,
43 },
44 ProgramData {
46 slot: u64,
48 upgrade_authority_address: Option<Pubkey>,
50 },
53}
54impl UpgradeableLoaderState {
55 pub const fn size_of_buffer_metadata() -> usize {
57 37 }
59
60 pub const fn size_of_programdata_metadata() -> usize {
62 45 }
64
65 pub const fn size_of_program() -> usize {
67 36 }
69
70 pub const fn size_of_buffer(program_len: usize) -> usize {
72 Self::size_of_buffer_metadata().saturating_add(program_len)
73 }
74
75 pub const fn size_of_programdata(program_len: usize) -> usize {
77 Self::size_of_programdata_metadata().saturating_add(program_len)
78 }
79
80 #[deprecated(since = "1.11.0", note = "Please use `size_of_buffer` instead")]
82 pub fn buffer_len(program_len: usize) -> Result<usize, InstructionError> {
83 Ok(Self::size_of_buffer(program_len))
84 }
85
86 #[deprecated(
88 since = "1.11.0",
89 note = "Please use `size_of_buffer_metadata` instead"
90 )]
91 pub fn buffer_data_offset() -> Result<usize, InstructionError> {
92 Ok(Self::size_of_buffer_metadata())
93 }
94
95 #[deprecated(since = "1.11.0", note = "Please use `size_of_program` instead")]
97 pub fn program_len() -> Result<usize, InstructionError> {
98 Ok(Self::size_of_program())
99 }
100
101 #[deprecated(since = "1.11.0", note = "Please use `size_of_programdata` instead")]
103 pub fn programdata_len(program_len: usize) -> Result<usize, InstructionError> {
104 Ok(Self::size_of_programdata(program_len))
105 }
106
107 #[deprecated(
109 since = "1.11.0",
110 note = "Please use `size_of_programdata_metadata` instead"
111 )]
112 pub fn programdata_data_offset() -> Result<usize, InstructionError> {
113 Ok(Self::size_of_programdata_metadata())
114 }
115}
116
117pub fn create_buffer(
119 payer_address: &Pubkey,
120 buffer_address: &Pubkey,
121 authority_address: &Pubkey,
122 lamports: u64,
123 program_len: usize,
124) -> Result<Vec<Instruction>, InstructionError> {
125 Ok(vec![
126 system_instruction::create_account(
127 payer_address,
128 buffer_address,
129 lamports,
130 UpgradeableLoaderState::size_of_buffer(program_len) as u64,
131 &id(),
132 ),
133 Instruction::new_with_bincode(
134 id(),
135 &UpgradeableLoaderInstruction::InitializeBuffer,
136 vec![
137 AccountMeta::new(*buffer_address, false),
138 AccountMeta::new_readonly(*authority_address, false),
139 ],
140 ),
141 ])
142}
143
144pub fn write(
147 buffer_address: &Pubkey,
148 authority_address: &Pubkey,
149 offset: u32,
150 bytes: Vec<u8>,
151) -> Instruction {
152 Instruction::new_with_bincode(
153 id(),
154 &UpgradeableLoaderInstruction::Write { offset, bytes },
155 vec![
156 AccountMeta::new(*buffer_address, false),
157 AccountMeta::new_readonly(*authority_address, true),
158 ],
159 )
160}
161
162pub fn deploy_with_max_program_len(
166 payer_address: &Pubkey,
167 program_address: &Pubkey,
168 buffer_address: &Pubkey,
169 upgrade_authority_address: &Pubkey,
170 program_lamports: u64,
171 max_data_len: usize,
172) -> Result<Vec<Instruction>, InstructionError> {
173 let (programdata_address, _) = Pubkey::find_program_address(&[program_address.as_ref()], &id());
174 Ok(vec![
175 system_instruction::create_account(
176 payer_address,
177 program_address,
178 program_lamports,
179 UpgradeableLoaderState::size_of_program() as u64,
180 &id(),
181 ),
182 Instruction::new_with_bincode(
183 id(),
184 &UpgradeableLoaderInstruction::DeployWithMaxDataLen { max_data_len },
185 vec![
186 AccountMeta::new(*payer_address, true),
187 AccountMeta::new(programdata_address, false),
188 AccountMeta::new(*program_address, false),
189 AccountMeta::new(*buffer_address, false),
190 AccountMeta::new_readonly(sysvar::rent::id(), false),
191 AccountMeta::new_readonly(sysvar::clock::id(), false),
192 AccountMeta::new_readonly(crate::system_program::id(), false),
193 AccountMeta::new_readonly(*upgrade_authority_address, true),
194 ],
195 ),
196 ])
197}
198
199pub fn upgrade(
201 program_address: &Pubkey,
202 buffer_address: &Pubkey,
203 authority_address: &Pubkey,
204 spill_address: &Pubkey,
205) -> Instruction {
206 let (programdata_address, _) = Pubkey::find_program_address(&[program_address.as_ref()], &id());
207 Instruction::new_with_bincode(
208 id(),
209 &UpgradeableLoaderInstruction::Upgrade,
210 vec![
211 AccountMeta::new(programdata_address, false),
212 AccountMeta::new(*program_address, false),
213 AccountMeta::new(*buffer_address, false),
214 AccountMeta::new(*spill_address, false),
215 AccountMeta::new_readonly(sysvar::rent::id(), false),
216 AccountMeta::new_readonly(sysvar::clock::id(), false),
217 AccountMeta::new_readonly(*authority_address, true),
218 ],
219 )
220}
221
222pub fn is_upgrade_instruction(instruction_data: &[u8]) -> bool {
223 !instruction_data.is_empty() && 3 == instruction_data[0]
224}
225
226pub fn is_set_authority_instruction(instruction_data: &[u8]) -> bool {
227 !instruction_data.is_empty() && 4 == instruction_data[0]
228}
229
230pub fn is_close_instruction(instruction_data: &[u8]) -> bool {
231 !instruction_data.is_empty() && 5 == instruction_data[0]
232}
233
234pub fn set_buffer_authority(
236 buffer_address: &Pubkey,
237 current_authority_address: &Pubkey,
238 new_authority_address: &Pubkey,
239) -> Instruction {
240 Instruction::new_with_bincode(
241 id(),
242 &UpgradeableLoaderInstruction::SetAuthority,
243 vec![
244 AccountMeta::new(*buffer_address, false),
245 AccountMeta::new_readonly(*current_authority_address, true),
246 AccountMeta::new_readonly(*new_authority_address, false),
247 ],
248 )
249}
250
251pub fn set_upgrade_authority(
253 program_address: &Pubkey,
254 current_authority_address: &Pubkey,
255 new_authority_address: Option<&Pubkey>,
256) -> Instruction {
257 let (programdata_address, _) = Pubkey::find_program_address(&[program_address.as_ref()], &id());
258
259 let mut metas = vec![
260 AccountMeta::new(programdata_address, false),
261 AccountMeta::new_readonly(*current_authority_address, true),
262 ];
263 if let Some(address) = new_authority_address {
264 metas.push(AccountMeta::new_readonly(*address, false));
265 }
266 Instruction::new_with_bincode(id(), &UpgradeableLoaderInstruction::SetAuthority, metas)
267}
268
269pub fn close(
271 close_address: &Pubkey,
272 recipient_address: &Pubkey,
273 authority_address: &Pubkey,
274) -> Instruction {
275 close_any(
276 close_address,
277 recipient_address,
278 Some(authority_address),
279 None,
280 )
281}
282
283pub fn close_any(
285 close_address: &Pubkey,
286 recipient_address: &Pubkey,
287 authority_address: Option<&Pubkey>,
288 program_address: Option<&Pubkey>,
289) -> Instruction {
290 let mut metas = vec![
291 AccountMeta::new(*close_address, false),
292 AccountMeta::new(*recipient_address, false),
293 ];
294 if let Some(authority_address) = authority_address {
295 metas.push(AccountMeta::new_readonly(*authority_address, true));
296 }
297 if let Some(program_address) = program_address {
298 metas.push(AccountMeta::new(*program_address, false));
299 }
300 Instruction::new_with_bincode(id(), &UpgradeableLoaderInstruction::Close, metas)
301}
302
303pub fn extend_program(
306 program_address: &Pubkey,
307 payer_address: Option<&Pubkey>,
308 additional_bytes: u32,
309) -> Instruction {
310 let (program_data_address, _) =
311 Pubkey::find_program_address(&[program_address.as_ref()], &id());
312 let mut metas = vec![
313 AccountMeta::new(program_data_address, false),
314 AccountMeta::new(*program_address, false),
315 ];
316 if let Some(payer_address) = payer_address {
317 metas.push(AccountMeta::new_readonly(
318 crate::system_program::id(),
319 false,
320 ));
321 metas.push(AccountMeta::new(*payer_address, true));
322 }
323 Instruction::new_with_bincode(
324 id(),
325 &UpgradeableLoaderInstruction::ExtendProgram { additional_bytes },
326 metas,
327 )
328}
329
330#[cfg(test)]
331mod tests {
332 use {super::*, bincode::serialized_size};
333
334 #[test]
335 fn test_state_size_of_buffer_metadata() {
336 let buffer_state = UpgradeableLoaderState::Buffer {
337 authority_address: Some(Pubkey::default()),
338 };
339 let size = serialized_size(&buffer_state).unwrap();
340 assert_eq!(
341 UpgradeableLoaderState::size_of_buffer_metadata() as u64,
342 size
343 );
344 }
345
346 #[test]
347 fn test_state_size_of_programdata_metadata() {
348 let programdata_state = UpgradeableLoaderState::ProgramData {
349 upgrade_authority_address: Some(Pubkey::default()),
350 slot: 0,
351 };
352 let size = serialized_size(&programdata_state).unwrap();
353 assert_eq!(
354 UpgradeableLoaderState::size_of_programdata_metadata() as u64,
355 size
356 );
357 }
358
359 #[test]
360 fn test_state_size_of_program() {
361 let program_state = UpgradeableLoaderState::Program {
362 programdata_address: Pubkey::default(),
363 };
364 let size = serialized_size(&program_state).unwrap();
365 assert_eq!(UpgradeableLoaderState::size_of_program() as u64, size);
366 }
367
368 #[test]
369 #[allow(deprecated)]
370 fn test_account_lengths() {
371 assert_eq!(
372 4,
373 serialized_size(&UpgradeableLoaderState::Uninitialized).unwrap()
374 );
375 assert_eq!(36, UpgradeableLoaderState::program_len().unwrap());
376 assert_eq!(
377 45,
378 UpgradeableLoaderState::programdata_data_offset().unwrap()
379 );
380 assert_eq!(
381 45 + 42,
382 UpgradeableLoaderState::programdata_len(42).unwrap()
383 );
384 }
385
386 fn assert_is_instruction<F>(
387 is_instruction_fn: F,
388 expected_instruction: UpgradeableLoaderInstruction,
389 ) where
390 F: Fn(&[u8]) -> bool,
391 {
392 let result = is_instruction_fn(
393 &bincode::serialize(&UpgradeableLoaderInstruction::InitializeBuffer).unwrap(),
394 );
395 let expected_result = matches!(
396 expected_instruction,
397 UpgradeableLoaderInstruction::InitializeBuffer
398 );
399 assert_eq!(expected_result, result);
400
401 let result = is_instruction_fn(
402 &bincode::serialize(&UpgradeableLoaderInstruction::Write {
403 offset: 0,
404 bytes: vec![],
405 })
406 .unwrap(),
407 );
408 let expected_result = matches!(
409 expected_instruction,
410 UpgradeableLoaderInstruction::Write {
411 offset: _,
412 bytes: _,
413 }
414 );
415 assert_eq!(expected_result, result);
416
417 let result = is_instruction_fn(
418 &bincode::serialize(&UpgradeableLoaderInstruction::DeployWithMaxDataLen {
419 max_data_len: 0,
420 })
421 .unwrap(),
422 );
423 let expected_result = matches!(
424 expected_instruction,
425 UpgradeableLoaderInstruction::DeployWithMaxDataLen { max_data_len: _ }
426 );
427 assert_eq!(expected_result, result);
428
429 let result =
430 is_instruction_fn(&bincode::serialize(&UpgradeableLoaderInstruction::Upgrade).unwrap());
431 let expected_result = matches!(expected_instruction, UpgradeableLoaderInstruction::Upgrade);
432 assert_eq!(expected_result, result);
433
434 let result = is_instruction_fn(
435 &bincode::serialize(&UpgradeableLoaderInstruction::SetAuthority).unwrap(),
436 );
437 let expected_result = matches!(
438 expected_instruction,
439 UpgradeableLoaderInstruction::SetAuthority
440 );
441 assert_eq!(expected_result, result);
442
443 let result =
444 is_instruction_fn(&bincode::serialize(&UpgradeableLoaderInstruction::Close).unwrap());
445 let expected_result = matches!(expected_instruction, UpgradeableLoaderInstruction::Close);
446 assert_eq!(expected_result, result);
447 }
448
449 #[test]
450 fn test_is_set_authority_instruction() {
451 assert!(!is_set_authority_instruction(&[]));
452 assert_is_instruction(
453 is_set_authority_instruction,
454 UpgradeableLoaderInstruction::SetAuthority {},
455 );
456 }
457
458 #[test]
459 fn test_is_upgrade_instruction() {
460 assert!(!is_upgrade_instruction(&[]));
461 assert_is_instruction(
462 is_upgrade_instruction,
463 UpgradeableLoaderInstruction::Upgrade {},
464 );
465 }
466}