spl_memo/
processor.rs

1//! Program state processor
2
3use {
4    solana_account_info::AccountInfo, solana_msg::msg, solana_program_entrypoint::ProgramResult,
5    solana_program_error::ProgramError, solana_pubkey::Pubkey, std::str::from_utf8,
6};
7
8/// Instruction processor
9pub fn process_instruction(
10    _program_id: &Pubkey,
11    accounts: &[AccountInfo],
12    input: &[u8],
13) -> ProgramResult {
14    let account_info_iter = &mut accounts.iter();
15    let mut missing_required_signature = false;
16    for account_info in account_info_iter {
17        if let Some(address) = account_info.signer_key() {
18            msg!("Signed by {:?}", address);
19        } else {
20            missing_required_signature = true;
21        }
22    }
23    if missing_required_signature {
24        return Err(ProgramError::MissingRequiredSignature);
25    }
26
27    let memo = from_utf8(input).map_err(|err| {
28        msg!("Invalid UTF-8, from byte {}", err.valid_up_to());
29        ProgramError::InvalidInstructionData
30    })?;
31    msg!("Memo (len {}): {:?}", memo.len(), memo);
32
33    Ok(())
34}
35
36#[cfg(test)]
37mod tests {
38    use {
39        super::*, solana_account_info::IntoAccountInfo, solana_program_error::ProgramError,
40        solana_pubkey::Pubkey, solana_sdk::account::Account,
41    };
42
43    #[test]
44    fn test_utf8_memo() {
45        let program_id = Pubkey::new_from_array([0; 32]);
46
47        let string = b"letters and such";
48        assert_eq!(Ok(()), process_instruction(&program_id, &[], string));
49
50        let emoji = "🐆".as_bytes();
51        let bytes = [0xF0, 0x9F, 0x90, 0x86];
52        assert_eq!(emoji, bytes);
53        assert_eq!(Ok(()), process_instruction(&program_id, &[], emoji));
54
55        let mut bad_utf8 = bytes;
56        bad_utf8[3] = 0xFF; // Invalid UTF-8 byte
57        assert_eq!(
58            Err(ProgramError::InvalidInstructionData),
59            process_instruction(&program_id, &[], &bad_utf8)
60        );
61    }
62
63    #[test]
64    fn test_signers() {
65        let program_id = Pubkey::new_from_array([0; 32]);
66        let memo = "🐆".as_bytes();
67
68        let pubkey0 = Pubkey::new_unique();
69        let pubkey1 = Pubkey::new_unique();
70        let pubkey2 = Pubkey::new_unique();
71        let mut account0 = Account::default();
72        let mut account1 = Account::default();
73        let mut account2 = Account::default();
74
75        let signed_account_infos = vec![
76            (&pubkey0, true, &mut account0).into_account_info(),
77            (&pubkey1, true, &mut account1).into_account_info(),
78            (&pubkey2, true, &mut account2).into_account_info(),
79        ];
80        assert_eq!(
81            Ok(()),
82            process_instruction(&program_id, &signed_account_infos, memo)
83        );
84
85        assert_eq!(Ok(()), process_instruction(&program_id, &[], memo));
86
87        let unsigned_account_infos = vec![
88            (&pubkey0, false, &mut account0).into_account_info(),
89            (&pubkey1, false, &mut account1).into_account_info(),
90            (&pubkey2, false, &mut account2).into_account_info(),
91        ];
92        assert_eq!(
93            Err(ProgramError::MissingRequiredSignature),
94            process_instruction(&program_id, &unsigned_account_infos, memo)
95        );
96
97        let partially_signed_account_infos = vec![
98            (&pubkey0, true, &mut account0).into_account_info(),
99            (&pubkey1, false, &mut account1).into_account_info(),
100            (&pubkey2, true, &mut account2).into_account_info(),
101        ];
102        assert_eq!(
103            Err(ProgramError::MissingRequiredSignature),
104            process_instruction(&program_id, &partially_signed_account_infos, memo)
105        );
106    }
107}