1use crate::ownable_instruction::OwnableError;
4use bincode::serialize_into;
5use solana_sdk::{
6 account::{ReadableAccount, WritableAccount},
7 instruction::InstructionError,
8 keyed_account::{keyed_account_at_index, KeyedAccount},
9 process_instruction::InvokeContext,
10 program_utils::limited_deserialize,
11 pubkey::Pubkey,
12};
13
14fn set_owner(
15 account_owner_pubkey: &mut Pubkey,
16 new_owner_pubkey: Pubkey,
17 owner_keyed_account: &KeyedAccount,
18) -> Result<(), InstructionError> {
19 match owner_keyed_account.signer_key() {
20 None => return Err(InstructionError::MissingRequiredSignature),
21 Some(signer_key) => {
22 if account_owner_pubkey != signer_key {
23 return Err(OwnableError::IncorrectOwner.into());
24 }
25 *account_owner_pubkey = new_owner_pubkey;
26 }
27 }
28 Ok(())
29}
30
31pub fn process_instruction(
32 _program_id: &Pubkey,
33 data: &[u8],
34 invoke_context: &mut dyn InvokeContext,
35) -> Result<(), InstructionError> {
36 let keyed_accounts = invoke_context.get_keyed_accounts()?;
37
38 let new_owner_pubkey: Pubkey = limited_deserialize(data)?;
39 let account_keyed_account = &mut keyed_account_at_index(keyed_accounts, 0)?;
40 let mut account_owner_pubkey: Pubkey =
41 limited_deserialize(account_keyed_account.try_account_ref()?.data())?;
42
43 if account_owner_pubkey == Pubkey::default() {
44 account_owner_pubkey = new_owner_pubkey;
45 } else {
46 let owner_keyed_account = &mut keyed_account_at_index(keyed_accounts, 1)?;
47 set_owner(
48 &mut account_owner_pubkey,
49 new_owner_pubkey,
50 owner_keyed_account,
51 )?;
52 }
53
54 let mut account = account_keyed_account.try_account_ref_mut()?;
55 serialize_into(account.data_as_mut_slice(), &account_owner_pubkey)
56 .map_err(|_| InstructionError::AccountDataTooSmall)
57}
58
59#[cfg(test)]
60mod tests {
61 use super::*;
62 use crate::ownable_instruction;
63 use solana_runtime::{bank::Bank, bank_client::BankClient};
64 use solana_sdk::{
65 account::AccountSharedData,
66 client::SyncClient,
67 genesis_config::create_genesis_config,
68 message::Message,
69 signature::{Keypair, Signature, Signer},
70 system_program,
71 transport::Result,
72 };
73
74 fn create_bank(lamports: u64) -> (Bank, Keypair) {
75 let (genesis_config, mint_keypair) = create_genesis_config(lamports);
76 let mut bank = Bank::new(&genesis_config);
77 bank.add_builtin("ownable_program", crate::id(), process_instruction);
78 (bank, mint_keypair)
79 }
80
81 fn create_bank_client(lamports: u64) -> (BankClient, Keypair) {
82 let (bank, mint_keypair) = create_bank(lamports);
83 (BankClient::new(bank), mint_keypair)
84 }
85
86 fn create_ownable_account(
87 bank_client: &BankClient,
88 payer_keypair: &Keypair,
89 account_keypair: &Keypair,
90 owner_pubkey: &Pubkey,
91 lamports: u64,
92 ) -> Result<Signature> {
93 let instructions = ownable_instruction::create_account(
94 &payer_keypair.pubkey(),
95 &account_keypair.pubkey(),
96 owner_pubkey,
97 lamports,
98 );
99 let message = Message::new(&instructions, Some(&payer_keypair.pubkey()));
100 bank_client.send_and_confirm_message(&[payer_keypair, account_keypair], message)
101 }
102
103 fn send_set_owner(
104 bank_client: &BankClient,
105 payer_keypair: &Keypair,
106 account_pubkey: &Pubkey,
107 old_owner_keypair: &Keypair,
108 new_owner_pubkey: &Pubkey,
109 ) -> Result<Signature> {
110 let instruction = ownable_instruction::set_owner(
111 account_pubkey,
112 &old_owner_keypair.pubkey(),
113 new_owner_pubkey,
114 );
115 let message = Message::new(&[instruction], Some(&payer_keypair.pubkey()));
116 bank_client.send_and_confirm_message(&[payer_keypair, old_owner_keypair], message)
117 }
118
119 #[test]
120 fn test_ownable_set_owner() {
121 let (bank_client, payer_keypair) = create_bank_client(2);
122 let account_keypair = Keypair::new();
123 let account_pubkey = account_keypair.pubkey();
124 let owner_keypair = Keypair::new();
125 let owner_pubkey = owner_keypair.pubkey();
126
127 create_ownable_account(
128 &bank_client,
129 &payer_keypair,
130 &account_keypair,
131 &owner_pubkey,
132 1,
133 )
134 .unwrap();
135
136 let new_owner_keypair = Keypair::new();
137 let new_owner_pubkey = new_owner_keypair.pubkey();
138 send_set_owner(
139 &bank_client,
140 &payer_keypair,
141 &account_pubkey,
142 &owner_keypair,
143 &new_owner_pubkey,
144 )
145 .unwrap();
146
147 let account_data = bank_client
148 .get_account_data(&account_pubkey)
149 .unwrap()
150 .unwrap();
151 let account_owner_pubkey: Pubkey = limited_deserialize(&account_data).unwrap();
152 assert_eq!(account_owner_pubkey, new_owner_pubkey);
153 }
154
155 #[test]
156 fn test_ownable_missing_owner_signature() {
157 let mut account_owner_pubkey = solana_sdk::pubkey::new_rand();
158 let owner_pubkey = account_owner_pubkey;
159 let new_owner_pubkey = solana_sdk::pubkey::new_rand();
160 let account = AccountSharedData::new_ref(1, 0, &system_program::id());
161 let owner_keyed_account = KeyedAccount::new(&owner_pubkey, false, &account); let err = set_owner(
163 &mut account_owner_pubkey,
164 new_owner_pubkey,
165 &owner_keyed_account,
166 )
167 .unwrap_err();
168 assert_eq!(err, InstructionError::MissingRequiredSignature);
169 }
170
171 #[test]
172 fn test_ownable_incorrect_owner() {
173 let mut account_owner_pubkey = solana_sdk::pubkey::new_rand();
174 let new_owner_pubkey = solana_sdk::pubkey::new_rand();
175 let account = AccountSharedData::new_ref(1, 0, &system_program::id());
176 let mallory_pubkey = solana_sdk::pubkey::new_rand(); let owner_keyed_account = KeyedAccount::new(&mallory_pubkey, true, &account);
178 let err = set_owner(
179 &mut account_owner_pubkey,
180 new_owner_pubkey,
181 &owner_keyed_account,
182 )
183 .unwrap_err();
184 assert_eq!(err, OwnableError::IncorrectOwner.into());
185 }
186}