1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
use log::*;
use solana_sdk::account::KeyedAccount;
use solana_sdk::native_program::ProgramError;
use solana_sdk::pubkey::Pubkey;
use solana_sdk::solana_entrypoint;
use solana_sdk::system_instruction::SystemInstruction;
use solana_sdk::system_program;

solana_entrypoint!(entrypoint);
pub fn entrypoint(
    _program_id: &Pubkey,
    keyed_accounts: &mut [KeyedAccount],
    data: &[u8],
    _tick_height: u64,
) -> Result<(), ProgramError> {
    if let Ok(syscall) = bincode::deserialize(data) {
        trace!("process_instruction: {:?}", syscall);
        trace!("keyed_accounts: {:?}", keyed_accounts);
        let from = 0;

        // all system instructions require that accounts_keys[0] be a signer
        if keyed_accounts[from].signer_key().is_none() {
            info!("account[from] is unsigned");
            Err(ProgramError::InvalidArgument)?;
        }

        match syscall {
            SystemInstruction::CreateAccount {
                tokens,
                space,
                program_id,
            } => {
                let to = 1;
                if !system_program::check_id(&keyed_accounts[from].account.owner) {
                    info!("CreateAccount: invalid account[from] owner");
                    Err(ProgramError::InvalidArgument)?;
                }

                if space > 0
                    && (!keyed_accounts[to].account.userdata.is_empty()
                        || !system_program::check_id(&keyed_accounts[to].account.owner))
                {
                    info!(
                        "CreateAccount: invalid argument space: {} accounts.userdata.len(): {}",
                        space,
                        keyed_accounts[to].account.userdata.len(),
                    );
                    Err(ProgramError::InvalidArgument)?;
                }
                if tokens > keyed_accounts[from].account.tokens {
                    info!(
                        "CreateAccount: insufficient tokens ({}, need {})",
                        keyed_accounts[from].account.tokens, tokens
                    );
                    Err(ProgramError::ResultWithNegativeTokens)?;
                }
                keyed_accounts[from].account.tokens -= tokens;
                keyed_accounts[to].account.tokens += tokens;
                keyed_accounts[to].account.owner = program_id;
                keyed_accounts[to].account.userdata = vec![0; space as usize];
                keyed_accounts[to].account.executable = false;
                keyed_accounts[to].account.loader = Pubkey::default();
            }
            SystemInstruction::Assign { program_id } => {
                if !system_program::check_id(&keyed_accounts[from].account.owner) {
                    Err(ProgramError::AssignOfUnownedAccount)?;
                }
                keyed_accounts[from].account.owner = program_id;
            }
            SystemInstruction::Move { tokens } => {
                let to = 1;

                // bank should be verifying correctness
                if tokens > keyed_accounts[from].account.tokens {
                    info!(
                        "Move: insufficient tokens ({}, need {})",
                        keyed_accounts[from].account.tokens, tokens
                    );
                    Err(ProgramError::ResultWithNegativeTokens)?;
                }
                keyed_accounts[from].account.tokens -= tokens;
                keyed_accounts[to].account.tokens += tokens;
            }
            SystemInstruction::Spawn => {
                if !keyed_accounts[from].account.executable
                    || keyed_accounts[from].account.loader != Pubkey::default()
                {
                    Err(ProgramError::AccountNotFinalized)?;
                }
                keyed_accounts[from].account.loader = keyed_accounts[from].account.owner;
                keyed_accounts[from].account.owner = *keyed_accounts[from].signer_key().unwrap();
            }
        }
        Ok(())
    } else {
        info!("Invalid transaction instruction userdata: {:?}", data);
        Err(ProgramError::InvalidArgument)
    }
}