2019-03-11 15:35:25 -07:00
|
|
|
use bincode::serialize;
|
2018-12-14 20:39:10 -08:00
|
|
|
use log::*;
|
2019-03-11 13:50:31 -07:00
|
|
|
use serde_derive::Serialize;
|
2018-12-03 13:32:31 -08:00
|
|
|
use solana_sdk::account::KeyedAccount;
|
|
|
|
use solana_sdk::native_program::ProgramError;
|
|
|
|
use solana_sdk::pubkey::Pubkey;
|
|
|
|
use solana_sdk::system_instruction::SystemInstruction;
|
|
|
|
use solana_sdk::system_program;
|
|
|
|
|
2019-03-05 11:11:05 -08:00
|
|
|
const FROM_ACCOUNT_INDEX: usize = 0;
|
|
|
|
const TO_ACCOUNT_INDEX: usize = 1;
|
|
|
|
|
2019-03-11 13:50:31 -07:00
|
|
|
#[derive(Serialize, Debug, Clone, PartialEq)]
|
2019-03-05 11:11:05 -08:00
|
|
|
enum SystemError {
|
|
|
|
AccountAlreadyInUse,
|
2019-03-05 16:28:14 -08:00
|
|
|
ResultWithNegativeLamports,
|
2019-03-05 11:11:05 -08:00
|
|
|
SourceNotSystemAccount,
|
|
|
|
}
|
|
|
|
|
|
|
|
fn create_system_account(
|
|
|
|
keyed_accounts: &mut [KeyedAccount],
|
2019-03-05 16:28:14 -08:00
|
|
|
lamports: u64,
|
2019-03-05 11:11:05 -08:00
|
|
|
space: u64,
|
2019-03-09 19:28:43 -08:00
|
|
|
program_id: &Pubkey,
|
2019-03-05 11:11:05 -08:00
|
|
|
) -> Result<(), SystemError> {
|
|
|
|
if !system_program::check_id(&keyed_accounts[FROM_ACCOUNT_INDEX].account.owner) {
|
|
|
|
info!("CreateAccount: invalid account[from] owner");
|
|
|
|
Err(SystemError::SourceNotSystemAccount)?;
|
|
|
|
}
|
|
|
|
|
|
|
|
if !keyed_accounts[TO_ACCOUNT_INDEX].account.userdata.is_empty()
|
|
|
|
|| !system_program::check_id(&keyed_accounts[TO_ACCOUNT_INDEX].account.owner)
|
|
|
|
{
|
|
|
|
info!(
|
|
|
|
"CreateAccount: invalid argument; account {} already in use",
|
|
|
|
keyed_accounts[TO_ACCOUNT_INDEX].unsigned_key()
|
|
|
|
);
|
|
|
|
Err(SystemError::AccountAlreadyInUse)?;
|
|
|
|
}
|
2019-03-05 16:28:14 -08:00
|
|
|
if lamports > keyed_accounts[FROM_ACCOUNT_INDEX].account.lamports {
|
2019-03-05 11:11:05 -08:00
|
|
|
info!(
|
2019-03-05 16:28:14 -08:00
|
|
|
"CreateAccount: insufficient lamports ({}, need {})",
|
|
|
|
keyed_accounts[FROM_ACCOUNT_INDEX].account.lamports, lamports
|
2019-03-05 11:11:05 -08:00
|
|
|
);
|
2019-03-05 16:28:14 -08:00
|
|
|
Err(SystemError::ResultWithNegativeLamports)?;
|
2019-03-05 11:11:05 -08:00
|
|
|
}
|
2019-03-05 16:28:14 -08:00
|
|
|
keyed_accounts[FROM_ACCOUNT_INDEX].account.lamports -= lamports;
|
|
|
|
keyed_accounts[TO_ACCOUNT_INDEX].account.lamports += lamports;
|
2019-03-09 19:28:43 -08:00
|
|
|
keyed_accounts[TO_ACCOUNT_INDEX].account.owner = *program_id;
|
2019-03-05 11:11:05 -08:00
|
|
|
keyed_accounts[TO_ACCOUNT_INDEX].account.userdata = vec![0; space as usize];
|
|
|
|
keyed_accounts[TO_ACCOUNT_INDEX].account.executable = false;
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
fn assign_account_to_program(
|
|
|
|
keyed_accounts: &mut [KeyedAccount],
|
2019-03-09 19:28:43 -08:00
|
|
|
program_id: &Pubkey,
|
2019-03-05 11:11:05 -08:00
|
|
|
) -> Result<(), ProgramError> {
|
|
|
|
if !system_program::check_id(&keyed_accounts[FROM_ACCOUNT_INDEX].account.owner) {
|
|
|
|
Err(ProgramError::AssignOfUnownedAccount)?;
|
|
|
|
}
|
2019-03-09 19:28:43 -08:00
|
|
|
keyed_accounts[FROM_ACCOUNT_INDEX].account.owner = *program_id;
|
2019-03-05 11:11:05 -08:00
|
|
|
Ok(())
|
|
|
|
}
|
2019-03-05 16:28:14 -08:00
|
|
|
fn move_lamports(keyed_accounts: &mut [KeyedAccount], lamports: u64) -> Result<(), ProgramError> {
|
|
|
|
if lamports > keyed_accounts[FROM_ACCOUNT_INDEX].account.lamports {
|
2019-03-05 11:11:05 -08:00
|
|
|
info!(
|
2019-03-05 16:28:14 -08:00
|
|
|
"Move: insufficient lamports ({}, need {})",
|
|
|
|
keyed_accounts[FROM_ACCOUNT_INDEX].account.lamports, lamports
|
2019-03-05 11:11:05 -08:00
|
|
|
);
|
2019-03-05 16:28:14 -08:00
|
|
|
Err(ProgramError::ResultWithNegativeLamports)?;
|
2019-03-05 11:11:05 -08:00
|
|
|
}
|
2019-03-05 16:28:14 -08:00
|
|
|
keyed_accounts[FROM_ACCOUNT_INDEX].account.lamports -= lamports;
|
|
|
|
keyed_accounts[TO_ACCOUNT_INDEX].account.lamports += lamports;
|
2019-03-05 11:11:05 -08:00
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
2018-12-05 09:27:25 -08:00
|
|
|
pub fn entrypoint(
|
2018-12-03 13:32:31 -08:00
|
|
|
_program_id: &Pubkey,
|
|
|
|
keyed_accounts: &mut [KeyedAccount],
|
|
|
|
data: &[u8],
|
|
|
|
_tick_height: u64,
|
|
|
|
) -> Result<(), ProgramError> {
|
2018-12-14 12:36:50 -08:00
|
|
|
if let Ok(syscall) = bincode::deserialize(data) {
|
2018-12-03 13:32:31 -08:00
|
|
|
trace!("process_instruction: {:?}", syscall);
|
|
|
|
trace!("keyed_accounts: {:?}", keyed_accounts);
|
|
|
|
|
2019-03-05 12:11:20 -08:00
|
|
|
// All system instructions require that accounts_keys[0] be a signer
|
2019-03-05 11:11:05 -08:00
|
|
|
if keyed_accounts[FROM_ACCOUNT_INDEX].signer_key().is_none() {
|
2018-12-03 13:32:31 -08:00
|
|
|
info!("account[from] is unsigned");
|
|
|
|
Err(ProgramError::InvalidArgument)?;
|
|
|
|
}
|
|
|
|
|
|
|
|
match syscall {
|
|
|
|
SystemInstruction::CreateAccount {
|
2019-03-05 16:28:14 -08:00
|
|
|
lamports,
|
2018-12-03 13:32:31 -08:00
|
|
|
space,
|
|
|
|
program_id,
|
2019-03-09 19:28:43 -08:00
|
|
|
} => create_system_account(keyed_accounts, lamports, space, &program_id).map_err(|e| {
|
2019-03-05 16:28:14 -08:00
|
|
|
match e {
|
|
|
|
SystemError::ResultWithNegativeLamports => {
|
|
|
|
ProgramError::ResultWithNegativeLamports
|
|
|
|
}
|
2019-03-11 15:35:25 -07:00
|
|
|
e => ProgramError::CustomError(serialize(&e).unwrap()),
|
2019-03-05 16:28:14 -08:00
|
|
|
}
|
|
|
|
}),
|
2018-12-03 13:32:31 -08:00
|
|
|
SystemInstruction::Assign { program_id } => {
|
2019-03-09 19:28:43 -08:00
|
|
|
assign_account_to_program(keyed_accounts, &program_id)
|
2018-12-03 13:32:31 -08:00
|
|
|
}
|
2019-03-05 16:28:14 -08:00
|
|
|
SystemInstruction::Move { lamports } => move_lamports(keyed_accounts, lamports),
|
2018-12-03 13:32:31 -08:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
info!("Invalid transaction instruction userdata: {:?}", data);
|
2019-03-05 11:11:05 -08:00
|
|
|
Err(ProgramError::InvalidUserdata)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use super::*;
|
|
|
|
use solana_sdk::account::Account;
|
|
|
|
use solana_sdk::signature::{Keypair, KeypairUtil};
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_create_system_account() {
|
|
|
|
let new_program_owner = Pubkey::new(&[9; 32]);
|
|
|
|
let from = Keypair::new().pubkey();
|
2019-03-09 19:28:43 -08:00
|
|
|
let mut from_account = Account::new(100, 0, &system_program::id());
|
2019-03-05 13:43:05 -08:00
|
|
|
|
2019-03-05 11:11:05 -08:00
|
|
|
let to = Keypair::new().pubkey();
|
2019-03-09 19:28:43 -08:00
|
|
|
let mut to_account = Account::new(0, 0, &Pubkey::default());
|
2019-03-05 13:43:05 -08:00
|
|
|
|
2019-03-05 11:11:05 -08:00
|
|
|
let mut keyed_accounts = [
|
|
|
|
KeyedAccount::new(&from, true, &mut from_account),
|
|
|
|
KeyedAccount::new(&to, false, &mut to_account),
|
|
|
|
];
|
2019-03-09 19:28:43 -08:00
|
|
|
create_system_account(&mut keyed_accounts, 50, 2, &new_program_owner).unwrap();
|
2019-03-05 16:28:14 -08:00
|
|
|
let from_lamports = from_account.lamports;
|
|
|
|
let to_lamports = to_account.lamports;
|
2019-03-05 11:11:05 -08:00
|
|
|
let to_owner = to_account.owner;
|
|
|
|
let to_userdata = to_account.userdata.clone();
|
2019-03-05 16:28:14 -08:00
|
|
|
assert_eq!(from_lamports, 50);
|
|
|
|
assert_eq!(to_lamports, 50);
|
2019-03-05 11:11:05 -08:00
|
|
|
assert_eq!(to_owner, new_program_owner);
|
|
|
|
assert_eq!(to_userdata, [0, 0]);
|
2019-03-05 13:43:05 -08:00
|
|
|
}
|
2019-03-05 11:11:05 -08:00
|
|
|
|
2019-03-05 13:43:05 -08:00
|
|
|
#[test]
|
2019-03-05 16:28:14 -08:00
|
|
|
fn test_create_negative_lamports() {
|
|
|
|
// Attempt to create account with more lamports than remaining in from_account
|
2019-03-05 13:43:05 -08:00
|
|
|
let new_program_owner = Pubkey::new(&[9; 32]);
|
|
|
|
let from = Keypair::new().pubkey();
|
2019-03-09 19:28:43 -08:00
|
|
|
let mut from_account = Account::new(100, 0, &system_program::id());
|
2019-03-05 13:43:05 -08:00
|
|
|
|
2019-03-05 12:11:20 -08:00
|
|
|
let to = Keypair::new().pubkey();
|
2019-03-09 19:28:43 -08:00
|
|
|
let mut to_account = Account::new(0, 0, &Pubkey::default());
|
2019-03-05 11:11:05 -08:00
|
|
|
let unchanged_account = to_account.clone();
|
2019-03-05 13:43:05 -08:00
|
|
|
|
|
|
|
let mut keyed_accounts = [
|
2019-03-05 11:11:05 -08:00
|
|
|
KeyedAccount::new(&from, true, &mut from_account),
|
|
|
|
KeyedAccount::new(&to, false, &mut to_account),
|
|
|
|
];
|
2019-03-09 19:28:43 -08:00
|
|
|
let result = create_system_account(&mut keyed_accounts, 150, 2, &new_program_owner);
|
2019-03-05 16:28:14 -08:00
|
|
|
assert_eq!(result, Err(SystemError::ResultWithNegativeLamports));
|
|
|
|
let from_lamports = from_account.lamports;
|
|
|
|
assert_eq!(from_lamports, 100);
|
2019-03-05 11:11:05 -08:00
|
|
|
assert_eq!(to_account, unchanged_account);
|
2019-03-05 13:43:05 -08:00
|
|
|
}
|
2019-03-05 11:11:05 -08:00
|
|
|
|
2019-03-05 13:43:05 -08:00
|
|
|
#[test]
|
|
|
|
fn test_create_already_owned() {
|
2019-03-05 11:11:05 -08:00
|
|
|
// Attempt to create system account in account already owned by another program
|
2019-03-05 13:43:05 -08:00
|
|
|
let new_program_owner = Pubkey::new(&[9; 32]);
|
|
|
|
let from = Keypair::new().pubkey();
|
2019-03-09 19:28:43 -08:00
|
|
|
let mut from_account = Account::new(100, 0, &system_program::id());
|
2019-03-05 13:43:05 -08:00
|
|
|
|
2019-03-05 11:11:05 -08:00
|
|
|
let original_program_owner = Pubkey::new(&[5; 32]);
|
|
|
|
let owned_key = Keypair::new().pubkey();
|
2019-03-09 19:28:43 -08:00
|
|
|
let mut owned_account = Account::new(0, 0, &original_program_owner);
|
2019-03-05 11:11:05 -08:00
|
|
|
let unchanged_account = owned_account.clone();
|
2019-03-05 13:43:05 -08:00
|
|
|
|
|
|
|
let mut keyed_accounts = [
|
2019-03-05 11:11:05 -08:00
|
|
|
KeyedAccount::new(&from, true, &mut from_account),
|
|
|
|
KeyedAccount::new(&owned_key, false, &mut owned_account),
|
|
|
|
];
|
2019-03-09 19:28:43 -08:00
|
|
|
let result = create_system_account(&mut keyed_accounts, 50, 2, &new_program_owner);
|
2019-03-05 12:11:20 -08:00
|
|
|
assert_eq!(result, Err(SystemError::AccountAlreadyInUse));
|
2019-03-05 16:28:14 -08:00
|
|
|
let from_lamports = from_account.lamports;
|
|
|
|
assert_eq!(from_lamports, 100);
|
2019-03-05 11:11:05 -08:00
|
|
|
assert_eq!(owned_account, unchanged_account);
|
2019-03-05 13:43:05 -08:00
|
|
|
}
|
2019-03-05 11:11:05 -08:00
|
|
|
|
2019-03-05 13:43:05 -08:00
|
|
|
#[test]
|
|
|
|
fn test_create_userdata_populated() {
|
2019-03-05 11:11:05 -08:00
|
|
|
// Attempt to create system account in account with populated userdata
|
2019-03-05 13:43:05 -08:00
|
|
|
let new_program_owner = Pubkey::new(&[9; 32]);
|
|
|
|
let from = Keypair::new().pubkey();
|
2019-03-09 19:28:43 -08:00
|
|
|
let mut from_account = Account::new(100, 0, &system_program::id());
|
2019-03-05 13:43:05 -08:00
|
|
|
|
2019-03-05 11:11:05 -08:00
|
|
|
let populated_key = Keypair::new().pubkey();
|
|
|
|
let mut populated_account = Account {
|
2019-03-05 16:28:14 -08:00
|
|
|
lamports: 0,
|
2019-03-05 11:11:05 -08:00
|
|
|
userdata: vec![0, 1, 2, 3],
|
|
|
|
owner: Pubkey::default(),
|
|
|
|
executable: false,
|
|
|
|
};
|
|
|
|
let unchanged_account = populated_account.clone();
|
2019-03-05 13:43:05 -08:00
|
|
|
|
|
|
|
let mut keyed_accounts = [
|
2019-03-05 11:11:05 -08:00
|
|
|
KeyedAccount::new(&from, true, &mut from_account),
|
|
|
|
KeyedAccount::new(&populated_key, false, &mut populated_account),
|
|
|
|
];
|
2019-03-09 19:28:43 -08:00
|
|
|
let result = create_system_account(&mut keyed_accounts, 50, 2, &new_program_owner);
|
2019-03-05 12:11:20 -08:00
|
|
|
assert_eq!(result, Err(SystemError::AccountAlreadyInUse));
|
2019-03-05 16:28:14 -08:00
|
|
|
assert_eq!(from_account.lamports, 100);
|
2019-03-05 11:11:05 -08:00
|
|
|
assert_eq!(populated_account, unchanged_account);
|
|
|
|
}
|
|
|
|
|
2019-03-05 12:11:20 -08:00
|
|
|
#[test]
|
|
|
|
fn test_create_not_system_account() {
|
|
|
|
let other_program = Pubkey::new(&[9; 32]);
|
|
|
|
|
|
|
|
let from = Keypair::new().pubkey();
|
2019-03-09 19:28:43 -08:00
|
|
|
let mut from_account = Account::new(100, 0, &other_program);
|
2019-03-05 12:11:20 -08:00
|
|
|
let to = Keypair::new().pubkey();
|
2019-03-09 19:28:43 -08:00
|
|
|
let mut to_account = Account::new(0, 0, &Pubkey::default());
|
2019-03-05 12:11:20 -08:00
|
|
|
let mut keyed_accounts = [
|
|
|
|
KeyedAccount::new(&from, true, &mut from_account),
|
|
|
|
KeyedAccount::new(&to, false, &mut to_account),
|
|
|
|
];
|
2019-03-09 19:28:43 -08:00
|
|
|
let result = create_system_account(&mut keyed_accounts, 50, 2, &other_program);
|
2019-03-05 12:11:20 -08:00
|
|
|
assert_eq!(result, Err(SystemError::SourceNotSystemAccount));
|
|
|
|
}
|
|
|
|
|
2019-03-05 11:11:05 -08:00
|
|
|
#[test]
|
|
|
|
fn test_assign_account_to_program() {
|
|
|
|
let new_program_owner = Pubkey::new(&[9; 32]);
|
|
|
|
|
|
|
|
let from = Keypair::new().pubkey();
|
2019-03-09 19:28:43 -08:00
|
|
|
let mut from_account = Account::new(100, 0, &system_program::id());
|
2019-03-05 11:11:05 -08:00
|
|
|
let mut keyed_accounts = [KeyedAccount::new(&from, true, &mut from_account)];
|
2019-03-09 19:28:43 -08:00
|
|
|
assign_account_to_program(&mut keyed_accounts, &new_program_owner).unwrap();
|
2019-03-05 11:11:05 -08:00
|
|
|
let from_owner = from_account.owner;
|
|
|
|
assert_eq!(from_owner, new_program_owner);
|
|
|
|
|
|
|
|
// Attempt to assign account not owned by system program
|
|
|
|
let another_program_owner = Pubkey::new(&[8; 32]);
|
|
|
|
keyed_accounts = [KeyedAccount::new(&from, true, &mut from_account)];
|
2019-03-09 19:28:43 -08:00
|
|
|
let result = assign_account_to_program(&mut keyed_accounts, &another_program_owner);
|
2019-03-05 12:11:20 -08:00
|
|
|
assert_eq!(result, Err(ProgramError::AssignOfUnownedAccount));
|
2019-03-05 11:11:05 -08:00
|
|
|
assert_eq!(from_account.owner, new_program_owner);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
2019-03-05 16:28:14 -08:00
|
|
|
fn test_move_lamports() {
|
2019-03-05 11:11:05 -08:00
|
|
|
let from = Keypair::new().pubkey();
|
2019-03-09 19:28:43 -08:00
|
|
|
let mut from_account = Account::new(100, 0, &Pubkey::new(&[2; 32])); // account owner should not matter
|
2019-03-05 11:11:05 -08:00
|
|
|
let to = Keypair::new().pubkey();
|
2019-03-09 19:28:43 -08:00
|
|
|
let mut to_account = Account::new(1, 0, &Pubkey::new(&[3; 32])); // account owner should not matter
|
2019-03-05 11:11:05 -08:00
|
|
|
let mut keyed_accounts = [
|
|
|
|
KeyedAccount::new(&from, true, &mut from_account),
|
|
|
|
KeyedAccount::new(&to, false, &mut to_account),
|
|
|
|
];
|
2019-03-05 16:28:14 -08:00
|
|
|
move_lamports(&mut keyed_accounts, 50).unwrap();
|
|
|
|
let from_lamports = from_account.lamports;
|
|
|
|
let to_lamports = to_account.lamports;
|
|
|
|
assert_eq!(from_lamports, 50);
|
|
|
|
assert_eq!(to_lamports, 51);
|
2019-03-05 11:11:05 -08:00
|
|
|
|
2019-03-05 16:28:14 -08:00
|
|
|
// Attempt to move more lamports than remaining in from_account
|
2019-03-05 11:11:05 -08:00
|
|
|
keyed_accounts = [
|
|
|
|
KeyedAccount::new(&from, true, &mut from_account),
|
|
|
|
KeyedAccount::new(&to, false, &mut to_account),
|
|
|
|
];
|
2019-03-05 16:28:14 -08:00
|
|
|
let result = move_lamports(&mut keyed_accounts, 100);
|
|
|
|
assert_eq!(result, Err(ProgramError::ResultWithNegativeLamports));
|
|
|
|
assert_eq!(from_account.lamports, 50);
|
|
|
|
assert_eq!(to_account.lamports, 51);
|
2018-12-03 13:32:31 -08:00
|
|
|
}
|
|
|
|
}
|