upgrade/
instructions.rs

1use borsh::{BorshDeserialize, BorshSerialize};
2use solana_program::pubkey::Pubkey;
3use solana_program::secp256k1_recover::{SECP256K1_PUBLIC_KEY_LENGTH, SECP256K1_SIGNATURE_LENGTH};
4use solana_program::instruction::{Instruction, AccountMeta};
5use crate::PDA_ADMIN_SEED;
6
7#[repr(C)]
8#[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug, Clone)]
9pub struct InitializeAdminArgs {
10    // ECDSA public key (64 byte format)
11    pub public_key: [u8; SECP256K1_PUBLIC_KEY_LENGTH],
12    // Contract to manage
13    pub contract: Pubkey,
14}
15
16#[repr(C)]
17#[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug, Clone)]
18pub struct ChangePublicKeyArgs {
19    // New ECDSA public key (64 byte format)
20    pub new_public_key: [u8; SECP256K1_PUBLIC_KEY_LENGTH],
21    // Signature of keccak_hash(nonce, "solana-upgrade-program".bytes, new_public_key) by old public key
22    pub signature: [u8; SECP256K1_SIGNATURE_LENGTH],
23    // Signature recovery id
24    pub recovery_id: u8,
25}
26
27#[repr(C)]
28#[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug, Clone)]
29pub struct ChangeAuthorityArgs {
30    // Signature of keccak_hash(nonce, "solana-upgrade-program".bytes, new_authority)
31    pub signature: [u8; SECP256K1_SIGNATURE_LENGTH],
32    // Signature recovery id
33    pub recovery_id: u8,
34}
35
36#[repr(C)]
37#[derive(BorshSerialize, BorshDeserialize, PartialEq, Debug, Clone)]
38pub struct UpgradeArgs {
39    // Signature for keccak_hash(target_contract, nonce, "solana-upgrade-program".bytes, buffer_address)
40    pub signature: [u8; SECP256K1_PUBLIC_KEY_LENGTH],
41    // Corresponding seed to use in PDA for admin account
42    pub recovery_id: u8,
43}
44
45#[repr(C)]
46#[derive(BorshSerialize, BorshDeserialize, Clone)]
47pub enum UpgradeInstruction {
48    /// Initialize new UpgradeAdmin that will be an authority for target upgradable program.
49    ///
50    /// Accounts expected by this instruction:
51    ///
52    ///   0. `[writable]` The UpgradeAdmin account to initialize
53    ///   1. `[writable,signer]` The fee payer
54    ///   2. `[]` System program
55    ///   3. `[]` Rent sysvar
56    InitializeAdmin(InitializeAdminArgs),
57
58    /// Change pubkey in UpgradeAdmin. The Keccak Hash of `[target_contract, nonce, "solana-upgrade-program".bytes, new_public_key]`
59    /// should be signed by old public key to perform that operation.
60    ///
61    /// Accounts expected by this instruction:
62    ///
63    ///   0. `[writable]` The UpgradeAdmin account
64    ChangePublicKey(ChangePublicKeyArgs),
65
66    /// Change contract upgrade authority. The Keccak Hash of `[target_contract, nonce, "solana-upgrade-program".bytes, new_authority]`
67    /// should be signed by stored public key to perform that operation.
68    ///
69    /// Accounts expected by this instruction:
70    ///
71    ///   0. `[writable]` The UpgradeAdmin account
72    ///   1. `[writable]` The ProgramData account.
73    ///   2. `[]` The new authority account
74    ///   3. `[]` BPFLoaderUpgradable program
75    ChangeAuthority(ChangeAuthorityArgs),
76
77    /// Upgrade contract. The Keccak Hash of `[target_contract, nonce, "solana-upgrade-program".bytes, buffer_address]`
78    /// should be signed by stored public key to perform that operation.
79    ///
80    /// Accounts expected by this instruction:
81    ///
82    ///   0. `[writable]` The UpgradeAdmin account
83    ///   1. `[writable]` The ProgramData account.
84    ///   2. `[writable]` The Program account corresponding to stores address in UpgradeAdmin.
85    ///   3. `[writable]` The Buffer account where the program data has been
86    ///      written.  The buffer account's authority must match the program's
87    ///      authority
88    ///   4. `[writable]` The spill account.
89    ///   5. `[]` Rent sysvar.
90    ///   6. `[]` Clock sysvar.
91    ///   7. `[]` BPFLoaderUpgradable program
92    Upgrade(UpgradeArgs),
93}
94
95pub fn initialize_admin(
96    program_id: Pubkey,
97    contract: Pubkey,
98    fee_payer: Pubkey,
99    public_key: [u8; SECP256K1_PUBLIC_KEY_LENGTH],
100) -> Instruction {
101    let (admin, _) = Pubkey::find_program_address(&[PDA_ADMIN_SEED.as_bytes(), contract.as_ref()], &program_id);
102    Instruction{
103        program_id,
104        data: UpgradeInstruction::InitializeAdmin(
105            InitializeAdminArgs {
106                public_key,
107                contract: Default::default(),
108            }
109        ).try_to_vec().unwrap(),
110        accounts: vec![
111            AccountMeta::new(admin, false),
112            AccountMeta::new(fee_payer, true),
113            AccountMeta::new_readonly(solana_program::system_program::id(), false),
114            AccountMeta::new_readonly(solana_program::sysvar::clock::id(), false),
115        ],
116    }
117}
118
119
120pub fn change_public_key(
121    program_id: Pubkey,
122    contract: Pubkey,
123    new_public_key: [u8; SECP256K1_PUBLIC_KEY_LENGTH],
124    signature: [u8; SECP256K1_SIGNATURE_LENGTH],
125    recovery_id: u8,
126) -> Instruction {
127    let (admin, _) = Pubkey::find_program_address(&[PDA_ADMIN_SEED.as_bytes(), contract.as_ref()], &program_id);
128    Instruction{
129        program_id,
130        data: UpgradeInstruction::ChangePublicKey(
131            ChangePublicKeyArgs {
132                new_public_key,
133                signature,
134                recovery_id,
135            }
136        ).try_to_vec().unwrap(),
137        accounts: vec![
138            AccountMeta::new(admin, false),
139        ],
140    }
141}
142
143pub fn change_authority(
144    program_id: Pubkey,
145    contract: Pubkey,
146    new_authority: Pubkey,
147    signature: [u8; SECP256K1_SIGNATURE_LENGTH],
148    recovery_id: u8,
149) -> Instruction {
150    let (admin, _) = Pubkey::find_program_address(&[PDA_ADMIN_SEED.as_bytes(), contract.as_ref()], &program_id);
151    let (program_data, _) = Pubkey::find_program_address(&[contract.as_ref()], &solana_program::bpf_loader_upgradeable::id());
152
153    Instruction{
154        program_id,
155        data: UpgradeInstruction::ChangeAuthority(
156            ChangeAuthorityArgs {
157                signature,
158                recovery_id,
159            }
160        ).try_to_vec().unwrap(),
161        accounts: vec![
162            AccountMeta::new(admin, false),
163            AccountMeta::new(program_data, false),
164            AccountMeta::new(new_authority, false),
165            AccountMeta::new(solana_program::bpf_loader_upgradeable::id(), false),
166        ],
167    }
168}
169
170pub fn upgrade(
171    program_id: Pubkey,
172    contract: Pubkey,
173    buffer: Pubkey,
174    spill: Pubkey,
175    signature: [u8; SECP256K1_SIGNATURE_LENGTH],
176    recovery_id: u8,
177) -> Instruction {
178    let (admin, _) = Pubkey::find_program_address(&[PDA_ADMIN_SEED.as_bytes(), contract.as_ref()], &program_id);
179    let (program_data, _) = Pubkey::find_program_address(&[contract.as_ref()], &solana_program::bpf_loader_upgradeable::id());
180
181    Instruction {
182        program_id,
183        data: UpgradeInstruction::Upgrade(
184            UpgradeArgs {
185                signature,
186                recovery_id,
187            }
188        ).try_to_vec().unwrap(),
189        accounts: vec![
190            AccountMeta::new(admin, false),
191            AccountMeta::new(program_data, false),
192            AccountMeta::new(contract, false),
193            AccountMeta::new(buffer, false),
194            AccountMeta::new(spill, false),
195            AccountMeta::new(solana_program::sysvar::rent::id(), false),
196            AccountMeta::new(solana_program::sysvar::clock::id(), false),
197            AccountMeta::new(solana_program::bpf_loader_upgradeable::id(), false),
198        ],
199    }
200}