upgrade/
processor.rs

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}