1use solana_program::{
2 account_info::{AccountInfo, next_account_info},
3 entrypoint::ProgramResult, msg,
4 program::{invoke_signed}, pubkey::Pubkey, system_instruction,
5 sysvar::{rent::Rent, Sysvar},
6};
7use borsh::{BorshDeserialize, BorshSerialize};
8use solana_program::secp256k1_recover::{SECP256K1_PUBLIC_KEY_LENGTH, SECP256K1_SIGNATURE_LENGTH};
9use crate::state::{MAX_ADMIN_SIZE, UpgradeAdmin};
10use crate::instructions::UpgradeInstruction;
11use crate::ecdsa::verify_ecdsa_signature;
12use crate::{HASH_CONSTANT, PDA_ADMIN_SEED};
13use crate::error::UpgradeError;
14
15pub fn process_instruction<'a>(
16 program_id: &'a Pubkey,
17 accounts: &'a [AccountInfo<'a>],
18 input: &[u8],
19) -> ProgramResult {
20 let instruction = UpgradeInstruction::try_from_slice(input)?;
21 match instruction {
22 UpgradeInstruction::InitializeAdmin(args) => {
23 msg!("Instruction: Create upgrade admin");
24 process_init_admin(program_id, accounts, args.public_key, args.contract)
25 }
26 UpgradeInstruction::ChangePublicKey(args) => {
27 msg!("Instruction: Change public key");
28 process_change_public_key(program_id, accounts, args.new_public_key, args.signature, args.recovery_id)
29 }
30 UpgradeInstruction::ChangeAuthority(args) => {
31 msg!("Instruction: Transfer upgrade authority");
32 process_change_authority(program_id, accounts, args.signature, args.recovery_id)
33 }
34 UpgradeInstruction::Upgrade(args) => {
35 msg!("Instruction: Upgrade");
36 process_upgrade(program_id, accounts, args.signature, args.recovery_id)
37 }
38 }
39}
40
41
42pub fn process_init_admin<'a>(
43 program_id: &'a Pubkey,
44 accounts: &'a [AccountInfo<'a>],
45 public_key: [u8; SECP256K1_PUBLIC_KEY_LENGTH],
46 upgrade_program: Pubkey,
47) -> ProgramResult {
48 let account_info_iter = &mut accounts.iter();
49
50 let upgrade_admin_info = next_account_info(account_info_iter)?;
51 let fee_payer_info = next_account_info(account_info_iter)?;
52 let system_program = next_account_info(account_info_iter)?;
53 let rent_info = next_account_info(account_info_iter)?;
54
55 let (upgrade_key, bump) = Pubkey::find_program_address(&[PDA_ADMIN_SEED.as_bytes(), upgrade_program.as_ref()], &program_id);
56 if upgrade_key != *upgrade_admin_info.key {
57 return Err(UpgradeError::WrongAdmin.into());
58 }
59
60 let rent = Rent::from_account_info(rent_info)?;
61
62 let instruction = system_instruction::create_account(
63 fee_payer_info.key,
64 upgrade_admin_info.key,
65 rent.minimum_balance(MAX_ADMIN_SIZE),
66 MAX_ADMIN_SIZE as u64,
67 program_id,
68 );
69
70 invoke_signed(
71 &instruction,
72 &[
73 fee_payer_info.clone(),
74 upgrade_admin_info.clone(),
75 system_program.clone(),
76 ],
77 &[&[PDA_ADMIN_SEED.as_bytes(), upgrade_program.as_ref(), &[bump]]],
78 )?;
79
80 let mut upgrade_admin: UpgradeAdmin = BorshDeserialize::deserialize(&mut upgrade_admin_info.data.borrow_mut().as_ref())?;
81 if upgrade_admin.is_initialized {
82 return Err(UpgradeError::AlreadyInUse.into());
83 }
84
85 upgrade_admin.contract = upgrade_program;
86 upgrade_admin.public_key = public_key;
87 upgrade_admin.is_initialized = true;
88 upgrade_admin.nonce = 0;
89 upgrade_admin.serialize(&mut *upgrade_admin_info.data.borrow_mut())?;
90 Ok(())
91}
92
93
94pub fn process_change_public_key<'a>(
95 program_id: &'a Pubkey,
96 accounts: &'a [AccountInfo<'a>],
97 new_public_key: [u8; SECP256K1_PUBLIC_KEY_LENGTH],
98 signature: [u8; SECP256K1_SIGNATURE_LENGTH],
99 recovery_id: u8,
100) -> ProgramResult {
101 let account_info_iter = &mut accounts.iter();
102 let upgrade_admin_info = next_account_info(account_info_iter)?;
103
104 let mut upgrade_admin: UpgradeAdmin = BorshDeserialize::deserialize(&mut upgrade_admin_info.data.borrow_mut().as_ref())?;
105 if !upgrade_admin.is_initialized {
106 return Err(UpgradeError::NotInitialized.into());
107 }
108
109 let (upgrade_admin_key, _) = Pubkey::find_program_address(&[PDA_ADMIN_SEED.as_bytes(), upgrade_admin.contract.as_ref()], &program_id);
110 if upgrade_admin_key != *upgrade_admin_info.key {
111 return Err(UpgradeError::WrongSeeds.into());
112 }
113
114 verify_ecdsa_signature(
115 solana_program::keccak::hash(
116 &[
117 upgrade_admin.contract.as_ref(),
118 upgrade_admin.nonce.to_be_bytes().as_ref(),
119 HASH_CONSTANT.as_bytes(),
120 new_public_key.as_ref(),
121 ].concat()
122 ).as_ref(),
123 signature.as_slice(),
124 recovery_id,
125 upgrade_admin.public_key,
126 )?;
127
128 upgrade_admin.public_key = new_public_key;
129 upgrade_admin.nonce = upgrade_admin.nonce + 1;
130 upgrade_admin.serialize(&mut *upgrade_admin_info.data.borrow_mut())?;
131 Ok(())
132}
133
134
135pub fn process_change_authority<'a>(
136 program_id: &'a Pubkey,
137 accounts: &'a [AccountInfo<'a>],
138 signature: [u8; SECP256K1_SIGNATURE_LENGTH],
139 recovery_id: u8,
140) -> ProgramResult {
141 let account_info_iter = &mut accounts.iter();
142 let upgrade_admin_info = next_account_info(account_info_iter)?;
143 let upgrade_program_data = next_account_info(account_info_iter)?;
144 let authority = next_account_info(account_info_iter)?;
145
146
147 let mut upgrade_admin: UpgradeAdmin = BorshDeserialize::deserialize(&mut upgrade_admin_info.data.borrow_mut().as_ref())?;
148 if !upgrade_admin.is_initialized {
149 return Err(UpgradeError::NotInitialized.into());
150 }
151
152 let (upgrade_admin_key, bump) = Pubkey::find_program_address(&[PDA_ADMIN_SEED.as_bytes(), upgrade_admin.contract.as_ref()], &program_id);
153 if upgrade_admin_key != *upgrade_admin_info.key {
154 return Err(UpgradeError::WrongSeeds.into());
155 }
156
157 verify_ecdsa_signature(
158 solana_program::keccak::hash(
159 &[
160 upgrade_admin.contract.as_ref(),
161 upgrade_admin.nonce.to_be_bytes().as_ref(),
162 HASH_CONSTANT.as_bytes(),
163 authority.key.as_ref(),
164 ].concat()
165 ).as_ref(),
166 signature.as_slice(),
167 recovery_id,
168 upgrade_admin.public_key,
169 )?;
170
171
172 let instruction = solana_program::bpf_loader_upgradeable::set_upgrade_authority(
173 &upgrade_admin.contract,
174 upgrade_admin_info.key,
175 Some(authority.key),
176 );
177
178 invoke_signed(
179 &instruction,
180 &[
181 upgrade_program_data.clone(),
182 upgrade_admin_info.clone(),
183 authority.clone(),
184 ],
185 &[&[PDA_ADMIN_SEED.as_bytes(), upgrade_admin.contract.as_ref(), &[bump]]],
186 )?;
187
188
189 upgrade_admin.nonce = upgrade_admin.nonce + 1;
190 upgrade_admin.serialize(&mut *upgrade_admin_info.data.borrow_mut())?;
191 Ok(())
192}
193
194
195pub fn process_upgrade<'a>(
196 program_id: &'a Pubkey,
197 accounts: &'a [AccountInfo<'a>],
198 signature: [u8; SECP256K1_SIGNATURE_LENGTH],
199 recovery_id: u8,
200) -> ProgramResult {
201 let account_info_iter = &mut accounts.iter();
202 let upgrade_admin_info = next_account_info(account_info_iter)?;
203 let upgrade_program_data = next_account_info(account_info_iter)?;
204 let upgrade_program = next_account_info(account_info_iter)?;
205 let upgrade_buffer = next_account_info(account_info_iter)?;
206 let upgrade_spill = next_account_info(account_info_iter)?;
207 let rent_info = next_account_info(account_info_iter)?;
208 let clock_info = next_account_info(account_info_iter)?;
209
210 let (upgrade_admin_key, bump) = Pubkey::find_program_address(&[PDA_ADMIN_SEED.as_bytes(), upgrade_program.key.as_ref()], &program_id);
211 if upgrade_admin_key != *upgrade_admin_info.key {
212 return Err(UpgradeError::WrongSeeds.into());
213 }
214
215 let mut upgrade_admin: UpgradeAdmin = BorshDeserialize::deserialize(&mut upgrade_admin_info.data.borrow_mut().as_ref())?;
216 if !upgrade_admin.is_initialized {
217 return Err(UpgradeError::NotInitialized.into());
218 }
219
220 verify_ecdsa_signature(
221 solana_program::keccak::hash(
222 &[
223 upgrade_admin.contract.as_ref(),
224 upgrade_admin.nonce.to_be_bytes().as_ref(),
225 HASH_CONSTANT.as_bytes(),
226 upgrade_buffer.key.as_ref(),
227 ].concat()
228 ).as_ref(),
229 signature.as_slice(),
230 recovery_id,
231 upgrade_admin.public_key,
232 )?;
233
234 let instruction = solana_program::bpf_loader_upgradeable::upgrade(
235 upgrade_program.key,
236 upgrade_buffer.key,
237 &upgrade_admin_key,
238 upgrade_spill.key,
239 );
240
241 invoke_signed(
242 &instruction,
243 &[
244 upgrade_program_data.clone(),
245 upgrade_program.clone(),
246 upgrade_buffer.clone(),
247 upgrade_spill.clone(),
248 rent_info.clone(),
249 clock_info.clone(),
250 upgrade_admin_info.clone(),
251 ],
252 &[&[PDA_ADMIN_SEED.as_bytes(), upgrade_program.key.as_ref(), &[bump]]],
253 )?;
254
255 upgrade_admin.nonce = upgrade_admin.nonce + 1;
256 upgrade_admin.serialize(&mut *upgrade_admin_info.data.borrow_mut())?;
257 Ok(())
258}