safe_memo/
processor.rs

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