1use {
4 crate::{error::RecordError, instruction::RecordInstruction, state::RecordData},
5 solana_account_info::{next_account_info, AccountInfo},
6 solana_msg::msg,
7 solana_program_error::{ProgramError, ProgramResult},
8 solana_program_pack::IsInitialized,
9 solana_pubkey::Pubkey,
10};
11
12fn check_authority(authority_info: &AccountInfo, expected_authority: &Pubkey) -> ProgramResult {
13 if expected_authority != authority_info.key {
14 msg!("Incorrect record authority provided");
15 return Err(RecordError::IncorrectAuthority.into());
16 }
17 if !authority_info.is_signer {
18 msg!("Record authority signature missing");
19 return Err(ProgramError::MissingRequiredSignature);
20 }
21 Ok(())
22}
23
24pub fn process_instruction(
26 _program_id: &Pubkey,
27 accounts: &[AccountInfo],
28 input: &[u8],
29) -> ProgramResult {
30 let instruction = RecordInstruction::unpack(input)?;
31 let account_info_iter = &mut accounts.iter();
32
33 match instruction {
34 RecordInstruction::Initialize => {
35 msg!("RecordInstruction::Initialize");
36
37 let data_info = next_account_info(account_info_iter)?;
38 let authority_info = next_account_info(account_info_iter)?;
39
40 let raw_data = &mut data_info.data.borrow_mut();
41 if raw_data.len() < RecordData::WRITABLE_START_INDEX {
42 return Err(ProgramError::InvalidAccountData);
43 }
44
45 let account_data = bytemuck::try_from_bytes_mut::<RecordData>(
46 &mut raw_data[..RecordData::WRITABLE_START_INDEX],
47 )
48 .map_err(|_| ProgramError::InvalidArgument)?;
49 if account_data.is_initialized() {
50 msg!("Record account already initialized");
51 return Err(ProgramError::AccountAlreadyInitialized);
52 }
53
54 account_data.authority = *authority_info.key;
55 account_data.version = RecordData::CURRENT_VERSION;
56 Ok(())
57 }
58
59 RecordInstruction::Write { offset, data } => {
60 msg!("RecordInstruction::Write");
61 let data_info = next_account_info(account_info_iter)?;
62 let authority_info = next_account_info(account_info_iter)?;
63 {
64 let raw_data = &data_info.data.borrow();
65 if raw_data.len() < RecordData::WRITABLE_START_INDEX {
66 return Err(ProgramError::InvalidAccountData);
67 }
68 let account_data = bytemuck::try_from_bytes::<RecordData>(
69 &raw_data[..RecordData::WRITABLE_START_INDEX],
70 )
71 .map_err(|_| ProgramError::InvalidArgument)?;
72 if !account_data.is_initialized() {
73 msg!("Record account not initialized");
74 return Err(ProgramError::UninitializedAccount);
75 }
76 check_authority(authority_info, &account_data.authority)?;
77 }
78 let start = RecordData::WRITABLE_START_INDEX.saturating_add(offset as usize);
79 let end = start.saturating_add(data.len());
80 if end > data_info.data.borrow().len() {
81 Err(ProgramError::AccountDataTooSmall)
82 } else {
83 data_info.data.borrow_mut()[start..end].copy_from_slice(data);
84 Ok(())
85 }
86 }
87
88 RecordInstruction::SetAuthority => {
89 msg!("RecordInstruction::SetAuthority");
90 let data_info = next_account_info(account_info_iter)?;
91 let authority_info = next_account_info(account_info_iter)?;
92 let new_authority_info = next_account_info(account_info_iter)?;
93 let raw_data = &mut data_info.data.borrow_mut();
94 if raw_data.len() < RecordData::WRITABLE_START_INDEX {
95 return Err(ProgramError::InvalidAccountData);
96 }
97 let account_data = bytemuck::try_from_bytes_mut::<RecordData>(
98 &mut raw_data[..RecordData::WRITABLE_START_INDEX],
99 )
100 .map_err(|_| ProgramError::InvalidArgument)?;
101 if !account_data.is_initialized() {
102 msg!("Record account not initialized");
103 return Err(ProgramError::UninitializedAccount);
104 }
105 check_authority(authority_info, &account_data.authority)?;
106 account_data.authority = *new_authority_info.key;
107 Ok(())
108 }
109
110 RecordInstruction::CloseAccount => {
111 msg!("RecordInstruction::CloseAccount");
112 let data_info = next_account_info(account_info_iter)?;
113 let authority_info = next_account_info(account_info_iter)?;
114 let destination_info = next_account_info(account_info_iter)?;
115 let raw_data = &mut data_info.data.borrow_mut();
116 if raw_data.len() < RecordData::WRITABLE_START_INDEX {
117 return Err(ProgramError::InvalidAccountData);
118 }
119 let account_data = bytemuck::try_from_bytes_mut::<RecordData>(
120 &mut raw_data[..RecordData::WRITABLE_START_INDEX],
121 )
122 .map_err(|_| ProgramError::InvalidArgument)?;
123 if !account_data.is_initialized() {
124 msg!("Record not initialized");
125 return Err(ProgramError::UninitializedAccount);
126 }
127 check_authority(authority_info, &account_data.authority)?;
128 let destination_starting_lamports = destination_info.lamports();
129 let data_lamports = data_info.lamports();
130 **data_info.lamports.borrow_mut() = 0;
131 **destination_info.lamports.borrow_mut() = destination_starting_lamports
132 .checked_add(data_lamports)
133 .ok_or(RecordError::Overflow)?;
134 Ok(())
135 }
136
137 RecordInstruction::Reallocate { data_length } => {
138 msg!("RecordInstruction::Reallocate");
139 let data_info = next_account_info(account_info_iter)?;
140 let authority_info = next_account_info(account_info_iter)?;
141
142 {
143 let raw_data = &mut data_info.data.borrow_mut();
144 if raw_data.len() < RecordData::WRITABLE_START_INDEX {
145 return Err(ProgramError::InvalidAccountData);
146 }
147 let account_data = bytemuck::try_from_bytes_mut::<RecordData>(
148 &mut raw_data[..RecordData::WRITABLE_START_INDEX],
149 )
150 .map_err(|_| ProgramError::InvalidArgument)?;
151 if !account_data.is_initialized() {
152 msg!("Record not initialized");
153 return Err(ProgramError::UninitializedAccount);
154 }
155 check_authority(authority_info, &account_data.authority)?;
156 }
157
158 let needed_account_length = std::mem::size_of::<RecordData>()
161 .checked_add(
162 usize::try_from(data_length).map_err(|_| ProgramError::InvalidArgument)?,
163 )
164 .unwrap();
165
166 if data_info.data_len() >= needed_account_length {
168 msg!("no additional reallocation needed");
169 return Ok(());
170 }
171 msg!(
172 "reallocating +{:?} bytes",
173 needed_account_length
174 .checked_sub(data_info.data_len())
175 .unwrap(),
176 );
177 data_info.resize(needed_account_length)?;
178 Ok(())
179 }
180 }
181}