spl_name_service/
processor.rs

1use {
2    crate::{
3        instruction::NameRegistryInstruction,
4        state::{get_seeds_and_key, write_data, NameRecordHeader},
5    },
6    borsh::BorshDeserialize,
7    solana_program::{
8        account_info::{next_account_info, AccountInfo},
9        entrypoint::ProgramResult,
10        msg,
11        program::{invoke, invoke_signed},
12        program_error::ProgramError,
13        program_pack::Pack,
14        pubkey::Pubkey,
15        rent::Rent,
16        system_instruction,
17        sysvar::Sysvar,
18    },
19    std::cmp::Ordering,
20};
21
22pub struct Processor {}
23
24impl Processor {
25    pub fn process_create(
26        program_id: &Pubkey,
27        accounts: &[AccountInfo],
28        hashed_name: Vec<u8>,
29        lamports: u64,
30        space: u32,
31    ) -> ProgramResult {
32        let accounts_iter = &mut accounts.iter();
33
34        let system_program = next_account_info(accounts_iter)?;
35        let payer_account = next_account_info(accounts_iter)?;
36        let name_account = next_account_info(accounts_iter)?;
37        let name_owner = next_account_info(accounts_iter)?;
38        let name_class = next_account_info(accounts_iter)?;
39        let parent_name_account = next_account_info(accounts_iter)?;
40        let parent_name_owner = next_account_info(accounts_iter).ok();
41
42        let (name_account_key, seeds) = get_seeds_and_key(
43            program_id,
44            hashed_name,
45            Some(name_class.key),
46            Some(parent_name_account.key),
47        );
48
49        // Verifications
50        if name_account_key != *name_account.key {
51            msg!("The given name account is incorrect.");
52            return Err(ProgramError::InvalidArgument);
53        }
54        if name_account.data.borrow().len() > 0 {
55            let name_record_header =
56                NameRecordHeader::unpack_from_slice(&name_account.data.borrow())?;
57            if name_record_header.owner != Pubkey::default() {
58                msg!("The given name account already exists.");
59                return Err(ProgramError::InvalidArgument);
60            }
61        }
62        if *name_class.key != Pubkey::default() && !name_class.is_signer {
63            msg!("The given name class is not a signer.");
64            return Err(ProgramError::InvalidArgument);
65        }
66        if *parent_name_account.key != Pubkey::default() {
67            if !parent_name_owner.unwrap().is_signer {
68                msg!("The given parent name account owner is not a signer.");
69                return Err(ProgramError::InvalidArgument);
70            } else {
71                let parent_name_record_header =
72                    NameRecordHeader::unpack_from_slice(&parent_name_account.data.borrow())?;
73                if &parent_name_record_header.owner != parent_name_owner.unwrap().key {
74                    msg!("The given parent name account owner is not correct.");
75                    return Err(ProgramError::InvalidArgument);
76                }
77            }
78        }
79        if name_owner.key == &Pubkey::default() {
80            msg!("The owner cannot be `Pubkey::default()`.");
81            return Err(ProgramError::InvalidArgument);
82        }
83
84        if name_account.data.borrow().len() == 0 {
85            // Issue the name registry account
86            // The creation is done in three steps: transfer, allocate and assign, because
87            // one cannot `system_instruction::create` an account to which lamports have
88            // been transferred before.
89            invoke(
90                &system_instruction::transfer(payer_account.key, &name_account_key, lamports),
91                &[
92                    payer_account.clone(),
93                    name_account.clone(),
94                    system_program.clone(),
95                ],
96            )?;
97
98            invoke_signed(
99                &system_instruction::allocate(
100                    &name_account_key,
101                    NameRecordHeader::LEN.saturating_add(space as usize) as u64,
102                ),
103                &[name_account.clone(), system_program.clone()],
104                &[&seeds.chunks(32).collect::<Vec<&[u8]>>()],
105            )?;
106
107            invoke_signed(
108                &system_instruction::assign(name_account.key, program_id),
109                &[name_account.clone(), system_program.clone()],
110                &[&seeds.chunks(32).collect::<Vec<&[u8]>>()],
111            )?;
112        }
113
114        let name_state = NameRecordHeader {
115            parent_name: *parent_name_account.key,
116            owner: *name_owner.key,
117            class: *name_class.key,
118        };
119
120        name_state.pack_into_slice(&mut name_account.data.borrow_mut());
121
122        Ok(())
123    }
124
125    pub fn process_update(accounts: &[AccountInfo], offset: u32, data: Vec<u8>) -> ProgramResult {
126        let accounts_iter = &mut accounts.iter();
127
128        let name_account = next_account_info(accounts_iter)?;
129        let name_update_signer = next_account_info(accounts_iter)?;
130        let parent_name = next_account_info(accounts_iter).ok();
131
132        let name_record_header = NameRecordHeader::unpack_from_slice(&name_account.data.borrow())?;
133
134        // Verifications
135        let is_parent_owner = if let Some(parent_name) = parent_name {
136            if name_record_header.parent_name != *parent_name.key {
137                msg!("Invalid parent name account");
138                return Err(ProgramError::InvalidArgument);
139            }
140            let parent_name_record_header =
141                NameRecordHeader::unpack_from_slice(&parent_name.data.borrow())?;
142            parent_name_record_header.owner == *name_update_signer.key
143        } else {
144            false
145        };
146        if !name_update_signer.is_signer {
147            msg!("The given name class or owner is not a signer.");
148            return Err(ProgramError::InvalidArgument);
149        }
150        if name_record_header.class != Pubkey::default()
151            && *name_update_signer.key != name_record_header.class
152        {
153            msg!("The given name class account is incorrect.");
154            return Err(ProgramError::InvalidArgument);
155        }
156        if name_record_header.class == Pubkey::default()
157            && *name_update_signer.key != name_record_header.owner
158            && !is_parent_owner
159        {
160            msg!("The given name owner account is incorrect.");
161            return Err(ProgramError::InvalidArgument);
162        }
163
164        write_data(
165            name_account,
166            &data,
167            NameRecordHeader::LEN.saturating_add(offset as usize),
168        );
169
170        Ok(())
171    }
172
173    pub fn process_transfer(accounts: &[AccountInfo], new_owner: Pubkey) -> ProgramResult {
174        let accounts_iter = &mut accounts.iter();
175
176        let name_account = next_account_info(accounts_iter)?;
177        let name_owner = next_account_info(accounts_iter)?;
178        let name_class_opt = next_account_info(accounts_iter).ok();
179        let parent_name = next_account_info(accounts_iter).ok();
180
181        let mut name_record_header =
182            NameRecordHeader::unpack_from_slice(&name_account.data.borrow())?;
183
184        // Verifications
185        let is_parent_owner = if let Some(parent_name) = parent_name {
186            if name_record_header.parent_name != *parent_name.key {
187                msg!("Invalid parent name account");
188                return Err(ProgramError::InvalidArgument);
189            }
190            let parent_name_record_header =
191                NameRecordHeader::unpack_from_slice(&parent_name.data.borrow())?;
192            parent_name_record_header.owner == *name_owner.key
193        } else {
194            false
195        };
196        if !name_owner.is_signer
197            || (name_record_header.owner != *name_owner.key && !is_parent_owner)
198        {
199            msg!("The given name owner is incorrect or not a signer.");
200            return Err(ProgramError::InvalidArgument);
201        }
202        if name_record_header.class != Pubkey::default()
203            && (name_class_opt.is_none()
204                || name_record_header.class != *name_class_opt.unwrap().key
205                || !name_class_opt.unwrap().is_signer)
206        {
207            msg!("The given name class account is incorrect or not a signer.");
208            return Err(ProgramError::InvalidArgument);
209        }
210
211        name_record_header.owner = new_owner;
212        name_record_header
213            .pack_into_slice(&mut name_account.data.borrow_mut()[..NameRecordHeader::LEN]);
214
215        Ok(())
216    }
217
218    pub fn process_delete(accounts: &[AccountInfo]) -> ProgramResult {
219        let accounts_iter = &mut accounts.iter();
220
221        let name_account = next_account_info(accounts_iter)?;
222        let name_owner = next_account_info(accounts_iter)?;
223        let refund_target = next_account_info(accounts_iter)?;
224
225        let name_record_header = NameRecordHeader::unpack_from_slice(&name_account.data.borrow())?;
226
227        // Verifications
228        if !name_owner.is_signer || name_record_header.owner != *name_owner.key {
229            msg!("The given name owner is incorrect or not a signer.");
230            return Err(ProgramError::InvalidArgument);
231        }
232
233        // Overwrite the data with zeroes
234        write_data(name_account, &vec![0; name_account.data_len()], 0);
235
236        // Close the account by transferring the rent sol
237        let source_amount: &mut u64 = &mut name_account.lamports.borrow_mut();
238        let dest_amount: &mut u64 = &mut refund_target.lamports.borrow_mut();
239        *dest_amount = dest_amount.saturating_add(*source_amount);
240        *source_amount = 0;
241
242        Ok(())
243    }
244
245    fn process_realloc(accounts: &[AccountInfo], space: u32) -> ProgramResult {
246        let accounts_iter = &mut accounts.iter();
247        let system_program = next_account_info(accounts_iter)?;
248        let payer_account = next_account_info(accounts_iter)?;
249        let name_account = next_account_info(accounts_iter)?;
250        let name_owner = next_account_info(accounts_iter)?;
251
252        let name_record_header = NameRecordHeader::unpack_from_slice(&name_account.data.borrow())?;
253
254        // Verifications
255        if !name_owner.is_signer || name_record_header.owner != *name_owner.key {
256            msg!("The given name owner is incorrect or not a signer.");
257            return Err(ProgramError::InvalidArgument);
258        }
259
260        let new_space = NameRecordHeader::LEN.saturating_add(space as usize);
261        let required_lamports = Rent::get()?.minimum_balance(new_space);
262        match name_account.lamports().cmp(&required_lamports) {
263            Ordering::Less => {
264                // Overflow cannot happen here because we already checked the sizes.
265                #[allow(clippy::arithmetic_side_effects)]
266                let lamports_to_add = required_lamports - name_account.lamports();
267                invoke(
268                    &system_instruction::transfer(
269                        payer_account.key,
270                        name_account.key,
271                        lamports_to_add,
272                    ),
273                    &[
274                        payer_account.clone(),
275                        name_account.clone(),
276                        system_program.clone(),
277                    ],
278                )?;
279            }
280            Ordering::Greater => {
281                // Overflow cannot happen here because we already checked the sizes.
282                #[allow(clippy::arithmetic_side_effects)]
283                let lamports_to_remove = name_account.lamports() - required_lamports;
284                let source_amount: &mut u64 = &mut name_account.lamports.borrow_mut();
285                let dest_amount: &mut u64 = &mut payer_account.lamports.borrow_mut();
286                *source_amount = source_amount.saturating_sub(lamports_to_remove);
287                *dest_amount = dest_amount.saturating_add(lamports_to_remove);
288            }
289            Ordering::Equal => {}
290        }
291        // Max data increase is checked in realloc. No need to check here.
292        name_account.realloc(new_space, false)?;
293        Ok(())
294    }
295
296    pub fn process_instruction(
297        program_id: &Pubkey,
298        accounts: &[AccountInfo],
299        instruction_data: &[u8],
300    ) -> ProgramResult {
301        msg!("Beginning processing");
302        let instruction = NameRegistryInstruction::try_from_slice(instruction_data)
303            .map_err(|_| ProgramError::InvalidInstructionData)?;
304        msg!("Instruction unpacked");
305
306        match instruction {
307            NameRegistryInstruction::Create {
308                hashed_name,
309                lamports,
310                space,
311            } => {
312                msg!("Instruction: Create");
313                Processor::process_create(program_id, accounts, hashed_name, lamports, space)?;
314            }
315            NameRegistryInstruction::Update { offset, data } => {
316                msg!("Instruction: Update Data");
317                Processor::process_update(accounts, offset, data)?;
318            }
319            NameRegistryInstruction::Transfer { new_owner } => {
320                msg!("Instruction: Transfer Ownership");
321                Processor::process_transfer(accounts, new_owner)?;
322            }
323            NameRegistryInstruction::Delete => {
324                msg!("Instruction: Delete Name");
325                Processor::process_delete(accounts)?;
326            }
327            NameRegistryInstruction::Realloc { space } => {
328                msg!("Instruction: Realloc Name Record");
329                Processor::process_realloc(accounts, space)?;
330            }
331        }
332        Ok(())
333    }
334}