spl_token/
processor.rs

1//! Program state processor
2
3use {
4    crate::{
5        amount_to_ui_amount_string_trimmed,
6        error::TokenError,
7        instruction::{is_valid_signer_index, AuthorityType, TokenInstruction, MAX_SIGNERS},
8        state::{Account, AccountState, Mint, Multisig},
9        try_ui_amount_into_amount,
10    },
11    solana_account_info::{next_account_info, AccountInfo},
12    solana_cpi::set_return_data,
13    solana_msg::msg,
14    solana_program_error::{ProgramError, ProgramResult},
15    solana_program_memory::sol_memcmp,
16    solana_program_option::COption,
17    solana_program_pack::{IsInitialized, Pack},
18    solana_pubkey::{Pubkey, PUBKEY_BYTES},
19    solana_rent::Rent,
20    solana_sdk_ids::system_program,
21    solana_sysvar::Sysvar,
22};
23
24/// Program state handler.
25pub struct Processor {}
26impl Processor {
27    fn _process_initialize_mint(
28        accounts: &[AccountInfo],
29        decimals: u8,
30        mint_authority: Pubkey,
31        freeze_authority: COption<Pubkey>,
32        rent_sysvar_account: bool,
33    ) -> ProgramResult {
34        let account_info_iter = &mut accounts.iter();
35        let mint_info = next_account_info(account_info_iter)?;
36        let mint_data_len = mint_info.data_len();
37        let rent = if rent_sysvar_account {
38            Rent::from_account_info(next_account_info(account_info_iter)?)?
39        } else {
40            Rent::get()?
41        };
42
43        let mut mint = Mint::unpack_unchecked(&mint_info.data.borrow())?;
44        if mint.is_initialized {
45            return Err(TokenError::AlreadyInUse.into());
46        }
47
48        if !rent.is_exempt(mint_info.lamports(), mint_data_len) {
49            return Err(TokenError::NotRentExempt.into());
50        }
51
52        mint.mint_authority = COption::Some(mint_authority);
53        mint.decimals = decimals;
54        mint.is_initialized = true;
55        mint.freeze_authority = freeze_authority;
56
57        Mint::pack(mint, &mut mint_info.data.borrow_mut())?;
58
59        Ok(())
60    }
61
62    /// Processes an [`InitializeMint`](enum.TokenInstruction.html) instruction.
63    pub fn process_initialize_mint(
64        accounts: &[AccountInfo],
65        decimals: u8,
66        mint_authority: Pubkey,
67        freeze_authority: COption<Pubkey>,
68    ) -> ProgramResult {
69        Self::_process_initialize_mint(accounts, decimals, mint_authority, freeze_authority, true)
70    }
71
72    /// Processes an [`InitializeMint2`](enum.TokenInstruction.html)
73    /// instruction.
74    pub fn process_initialize_mint2(
75        accounts: &[AccountInfo],
76        decimals: u8,
77        mint_authority: Pubkey,
78        freeze_authority: COption<Pubkey>,
79    ) -> ProgramResult {
80        Self::_process_initialize_mint(accounts, decimals, mint_authority, freeze_authority, false)
81    }
82
83    fn _process_initialize_account(
84        program_id: &Pubkey,
85        accounts: &[AccountInfo],
86        owner: Option<&Pubkey>,
87        rent_sysvar_account: bool,
88    ) -> ProgramResult {
89        let account_info_iter = &mut accounts.iter();
90        let new_account_info = next_account_info(account_info_iter)?;
91        let mint_info = next_account_info(account_info_iter)?;
92        let owner = if let Some(owner) = owner {
93            owner
94        } else {
95            next_account_info(account_info_iter)?.key
96        };
97        let new_account_info_data_len = new_account_info.data_len();
98        let rent = if rent_sysvar_account {
99            Rent::from_account_info(next_account_info(account_info_iter)?)?
100        } else {
101            Rent::get()?
102        };
103
104        let mut account = Account::unpack_unchecked(&new_account_info.data.borrow())?;
105        if account.is_initialized() {
106            return Err(TokenError::AlreadyInUse.into());
107        }
108
109        if !rent.is_exempt(new_account_info.lamports(), new_account_info_data_len) {
110            return Err(TokenError::NotRentExempt.into());
111        }
112
113        let is_native_mint = Self::cmp_pubkeys(mint_info.key, &crate::native_mint::id());
114        if !is_native_mint {
115            Self::check_account_owner(program_id, mint_info)?;
116            let _ = Mint::unpack(&mint_info.data.borrow_mut())
117                .map_err(|_| Into::<ProgramError>::into(TokenError::InvalidMint))?;
118        }
119
120        account.mint = *mint_info.key;
121        account.owner = *owner;
122        account.close_authority = COption::None;
123        account.delegate = COption::None;
124        account.delegated_amount = 0;
125        account.state = AccountState::Initialized;
126        if is_native_mint {
127            let rent_exempt_reserve = rent.minimum_balance(new_account_info_data_len);
128            account.is_native = COption::Some(rent_exempt_reserve);
129            account.amount = new_account_info
130                .lamports()
131                .checked_sub(rent_exempt_reserve)
132                .ok_or(TokenError::Overflow)?;
133        } else {
134            account.is_native = COption::None;
135            account.amount = 0;
136        };
137
138        Account::pack(account, &mut new_account_info.data.borrow_mut())?;
139
140        Ok(())
141    }
142
143    /// Processes an [`InitializeAccount`](enum.TokenInstruction.html)
144    /// instruction.
145    pub fn process_initialize_account(
146        program_id: &Pubkey,
147        accounts: &[AccountInfo],
148    ) -> ProgramResult {
149        Self::_process_initialize_account(program_id, accounts, None, true)
150    }
151
152    /// Processes an [`InitializeAccount2`](enum.TokenInstruction.html)
153    /// instruction.
154    pub fn process_initialize_account2(
155        program_id: &Pubkey,
156        accounts: &[AccountInfo],
157        owner: Pubkey,
158    ) -> ProgramResult {
159        Self::_process_initialize_account(program_id, accounts, Some(&owner), true)
160    }
161
162    /// Processes an [`InitializeAccount3`](enum.TokenInstruction.html)
163    /// instruction.
164    pub fn process_initialize_account3(
165        program_id: &Pubkey,
166        accounts: &[AccountInfo],
167        owner: Pubkey,
168    ) -> ProgramResult {
169        Self::_process_initialize_account(program_id, accounts, Some(&owner), false)
170    }
171
172    fn _process_initialize_multisig(
173        accounts: &[AccountInfo],
174        m: u8,
175        rent_sysvar_account: bool,
176    ) -> ProgramResult {
177        let account_info_iter = &mut accounts.iter();
178        let multisig_info = next_account_info(account_info_iter)?;
179        let multisig_info_data_len = multisig_info.data_len();
180        let rent = if rent_sysvar_account {
181            Rent::from_account_info(next_account_info(account_info_iter)?)?
182        } else {
183            Rent::get()?
184        };
185
186        let mut multisig = Multisig::unpack_unchecked(&multisig_info.data.borrow())?;
187        if multisig.is_initialized {
188            return Err(TokenError::AlreadyInUse.into());
189        }
190
191        if !rent.is_exempt(multisig_info.lamports(), multisig_info_data_len) {
192            return Err(TokenError::NotRentExempt.into());
193        }
194
195        let signer_infos = account_info_iter.as_slice();
196        multisig.m = m;
197        multisig.n = signer_infos.len() as u8;
198        if !is_valid_signer_index(multisig.n as usize) {
199            return Err(TokenError::InvalidNumberOfProvidedSigners.into());
200        }
201        if !is_valid_signer_index(multisig.m as usize) {
202            return Err(TokenError::InvalidNumberOfRequiredSigners.into());
203        }
204        for (i, signer_info) in signer_infos.iter().enumerate() {
205            multisig.signers[i] = *signer_info.key;
206        }
207        multisig.is_initialized = true;
208
209        Multisig::pack(multisig, &mut multisig_info.data.borrow_mut())?;
210
211        Ok(())
212    }
213
214    /// Processes a [`InitializeMultisig`](enum.TokenInstruction.html)
215    /// instruction.
216    pub fn process_initialize_multisig(accounts: &[AccountInfo], m: u8) -> ProgramResult {
217        Self::_process_initialize_multisig(accounts, m, true)
218    }
219
220    /// Processes a [`InitializeMultisig2`](enum.TokenInstruction.html)
221    /// instruction.
222    pub fn process_initialize_multisig2(accounts: &[AccountInfo], m: u8) -> ProgramResult {
223        Self::_process_initialize_multisig(accounts, m, false)
224    }
225
226    /// Processes a [`Transfer`](enum.TokenInstruction.html) instruction.
227    pub fn process_transfer(
228        program_id: &Pubkey,
229        accounts: &[AccountInfo],
230        amount: u64,
231        expected_decimals: Option<u8>,
232    ) -> ProgramResult {
233        let account_info_iter = &mut accounts.iter();
234
235        let source_account_info = next_account_info(account_info_iter)?;
236
237        let expected_mint_info = if let Some(expected_decimals) = expected_decimals {
238            Some((next_account_info(account_info_iter)?, expected_decimals))
239        } else {
240            None
241        };
242
243        let destination_account_info = next_account_info(account_info_iter)?;
244        let authority_info = next_account_info(account_info_iter)?;
245
246        let mut source_account = Account::unpack(&source_account_info.data.borrow())?;
247        let mut destination_account = Account::unpack(&destination_account_info.data.borrow())?;
248
249        if source_account.is_frozen() || destination_account.is_frozen() {
250            return Err(TokenError::AccountFrozen.into());
251        }
252        if source_account.amount < amount {
253            return Err(TokenError::InsufficientFunds.into());
254        }
255        if !Self::cmp_pubkeys(&source_account.mint, &destination_account.mint) {
256            return Err(TokenError::MintMismatch.into());
257        }
258
259        if let Some((mint_info, expected_decimals)) = expected_mint_info {
260            if !Self::cmp_pubkeys(mint_info.key, &source_account.mint) {
261                return Err(TokenError::MintMismatch.into());
262            }
263
264            let mint = Mint::unpack(&mint_info.data.borrow_mut())?;
265            if expected_decimals != mint.decimals {
266                return Err(TokenError::MintDecimalsMismatch.into());
267            }
268        }
269
270        let self_transfer =
271            Self::cmp_pubkeys(source_account_info.key, destination_account_info.key);
272
273        match source_account.delegate {
274            COption::Some(ref delegate) if Self::cmp_pubkeys(authority_info.key, delegate) => {
275                Self::validate_owner(
276                    program_id,
277                    delegate,
278                    authority_info,
279                    account_info_iter.as_slice(),
280                )?;
281                if source_account.delegated_amount < amount {
282                    return Err(TokenError::InsufficientFunds.into());
283                }
284                if !self_transfer {
285                    source_account.delegated_amount = source_account
286                        .delegated_amount
287                        .checked_sub(amount)
288                        .ok_or(TokenError::Overflow)?;
289                    if source_account.delegated_amount == 0 {
290                        source_account.delegate = COption::None;
291                    }
292                }
293            }
294            _ => Self::validate_owner(
295                program_id,
296                &source_account.owner,
297                authority_info,
298                account_info_iter.as_slice(),
299            )?,
300        };
301
302        if self_transfer || amount == 0 {
303            Self::check_account_owner(program_id, source_account_info)?;
304            Self::check_account_owner(program_id, destination_account_info)?;
305        }
306
307        // This check MUST occur just before the amounts are manipulated
308        // to ensure self-transfers are fully validated
309        if self_transfer {
310            return Ok(());
311        }
312
313        source_account.amount = source_account
314            .amount
315            .checked_sub(amount)
316            .ok_or(TokenError::Overflow)?;
317        destination_account.amount = destination_account
318            .amount
319            .checked_add(amount)
320            .ok_or(TokenError::Overflow)?;
321
322        if source_account.is_native() {
323            let source_starting_lamports = source_account_info.lamports();
324            **source_account_info.lamports.borrow_mut() = source_starting_lamports
325                .checked_sub(amount)
326                .ok_or(TokenError::Overflow)?;
327
328            let destination_starting_lamports = destination_account_info.lamports();
329            **destination_account_info.lamports.borrow_mut() = destination_starting_lamports
330                .checked_add(amount)
331                .ok_or(TokenError::Overflow)?;
332        }
333
334        Account::pack(source_account, &mut source_account_info.data.borrow_mut())?;
335        Account::pack(
336            destination_account,
337            &mut destination_account_info.data.borrow_mut(),
338        )?;
339
340        Ok(())
341    }
342
343    /// Processes an [`Approve`](enum.TokenInstruction.html) instruction.
344    pub fn process_approve(
345        program_id: &Pubkey,
346        accounts: &[AccountInfo],
347        amount: u64,
348        expected_decimals: Option<u8>,
349    ) -> ProgramResult {
350        let account_info_iter = &mut accounts.iter();
351
352        let source_account_info = next_account_info(account_info_iter)?;
353
354        let expected_mint_info = if let Some(expected_decimals) = expected_decimals {
355            Some((next_account_info(account_info_iter)?, expected_decimals))
356        } else {
357            None
358        };
359        let delegate_info = next_account_info(account_info_iter)?;
360        let owner_info = next_account_info(account_info_iter)?;
361
362        let mut source_account = Account::unpack(&source_account_info.data.borrow())?;
363
364        if source_account.is_frozen() {
365            return Err(TokenError::AccountFrozen.into());
366        }
367
368        if let Some((mint_info, expected_decimals)) = expected_mint_info {
369            if !Self::cmp_pubkeys(mint_info.key, &source_account.mint) {
370                return Err(TokenError::MintMismatch.into());
371            }
372
373            let mint = Mint::unpack(&mint_info.data.borrow_mut())?;
374            if expected_decimals != mint.decimals {
375                return Err(TokenError::MintDecimalsMismatch.into());
376            }
377        }
378
379        Self::validate_owner(
380            program_id,
381            &source_account.owner,
382            owner_info,
383            account_info_iter.as_slice(),
384        )?;
385
386        source_account.delegate = COption::Some(*delegate_info.key);
387        source_account.delegated_amount = amount;
388
389        Account::pack(source_account, &mut source_account_info.data.borrow_mut())?;
390
391        Ok(())
392    }
393
394    /// Processes an [`Revoke`](enum.TokenInstruction.html) instruction.
395    pub fn process_revoke(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
396        let account_info_iter = &mut accounts.iter();
397        let source_account_info = next_account_info(account_info_iter)?;
398
399        let mut source_account = Account::unpack(&source_account_info.data.borrow())?;
400
401        let owner_info = next_account_info(account_info_iter)?;
402
403        if source_account.is_frozen() {
404            return Err(TokenError::AccountFrozen.into());
405        }
406
407        Self::validate_owner(
408            program_id,
409            &source_account.owner,
410            owner_info,
411            account_info_iter.as_slice(),
412        )?;
413
414        source_account.delegate = COption::None;
415        source_account.delegated_amount = 0;
416
417        Account::pack(source_account, &mut source_account_info.data.borrow_mut())?;
418
419        Ok(())
420    }
421
422    /// Processes a [`SetAuthority`](enum.TokenInstruction.html) instruction.
423    pub fn process_set_authority(
424        program_id: &Pubkey,
425        accounts: &[AccountInfo],
426        authority_type: AuthorityType,
427        new_authority: COption<Pubkey>,
428    ) -> ProgramResult {
429        let account_info_iter = &mut accounts.iter();
430        let account_info = next_account_info(account_info_iter)?;
431        let authority_info = next_account_info(account_info_iter)?;
432
433        if account_info.data_len() == Account::get_packed_len() {
434            let mut account = Account::unpack(&account_info.data.borrow())?;
435
436            if account.is_frozen() {
437                return Err(TokenError::AccountFrozen.into());
438            }
439
440            match authority_type {
441                AuthorityType::AccountOwner => {
442                    Self::validate_owner(
443                        program_id,
444                        &account.owner,
445                        authority_info,
446                        account_info_iter.as_slice(),
447                    )?;
448
449                    if let COption::Some(authority) = new_authority {
450                        account.owner = authority;
451                    } else {
452                        return Err(TokenError::InvalidInstruction.into());
453                    }
454
455                    account.delegate = COption::None;
456                    account.delegated_amount = 0;
457
458                    if account.is_native() {
459                        account.close_authority = COption::None;
460                    }
461                }
462                AuthorityType::CloseAccount => {
463                    let authority = account.close_authority.unwrap_or(account.owner);
464                    Self::validate_owner(
465                        program_id,
466                        &authority,
467                        authority_info,
468                        account_info_iter.as_slice(),
469                    )?;
470                    account.close_authority = new_authority;
471                }
472                _ => {
473                    return Err(TokenError::AuthorityTypeNotSupported.into());
474                }
475            }
476            Account::pack(account, &mut account_info.data.borrow_mut())?;
477        } else if account_info.data_len() == Mint::get_packed_len() {
478            let mut mint = Mint::unpack(&account_info.data.borrow())?;
479            match authority_type {
480                AuthorityType::MintTokens => {
481                    // Once a mint's supply is fixed, it cannot be undone by setting a new
482                    // mint_authority
483                    let mint_authority = mint
484                        .mint_authority
485                        .ok_or(Into::<ProgramError>::into(TokenError::FixedSupply))?;
486                    Self::validate_owner(
487                        program_id,
488                        &mint_authority,
489                        authority_info,
490                        account_info_iter.as_slice(),
491                    )?;
492                    mint.mint_authority = new_authority;
493                }
494                AuthorityType::FreezeAccount => {
495                    // Once a mint's freeze authority is disabled, it cannot be re-enabled by
496                    // setting a new freeze_authority
497                    let freeze_authority = mint
498                        .freeze_authority
499                        .ok_or(Into::<ProgramError>::into(TokenError::MintCannotFreeze))?;
500                    Self::validate_owner(
501                        program_id,
502                        &freeze_authority,
503                        authority_info,
504                        account_info_iter.as_slice(),
505                    )?;
506                    mint.freeze_authority = new_authority;
507                }
508                _ => {
509                    return Err(TokenError::AuthorityTypeNotSupported.into());
510                }
511            }
512            Mint::pack(mint, &mut account_info.data.borrow_mut())?;
513        } else {
514            return Err(ProgramError::InvalidArgument);
515        }
516
517        Ok(())
518    }
519
520    /// Processes a [`MintTo`](enum.TokenInstruction.html) instruction.
521    pub fn process_mint_to(
522        program_id: &Pubkey,
523        accounts: &[AccountInfo],
524        amount: u64,
525        expected_decimals: Option<u8>,
526    ) -> ProgramResult {
527        let account_info_iter = &mut accounts.iter();
528        let mint_info = next_account_info(account_info_iter)?;
529        let destination_account_info = next_account_info(account_info_iter)?;
530        let owner_info = next_account_info(account_info_iter)?;
531
532        let mut destination_account = Account::unpack(&destination_account_info.data.borrow())?;
533        if destination_account.is_frozen() {
534            return Err(TokenError::AccountFrozen.into());
535        }
536
537        if destination_account.is_native() {
538            return Err(TokenError::NativeNotSupported.into());
539        }
540        if !Self::cmp_pubkeys(mint_info.key, &destination_account.mint) {
541            return Err(TokenError::MintMismatch.into());
542        }
543
544        let mut mint = Mint::unpack(&mint_info.data.borrow())?;
545        if let Some(expected_decimals) = expected_decimals {
546            if expected_decimals != mint.decimals {
547                return Err(TokenError::MintDecimalsMismatch.into());
548            }
549        }
550
551        match mint.mint_authority {
552            COption::Some(mint_authority) => Self::validate_owner(
553                program_id,
554                &mint_authority,
555                owner_info,
556                account_info_iter.as_slice(),
557            )?,
558            COption::None => return Err(TokenError::FixedSupply.into()),
559        }
560
561        if amount == 0 {
562            Self::check_account_owner(program_id, mint_info)?;
563            Self::check_account_owner(program_id, destination_account_info)?;
564        }
565
566        destination_account.amount = destination_account
567            .amount
568            .checked_add(amount)
569            .ok_or(TokenError::Overflow)?;
570
571        mint.supply = mint
572            .supply
573            .checked_add(amount)
574            .ok_or(TokenError::Overflow)?;
575
576        Account::pack(
577            destination_account,
578            &mut destination_account_info.data.borrow_mut(),
579        )?;
580        Mint::pack(mint, &mut mint_info.data.borrow_mut())?;
581
582        Ok(())
583    }
584
585    /// Processes a [`Burn`](enum.TokenInstruction.html) instruction.
586    pub fn process_burn(
587        program_id: &Pubkey,
588        accounts: &[AccountInfo],
589        amount: u64,
590        expected_decimals: Option<u8>,
591    ) -> ProgramResult {
592        let account_info_iter = &mut accounts.iter();
593
594        let source_account_info = next_account_info(account_info_iter)?;
595        let mint_info = next_account_info(account_info_iter)?;
596        let authority_info = next_account_info(account_info_iter)?;
597
598        let mut source_account = Account::unpack(&source_account_info.data.borrow())?;
599        let mut mint = Mint::unpack(&mint_info.data.borrow())?;
600
601        if source_account.is_frozen() {
602            return Err(TokenError::AccountFrozen.into());
603        }
604        if source_account.is_native() {
605            return Err(TokenError::NativeNotSupported.into());
606        }
607        if source_account.amount < amount {
608            return Err(TokenError::InsufficientFunds.into());
609        }
610        if !Self::cmp_pubkeys(mint_info.key, &source_account.mint) {
611            return Err(TokenError::MintMismatch.into());
612        }
613
614        if let Some(expected_decimals) = expected_decimals {
615            if expected_decimals != mint.decimals {
616                return Err(TokenError::MintDecimalsMismatch.into());
617            }
618        }
619
620        if !source_account.is_owned_by_system_program_or_incinerator() {
621            match source_account.delegate {
622                COption::Some(ref delegate) if Self::cmp_pubkeys(authority_info.key, delegate) => {
623                    Self::validate_owner(
624                        program_id,
625                        delegate,
626                        authority_info,
627                        account_info_iter.as_slice(),
628                    )?;
629
630                    if source_account.delegated_amount < amount {
631                        return Err(TokenError::InsufficientFunds.into());
632                    }
633                    source_account.delegated_amount = source_account
634                        .delegated_amount
635                        .checked_sub(amount)
636                        .ok_or(TokenError::Overflow)?;
637                    if source_account.delegated_amount == 0 {
638                        source_account.delegate = COption::None;
639                    }
640                }
641                _ => Self::validate_owner(
642                    program_id,
643                    &source_account.owner,
644                    authority_info,
645                    account_info_iter.as_slice(),
646                )?,
647            }
648        }
649
650        if amount == 0 {
651            Self::check_account_owner(program_id, source_account_info)?;
652            Self::check_account_owner(program_id, mint_info)?;
653        }
654
655        source_account.amount = source_account
656            .amount
657            .checked_sub(amount)
658            .ok_or(TokenError::Overflow)?;
659        mint.supply = mint
660            .supply
661            .checked_sub(amount)
662            .ok_or(TokenError::Overflow)?;
663
664        Account::pack(source_account, &mut source_account_info.data.borrow_mut())?;
665        Mint::pack(mint, &mut mint_info.data.borrow_mut())?;
666
667        Ok(())
668    }
669
670    /// Processes a [`CloseAccount`](enum.TokenInstruction.html) instruction.
671    pub fn process_close_account(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
672        let account_info_iter = &mut accounts.iter();
673        let source_account_info = next_account_info(account_info_iter)?;
674        let destination_account_info = next_account_info(account_info_iter)?;
675        let authority_info = next_account_info(account_info_iter)?;
676
677        if Self::cmp_pubkeys(source_account_info.key, destination_account_info.key) {
678            return Err(ProgramError::InvalidAccountData);
679        }
680
681        let source_account = Account::unpack(&source_account_info.data.borrow())?;
682        if !source_account.is_native() && source_account.amount != 0 {
683            return Err(TokenError::NonNativeHasBalance.into());
684        }
685
686        let authority = source_account
687            .close_authority
688            .unwrap_or(source_account.owner);
689        if !source_account.is_owned_by_system_program_or_incinerator() {
690            Self::validate_owner(
691                program_id,
692                &authority,
693                authority_info,
694                account_info_iter.as_slice(),
695            )?;
696        } else if !solana_sdk_ids::incinerator::check_id(destination_account_info.key) {
697            return Err(ProgramError::InvalidAccountData);
698        }
699
700        let destination_starting_lamports = destination_account_info.lamports();
701        **destination_account_info.lamports.borrow_mut() = destination_starting_lamports
702            .checked_add(source_account_info.lamports())
703            .ok_or(TokenError::Overflow)?;
704
705        **source_account_info.lamports.borrow_mut() = 0;
706        delete_account(source_account_info)?;
707
708        Ok(())
709    }
710
711    /// Processes a [`FreezeAccount`](enum.TokenInstruction.html) or a
712    /// [`ThawAccount`](enum.TokenInstruction.html) instruction.
713    pub fn process_toggle_freeze_account(
714        program_id: &Pubkey,
715        accounts: &[AccountInfo],
716        freeze: bool,
717    ) -> ProgramResult {
718        let account_info_iter = &mut accounts.iter();
719        let source_account_info = next_account_info(account_info_iter)?;
720        let mint_info = next_account_info(account_info_iter)?;
721        let authority_info = next_account_info(account_info_iter)?;
722
723        let mut source_account = Account::unpack(&source_account_info.data.borrow())?;
724        if freeze && source_account.is_frozen() || !freeze && !source_account.is_frozen() {
725            return Err(TokenError::InvalidState.into());
726        }
727        if source_account.is_native() {
728            return Err(TokenError::NativeNotSupported.into());
729        }
730        if !Self::cmp_pubkeys(mint_info.key, &source_account.mint) {
731            return Err(TokenError::MintMismatch.into());
732        }
733
734        let mint = Mint::unpack(&mint_info.data.borrow_mut())?;
735        match mint.freeze_authority {
736            COption::Some(authority) => Self::validate_owner(
737                program_id,
738                &authority,
739                authority_info,
740                account_info_iter.as_slice(),
741            ),
742            COption::None => Err(TokenError::MintCannotFreeze.into()),
743        }?;
744
745        source_account.state = if freeze {
746            AccountState::Frozen
747        } else {
748            AccountState::Initialized
749        };
750
751        Account::pack(source_account, &mut source_account_info.data.borrow_mut())?;
752
753        Ok(())
754    }
755
756    /// Processes a [`SyncNative`](enum.TokenInstruction.html) instruction
757    pub fn process_sync_native(program_id: &Pubkey, accounts: &[AccountInfo]) -> ProgramResult {
758        let account_info_iter = &mut accounts.iter();
759        let native_account_info = next_account_info(account_info_iter)?;
760        Self::check_account_owner(program_id, native_account_info)?;
761
762        let mut native_account = Account::unpack(&native_account_info.data.borrow())?;
763
764        if let COption::Some(rent_exempt_reserve) = native_account.is_native {
765            let new_amount = native_account_info
766                .lamports()
767                .checked_sub(rent_exempt_reserve)
768                .ok_or(TokenError::Overflow)?;
769            if new_amount < native_account.amount {
770                return Err(TokenError::InvalidState.into());
771            }
772            native_account.amount = new_amount;
773        } else {
774            return Err(TokenError::NonNativeNotSupported.into());
775        }
776
777        Account::pack(native_account, &mut native_account_info.data.borrow_mut())?;
778        Ok(())
779    }
780
781    /// Processes a [`GetAccountDataSize`](enum.TokenInstruction.html)
782    /// instruction
783    pub fn process_get_account_data_size(
784        program_id: &Pubkey,
785        accounts: &[AccountInfo],
786    ) -> ProgramResult {
787        let account_info_iter = &mut accounts.iter();
788        // make sure the mint is valid
789        let mint_info = next_account_info(account_info_iter)?;
790        Self::check_account_owner(program_id, mint_info)?;
791        let _ = Mint::unpack(&mint_info.data.borrow())
792            .map_err(|_| Into::<ProgramError>::into(TokenError::InvalidMint))?;
793        set_return_data(&Account::LEN.to_le_bytes());
794        Ok(())
795    }
796
797    /// Processes an [`InitializeImmutableOwner`](enum.TokenInstruction.html)
798    /// instruction
799    pub fn process_initialize_immutable_owner(accounts: &[AccountInfo]) -> ProgramResult {
800        let account_info_iter = &mut accounts.iter();
801        let token_account_info = next_account_info(account_info_iter)?;
802        let account = Account::unpack_unchecked(&token_account_info.data.borrow())?;
803        if account.is_initialized() {
804            return Err(TokenError::AlreadyInUse.into());
805        }
806        msg!("Please upgrade to SPL Token 2022 for immutable owner support");
807        Ok(())
808    }
809
810    /// Processes an [`AmountToUiAmount`](enum.TokenInstruction.html)
811    /// instruction
812    pub fn process_amount_to_ui_amount(
813        program_id: &Pubkey,
814        accounts: &[AccountInfo],
815        amount: u64,
816    ) -> ProgramResult {
817        let account_info_iter = &mut accounts.iter();
818        let mint_info = next_account_info(account_info_iter)?;
819        Self::check_account_owner(program_id, mint_info)?;
820
821        let mint = Mint::unpack(&mint_info.data.borrow_mut())
822            .map_err(|_| Into::<ProgramError>::into(TokenError::InvalidMint))?;
823        let ui_amount = amount_to_ui_amount_string_trimmed(amount, mint.decimals);
824
825        set_return_data(&ui_amount.into_bytes());
826        Ok(())
827    }
828
829    /// Processes an [`AmountToUiAmount`](enum.TokenInstruction.html)
830    /// instruction
831    pub fn process_ui_amount_to_amount(
832        program_id: &Pubkey,
833        accounts: &[AccountInfo],
834        ui_amount: &str,
835    ) -> ProgramResult {
836        let account_info_iter = &mut accounts.iter();
837        let mint_info = next_account_info(account_info_iter)?;
838        Self::check_account_owner(program_id, mint_info)?;
839
840        let mint = Mint::unpack(&mint_info.data.borrow_mut())
841            .map_err(|_| Into::<ProgramError>::into(TokenError::InvalidMint))?;
842        let amount = try_ui_amount_into_amount(ui_amount.to_string(), mint.decimals)?;
843
844        set_return_data(&amount.to_le_bytes());
845        Ok(())
846    }
847
848    /// Processes an [`Instruction`](enum.Instruction.html).
849    pub fn process(program_id: &Pubkey, accounts: &[AccountInfo], input: &[u8]) -> ProgramResult {
850        let instruction = TokenInstruction::unpack(input)?;
851
852        match instruction {
853            TokenInstruction::InitializeMint {
854                decimals,
855                mint_authority,
856                freeze_authority,
857            } => {
858                msg!("Instruction: InitializeMint");
859                Self::process_initialize_mint(accounts, decimals, mint_authority, freeze_authority)
860            }
861            TokenInstruction::InitializeMint2 {
862                decimals,
863                mint_authority,
864                freeze_authority,
865            } => {
866                msg!("Instruction: InitializeMint2");
867                Self::process_initialize_mint2(accounts, decimals, mint_authority, freeze_authority)
868            }
869            TokenInstruction::InitializeAccount => {
870                msg!("Instruction: InitializeAccount");
871                Self::process_initialize_account(program_id, accounts)
872            }
873            TokenInstruction::InitializeAccount2 { owner } => {
874                msg!("Instruction: InitializeAccount2");
875                Self::process_initialize_account2(program_id, accounts, owner)
876            }
877            TokenInstruction::InitializeAccount3 { owner } => {
878                msg!("Instruction: InitializeAccount3");
879                Self::process_initialize_account3(program_id, accounts, owner)
880            }
881            TokenInstruction::InitializeMultisig { m } => {
882                msg!("Instruction: InitializeMultisig");
883                Self::process_initialize_multisig(accounts, m)
884            }
885            TokenInstruction::InitializeMultisig2 { m } => {
886                msg!("Instruction: InitializeMultisig2");
887                Self::process_initialize_multisig2(accounts, m)
888            }
889            TokenInstruction::Transfer { amount } => {
890                msg!("Instruction: Transfer");
891                Self::process_transfer(program_id, accounts, amount, None)
892            }
893            TokenInstruction::Approve { amount } => {
894                msg!("Instruction: Approve");
895                Self::process_approve(program_id, accounts, amount, None)
896            }
897            TokenInstruction::Revoke => {
898                msg!("Instruction: Revoke");
899                Self::process_revoke(program_id, accounts)
900            }
901            TokenInstruction::SetAuthority {
902                authority_type,
903                new_authority,
904            } => {
905                msg!("Instruction: SetAuthority");
906                Self::process_set_authority(program_id, accounts, authority_type, new_authority)
907            }
908            TokenInstruction::MintTo { amount } => {
909                msg!("Instruction: MintTo");
910                Self::process_mint_to(program_id, accounts, amount, None)
911            }
912            TokenInstruction::Burn { amount } => {
913                msg!("Instruction: Burn");
914                Self::process_burn(program_id, accounts, amount, None)
915            }
916            TokenInstruction::CloseAccount => {
917                msg!("Instruction: CloseAccount");
918                Self::process_close_account(program_id, accounts)
919            }
920            TokenInstruction::FreezeAccount => {
921                msg!("Instruction: FreezeAccount");
922                Self::process_toggle_freeze_account(program_id, accounts, true)
923            }
924            TokenInstruction::ThawAccount => {
925                msg!("Instruction: ThawAccount");
926                Self::process_toggle_freeze_account(program_id, accounts, false)
927            }
928            TokenInstruction::TransferChecked { amount, decimals } => {
929                msg!("Instruction: TransferChecked");
930                Self::process_transfer(program_id, accounts, amount, Some(decimals))
931            }
932            TokenInstruction::ApproveChecked { amount, decimals } => {
933                msg!("Instruction: ApproveChecked");
934                Self::process_approve(program_id, accounts, amount, Some(decimals))
935            }
936            TokenInstruction::MintToChecked { amount, decimals } => {
937                msg!("Instruction: MintToChecked");
938                Self::process_mint_to(program_id, accounts, amount, Some(decimals))
939            }
940            TokenInstruction::BurnChecked { amount, decimals } => {
941                msg!("Instruction: BurnChecked");
942                Self::process_burn(program_id, accounts, amount, Some(decimals))
943            }
944            TokenInstruction::SyncNative => {
945                msg!("Instruction: SyncNative");
946                Self::process_sync_native(program_id, accounts)
947            }
948            TokenInstruction::GetAccountDataSize => {
949                msg!("Instruction: GetAccountDataSize");
950                Self::process_get_account_data_size(program_id, accounts)
951            }
952            TokenInstruction::InitializeImmutableOwner => {
953                msg!("Instruction: InitializeImmutableOwner");
954                Self::process_initialize_immutable_owner(accounts)
955            }
956            TokenInstruction::AmountToUiAmount { amount } => {
957                msg!("Instruction: AmountToUiAmount");
958                Self::process_amount_to_ui_amount(program_id, accounts, amount)
959            }
960            TokenInstruction::UiAmountToAmount { ui_amount } => {
961                msg!("Instruction: UiAmountToAmount");
962                Self::process_ui_amount_to_amount(program_id, accounts, ui_amount)
963            }
964        }
965    }
966
967    /// Checks that the account is owned by the expected program
968    pub fn check_account_owner(program_id: &Pubkey, account_info: &AccountInfo) -> ProgramResult {
969        if !Self::cmp_pubkeys(program_id, account_info.owner) {
970            Err(ProgramError::IncorrectProgramId)
971        } else {
972            Ok(())
973        }
974    }
975
976    /// Checks two pubkeys for equality in a computationally cheap way using
977    /// `sol_memcmp`
978    pub fn cmp_pubkeys(a: &Pubkey, b: &Pubkey) -> bool {
979        sol_memcmp(a.as_ref(), b.as_ref(), PUBKEY_BYTES) == 0
980    }
981
982    /// Validates owner(s) are present
983    pub fn validate_owner(
984        program_id: &Pubkey,
985        expected_owner: &Pubkey,
986        owner_account_info: &AccountInfo,
987        signers: &[AccountInfo],
988    ) -> ProgramResult {
989        if !Self::cmp_pubkeys(expected_owner, owner_account_info.key) {
990            return Err(TokenError::OwnerMismatch.into());
991        }
992        if Self::cmp_pubkeys(program_id, owner_account_info.owner)
993            && owner_account_info.data_len() == Multisig::get_packed_len()
994        {
995            let multisig = Multisig::unpack(&owner_account_info.data.borrow())?;
996            let mut num_signers = 0;
997            let mut matched = [false; MAX_SIGNERS];
998            for signer in signers.iter() {
999                for (position, key) in multisig.signers[0..multisig.n as usize].iter().enumerate() {
1000                    if Self::cmp_pubkeys(key, signer.key) && !matched[position] {
1001                        if !signer.is_signer {
1002                            return Err(ProgramError::MissingRequiredSignature);
1003                        }
1004                        matched[position] = true;
1005                        num_signers += 1;
1006                    }
1007                }
1008            }
1009            if num_signers < multisig.m {
1010                return Err(ProgramError::MissingRequiredSignature);
1011            }
1012            return Ok(());
1013        } else if !owner_account_info.is_signer {
1014            return Err(ProgramError::MissingRequiredSignature);
1015        }
1016        Ok(())
1017    }
1018}
1019
1020/// Helper function to mostly delete an account in a test environment.  We could
1021/// potentially muck around the bytes assuming that a vec is passed in, but that
1022/// would be more trouble than it's worth.
1023#[cfg(not(target_os = "solana"))]
1024fn delete_account(account_info: &AccountInfo) -> Result<(), ProgramError> {
1025    account_info.assign(&system_program::id());
1026    let mut account_data = account_info.data.borrow_mut();
1027    let data_len = account_data.len();
1028    solana_program_memory::sol_memset(*account_data, 0, data_len);
1029    Ok(())
1030}
1031
1032/// Helper function to totally delete an account on-chain
1033#[cfg(target_os = "solana")]
1034fn delete_account(account_info: &AccountInfo) -> Result<(), ProgramError> {
1035    account_info.assign(&system_program::id());
1036    account_info.realloc(0, false)
1037}
1038
1039#[cfg(test)]
1040mod tests {
1041    use {
1042        super::*,
1043        solana_clock::Epoch,
1044        solana_program_error::PrintProgramError,
1045        std::sync::{Arc, RwLock},
1046    };
1047
1048    lazy_static::lazy_static! {
1049        static ref EXPECTED_DATA: Arc<RwLock<Vec<u8>>> = Arc::new(RwLock::new(Vec::new()));
1050    }
1051
1052    fn return_token_error_as_program_error() -> ProgramError {
1053        TokenError::MintMismatch.into()
1054    }
1055
1056    #[test]
1057    fn test_print_error() {
1058        let error = return_token_error_as_program_error();
1059        error.print::<TokenError>();
1060    }
1061
1062    #[test]
1063    fn test_error_as_custom() {
1064        assert_eq!(
1065            return_token_error_as_program_error(),
1066            ProgramError::Custom(3)
1067        );
1068    }
1069
1070    #[test]
1071    fn test_unique_account_sizes() {
1072        assert_ne!(Mint::get_packed_len(), 0);
1073        assert_ne!(Mint::get_packed_len(), Account::get_packed_len());
1074        assert_ne!(Mint::get_packed_len(), Multisig::get_packed_len());
1075        assert_ne!(Account::get_packed_len(), 0);
1076        assert_ne!(Account::get_packed_len(), Multisig::get_packed_len());
1077        assert_ne!(Multisig::get_packed_len(), 0);
1078    }
1079
1080    #[test]
1081    fn test_pack_unpack() {
1082        // Mint
1083        let check = Mint {
1084            mint_authority: COption::Some(Pubkey::new_from_array([1; 32])),
1085            supply: 42,
1086            decimals: 7,
1087            is_initialized: true,
1088            freeze_authority: COption::Some(Pubkey::new_from_array([2; 32])),
1089        };
1090        let mut packed = vec![0; Mint::get_packed_len() + 1];
1091        assert_eq!(
1092            Err(ProgramError::InvalidAccountData),
1093            Mint::pack(check, &mut packed)
1094        );
1095        let mut packed = vec![0; Mint::get_packed_len() - 1];
1096        assert_eq!(
1097            Err(ProgramError::InvalidAccountData),
1098            Mint::pack(check, &mut packed)
1099        );
1100        let mut packed = vec![0; Mint::get_packed_len()];
1101        Mint::pack(check, &mut packed).unwrap();
1102        let expect = vec![
1103            1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1104            1, 1, 1, 1, 1, 1, 1, 42, 0, 0, 0, 0, 0, 0, 0, 7, 1, 1, 0, 0, 0, 2, 2, 2, 2, 2, 2, 2, 2,
1105            2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
1106        ];
1107        assert_eq!(packed, expect);
1108        let unpacked = Mint::unpack(&packed).unwrap();
1109        assert_eq!(unpacked, check);
1110
1111        // Account
1112        let check = Account {
1113            mint: Pubkey::new_from_array([1; 32]),
1114            owner: Pubkey::new_from_array([2; 32]),
1115            amount: 3,
1116            delegate: COption::Some(Pubkey::new_from_array([4; 32])),
1117            state: AccountState::Frozen,
1118            is_native: COption::Some(5),
1119            delegated_amount: 6,
1120            close_authority: COption::Some(Pubkey::new_from_array([7; 32])),
1121        };
1122        let mut packed = vec![0; Account::get_packed_len() + 1];
1123        assert_eq!(
1124            Err(ProgramError::InvalidAccountData),
1125            Account::pack(check, &mut packed)
1126        );
1127        let mut packed = vec![0; Account::get_packed_len() - 1];
1128        assert_eq!(
1129            Err(ProgramError::InvalidAccountData),
1130            Account::pack(check, &mut packed)
1131        );
1132        let mut packed = vec![0; Account::get_packed_len()];
1133        Account::pack(check, &mut packed).unwrap();
1134        let expect = vec![
1135            1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
1136            1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
1137            2, 2, 2, 2, 2, 2, 3, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
1138            4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 2, 1, 0, 0, 0, 5, 0, 0,
1139            0, 0, 0, 0, 0, 6, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
1140            7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7,
1141        ];
1142        assert_eq!(packed, expect);
1143        let unpacked = Account::unpack(&packed).unwrap();
1144        assert_eq!(unpacked, check);
1145
1146        // Multisig
1147        let check = Multisig {
1148            m: 1,
1149            n: 2,
1150            is_initialized: true,
1151            signers: [Pubkey::new_from_array([3; 32]); MAX_SIGNERS],
1152        };
1153        let mut packed = vec![0; Multisig::get_packed_len() + 1];
1154        assert_eq!(
1155            Err(ProgramError::InvalidAccountData),
1156            Multisig::pack(check, &mut packed)
1157        );
1158        let mut packed = vec![0; Multisig::get_packed_len() - 1];
1159        assert_eq!(
1160            Err(ProgramError::InvalidAccountData),
1161            Multisig::pack(check, &mut packed)
1162        );
1163        let mut packed = vec![0; Multisig::get_packed_len()];
1164        Multisig::pack(check, &mut packed).unwrap();
1165        let expect = vec![
1166            1, 2, 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
1167            3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
1168            3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
1169            3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
1170            3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
1171            3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
1172            3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
1173            3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
1174            3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
1175            3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
1176            3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
1177            3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
1178            3, 3, 3, 3, 3, 3, 3,
1179        ];
1180        assert_eq!(packed, expect);
1181        let unpacked = Multisig::unpack(&packed).unwrap();
1182        assert_eq!(unpacked, check);
1183    }
1184
1185    #[test]
1186    fn test_validate_owner() {
1187        let program_id = crate::id();
1188        let owner_key = Pubkey::new_unique();
1189        let mut signer_keys = [Pubkey::default(); MAX_SIGNERS];
1190        for signer_key in signer_keys.iter_mut().take(MAX_SIGNERS) {
1191            *signer_key = Pubkey::new_unique();
1192        }
1193        let mut signer_lamports = 0;
1194        let mut signer_data = vec![];
1195        let mut signers = vec![
1196            AccountInfo::new(
1197                &owner_key,
1198                true,
1199                false,
1200                &mut signer_lamports,
1201                &mut signer_data,
1202                &program_id,
1203                false,
1204                Epoch::default(),
1205            );
1206            MAX_SIGNERS + 1
1207        ];
1208        for (signer, key) in signers.iter_mut().zip(&signer_keys) {
1209            signer.key = key;
1210        }
1211        let mut lamports = 0;
1212        let mut data = vec![0; Multisig::get_packed_len()];
1213        let mut multisig = Multisig::unpack_unchecked(&data).unwrap();
1214        multisig.m = MAX_SIGNERS as u8;
1215        multisig.n = MAX_SIGNERS as u8;
1216        multisig.signers = signer_keys;
1217        multisig.is_initialized = true;
1218        Multisig::pack(multisig, &mut data).unwrap();
1219        let owner_account_info = AccountInfo::new(
1220            &owner_key,
1221            false,
1222            false,
1223            &mut lamports,
1224            &mut data,
1225            &program_id,
1226            false,
1227            Epoch::default(),
1228        );
1229
1230        // full 11 of 11
1231        Processor::validate_owner(&program_id, &owner_key, &owner_account_info, &signers).unwrap();
1232
1233        // 1 of 11
1234        {
1235            let mut multisig =
1236                Multisig::unpack_unchecked(&owner_account_info.data.borrow()).unwrap();
1237            multisig.m = 1;
1238            Multisig::pack(multisig, &mut owner_account_info.data.borrow_mut()).unwrap();
1239        }
1240        Processor::validate_owner(&program_id, &owner_key, &owner_account_info, &signers).unwrap();
1241
1242        // 2:1
1243        {
1244            let mut multisig =
1245                Multisig::unpack_unchecked(&owner_account_info.data.borrow()).unwrap();
1246            multisig.m = 2;
1247            multisig.n = 1;
1248            Multisig::pack(multisig, &mut owner_account_info.data.borrow_mut()).unwrap();
1249        }
1250        assert_eq!(
1251            Err(ProgramError::MissingRequiredSignature),
1252            Processor::validate_owner(&program_id, &owner_key, &owner_account_info, &signers)
1253        );
1254
1255        // 0:11
1256        {
1257            let mut multisig =
1258                Multisig::unpack_unchecked(&owner_account_info.data.borrow()).unwrap();
1259            multisig.m = 0;
1260            multisig.n = 11;
1261            Multisig::pack(multisig, &mut owner_account_info.data.borrow_mut()).unwrap();
1262        }
1263        Processor::validate_owner(&program_id, &owner_key, &owner_account_info, &signers).unwrap();
1264
1265        // 2:11 but 0 provided
1266        {
1267            let mut multisig =
1268                Multisig::unpack_unchecked(&owner_account_info.data.borrow()).unwrap();
1269            multisig.m = 2;
1270            multisig.n = 11;
1271            Multisig::pack(multisig, &mut owner_account_info.data.borrow_mut()).unwrap();
1272        }
1273        assert_eq!(
1274            Err(ProgramError::MissingRequiredSignature),
1275            Processor::validate_owner(&program_id, &owner_key, &owner_account_info, &[])
1276        );
1277        // 2:11 but 1 provided
1278        {
1279            let mut multisig =
1280                Multisig::unpack_unchecked(&owner_account_info.data.borrow()).unwrap();
1281            multisig.m = 2;
1282            multisig.n = 11;
1283            Multisig::pack(multisig, &mut owner_account_info.data.borrow_mut()).unwrap();
1284        }
1285        assert_eq!(
1286            Err(ProgramError::MissingRequiredSignature),
1287            Processor::validate_owner(&program_id, &owner_key, &owner_account_info, &signers[0..1])
1288        );
1289
1290        // 2:11, 2 from middle provided
1291        {
1292            let mut multisig =
1293                Multisig::unpack_unchecked(&owner_account_info.data.borrow()).unwrap();
1294            multisig.m = 2;
1295            multisig.n = 11;
1296            Multisig::pack(multisig, &mut owner_account_info.data.borrow_mut()).unwrap();
1297        }
1298        Processor::validate_owner(&program_id, &owner_key, &owner_account_info, &signers[5..7])
1299            .unwrap();
1300
1301        // 11:11, one is not a signer
1302        {
1303            let mut multisig =
1304                Multisig::unpack_unchecked(&owner_account_info.data.borrow()).unwrap();
1305            multisig.m = 11;
1306            multisig.n = 11;
1307            Multisig::pack(multisig, &mut owner_account_info.data.borrow_mut()).unwrap();
1308        }
1309        signers[5].is_signer = false;
1310        assert_eq!(
1311            Err(ProgramError::MissingRequiredSignature),
1312            Processor::validate_owner(&program_id, &owner_key, &owner_account_info, &signers)
1313        );
1314        signers[5].is_signer = true;
1315
1316        // 11:11, single signer signs multiple times
1317        {
1318            let mut signer_lamports = 0;
1319            let mut signer_data = vec![];
1320            let signers = vec![
1321                AccountInfo::new(
1322                    &signer_keys[5],
1323                    true,
1324                    false,
1325                    &mut signer_lamports,
1326                    &mut signer_data,
1327                    &program_id,
1328                    false,
1329                    Epoch::default(),
1330                );
1331                MAX_SIGNERS + 1
1332            ];
1333            let mut multisig =
1334                Multisig::unpack_unchecked(&owner_account_info.data.borrow()).unwrap();
1335            multisig.m = 11;
1336            multisig.n = 11;
1337            Multisig::pack(multisig, &mut owner_account_info.data.borrow_mut()).unwrap();
1338            assert_eq!(
1339                Err(ProgramError::MissingRequiredSignature),
1340                Processor::validate_owner(&program_id, &owner_key, &owner_account_info, &signers)
1341            );
1342        }
1343    }
1344}