Files
solana/src/system_program.rs

307 lines
10 KiB
Rust
Raw Normal View History

2018-09-19 17:25:57 -07:00
//! system program
use bincode::deserialize;
use solana_sdk::account::Account;
use solana_sdk::pubkey::Pubkey;
2018-11-16 08:04:46 -08:00
use solana_sdk::system_instruction::SystemInstruction;
use std;
use transaction::Transaction;
#[derive(Debug)]
pub enum Error {
InvalidArgument,
AssignOfUnownedAccount,
AccountNotFinalized,
ResultWithNegativeTokens(u8),
}
impl std::fmt::Display for Error {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
write!(f, "error")
}
}
impl std::error::Error for Error {}
pub type Result<T> = std::result::Result<T, Error>;
2018-11-16 08:04:46 -08:00
pub struct SystemProgram {}
2018-09-19 17:25:57 -07:00
pub const SYSTEM_PROGRAM_ID: [u8; 32] = [0u8; 32];
2018-09-19 17:25:57 -07:00
impl SystemProgram {
pub fn check_id(program_id: &Pubkey) -> bool {
program_id.as_ref() == SYSTEM_PROGRAM_ID
}
pub fn id() -> Pubkey {
2018-09-19 17:25:57 -07:00
Pubkey::new(&SYSTEM_PROGRAM_ID)
}
pub fn get_balance(account: &Account) -> u64 {
account.tokens
}
pub fn process_transaction(
tx: &Transaction,
pix: usize,
accounts: &mut [&mut Account],
) -> Result<()> {
if let Ok(syscall) = deserialize(tx.userdata(pix)) {
2018-09-20 13:54:42 -07:00
trace!("process_transaction: {:?}", syscall);
match syscall {
2018-11-16 08:04:46 -08:00
SystemInstruction::CreateAccount {
2018-09-20 13:54:42 -07:00
tokens,
space,
2018-09-19 17:25:57 -07:00
program_id,
2018-09-20 13:54:42 -07:00
} => {
if !Self::check_id(&accounts[0].owner) {
info!("Invalid account[0] owner");
Err(Error::InvalidArgument)?;
2018-09-20 13:54:42 -07:00
}
2018-09-20 13:54:42 -07:00
if space > 0
&& (!accounts[1].userdata.is_empty() || !Self::check_id(&accounts[1].owner))
2018-09-20 13:54:42 -07:00
{
info!("Invalid account[1]");
Err(Error::InvalidArgument)?;
2018-09-20 13:54:42 -07:00
}
if tokens > accounts[0].tokens {
info!("Insufficient tokens in account[0]");
Err(Error::ResultWithNegativeTokens(pix as u8))?;
}
2018-09-20 13:54:42 -07:00
accounts[0].tokens -= tokens;
accounts[1].tokens += tokens;
accounts[1].owner = program_id;
2018-09-20 13:54:42 -07:00
accounts[1].userdata = vec![0; space as usize];
accounts[1].executable = false;
accounts[1].loader = Pubkey::default();
}
2018-11-16 08:04:46 -08:00
SystemInstruction::Assign { program_id } => {
if !Self::check_id(&accounts[0].owner) {
Err(Error::AssignOfUnownedAccount)?;
2018-09-20 13:54:42 -07:00
}
accounts[0].owner = program_id;
}
2018-11-16 08:04:46 -08:00
SystemInstruction::Move { tokens } => {
2018-09-20 13:54:42 -07:00
//bank should be verifying correctness
if tokens > accounts[0].tokens {
info!("Insufficient tokens in account[0]");
Err(Error::ResultWithNegativeTokens(pix as u8))?;
}
2018-09-20 13:54:42 -07:00
accounts[0].tokens -= tokens;
accounts[1].tokens += tokens;
2018-09-20 20:17:37 -06:00
}
2018-11-16 08:04:46 -08:00
SystemInstruction::Spawn => {
if !accounts[0].executable || accounts[0].loader != Pubkey::default() {
Err(Error::AccountNotFinalized)?;
}
accounts[0].loader = accounts[0].owner;
accounts[0].owner = tx.account_keys[0];
}
}
Ok(())
2018-09-20 13:54:42 -07:00
} else {
info!("Invalid transaction userdata: {:?}", tx.userdata(pix));
Err(Error::InvalidArgument)
}
}
}
2018-09-17 14:29:20 -07:00
#[cfg(test)]
mod test {
use super::*;
2018-09-26 17:55:36 -06:00
use signature::{Keypair, KeypairUtil};
use solana_sdk::account::Account;
2018-11-16 08:04:46 -08:00
use solana_sdk::hash::Hash;
use solana_sdk::pubkey::Pubkey;
2018-09-19 17:25:57 -07:00
use system_program::SystemProgram;
use system_transaction::SystemTransaction;
use transaction::Transaction;
fn process_transaction(tx: &Transaction, accounts: &mut [Account]) -> Result<()> {
let mut refs: Vec<&mut Account> = accounts.iter_mut().collect();
SystemProgram::process_transaction(&tx, 0, &mut refs[..])
}
2018-09-17 14:29:20 -07:00
#[test]
fn test_create_noop() {
let from = Keypair::new();
let to = Keypair::new();
let mut accounts = vec![Account::default(), Account::default()];
let tx = Transaction::system_new(&from, to.pubkey(), 0, Hash::default());
process_transaction(&tx, &mut accounts).unwrap();
2018-09-17 14:29:20 -07:00
assert_eq!(accounts[0].tokens, 0);
assert_eq!(accounts[1].tokens, 0);
}
#[test]
fn test_create_spend() {
let from = Keypair::new();
let to = Keypair::new();
let mut accounts = vec![Account::default(), Account::default()];
accounts[0].tokens = 1;
let tx = Transaction::system_new(&from, to.pubkey(), 1, Hash::default());
process_transaction(&tx, &mut accounts).unwrap();
2018-09-17 14:29:20 -07:00
assert_eq!(accounts[0].tokens, 0);
assert_eq!(accounts[1].tokens, 1);
}
2018-09-17 14:29:20 -07:00
#[test]
fn test_create_spend_wrong_source() {
let from = Keypair::new();
let to = Keypair::new();
let mut accounts = vec![Account::default(), Account::default()];
accounts[0].tokens = 1;
accounts[0].owner = from.pubkey();
2018-09-17 14:29:20 -07:00
let tx = Transaction::system_new(&from, to.pubkey(), 1, Hash::default());
if let Ok(()) = process_transaction(&tx, &mut accounts) {
panic!("Account not owned by SystemProgram");
}
2018-09-17 14:29:20 -07:00
assert_eq!(accounts[0].tokens, 1);
assert_eq!(accounts[1].tokens, 0);
}
#[test]
fn test_create_assign_and_allocate() {
let from = Keypair::new();
let to = Keypair::new();
let mut accounts = vec![Account::default(), Account::default()];
2018-09-20 13:38:28 -06:00
let tx =
Transaction::system_create(&from, to.pubkey(), Hash::default(), 0, 1, to.pubkey(), 0);
process_transaction(&tx, &mut accounts).unwrap();
2018-09-17 14:29:20 -07:00
assert!(accounts[0].userdata.is_empty());
assert_eq!(accounts[1].userdata.len(), 1);
assert_eq!(accounts[1].owner, to.pubkey());
2018-09-17 14:29:20 -07:00
}
#[test]
2018-09-19 17:25:57 -07:00
fn test_create_allocate_wrong_dest_program() {
2018-09-17 14:29:20 -07:00
let from = Keypair::new();
let to = Keypair::new();
let mut accounts = vec![Account::default(), Account::default()];
accounts[1].owner = to.pubkey();
2018-09-20 13:38:28 -06:00
let tx = Transaction::system_create(
&from,
to.pubkey(),
Hash::default(),
0,
1,
Pubkey::default(),
0,
);
assert!(process_transaction(&tx, &mut accounts).is_err());
2018-09-17 14:29:20 -07:00
assert!(accounts[1].userdata.is_empty());
}
#[test]
2018-09-19 17:25:57 -07:00
fn test_create_allocate_wrong_source_program() {
2018-09-17 14:29:20 -07:00
let from = Keypair::new();
let to = Keypair::new();
let mut accounts = vec![Account::default(), Account::default()];
accounts[0].owner = to.pubkey();
2018-09-20 13:38:28 -06:00
let tx = Transaction::system_create(
&from,
to.pubkey(),
Hash::default(),
0,
1,
Pubkey::default(),
0,
);
assert!(process_transaction(&tx, &mut accounts).is_err());
2018-09-17 14:29:20 -07:00
assert!(accounts[1].userdata.is_empty());
}
#[test]
fn test_create_allocate_already_allocated() {
let from = Keypair::new();
let to = Keypair::new();
let mut accounts = vec![Account::default(), Account::default()];
accounts[1].userdata = vec![0, 0, 0];
2018-09-20 13:38:28 -06:00
let tx = Transaction::system_create(
&from,
to.pubkey(),
Hash::default(),
0,
2,
Pubkey::default(),
0,
);
assert!(process_transaction(&tx, &mut accounts).is_err());
2018-09-17 14:29:20 -07:00
assert_eq!(accounts[1].userdata.len(), 3);
}
#[test]
fn test_create_assign() {
let from = Keypair::new();
2018-09-19 17:25:57 -07:00
let program = Keypair::new();
2018-09-17 14:29:20 -07:00
let mut accounts = vec![Account::default()];
2018-09-19 17:25:57 -07:00
let tx = Transaction::system_assign(&from, Hash::default(), program.pubkey(), 0);
process_transaction(&tx, &mut accounts).unwrap();
assert_eq!(accounts[0].owner, program.pubkey());
2018-09-17 14:29:20 -07:00
}
#[test]
fn test_move() {
let from = Keypair::new();
let to = Keypair::new();
let mut accounts = vec![Account::default(), Account::default()];
accounts[0].tokens = 1;
let tx = Transaction::system_new(&from, to.pubkey(), 1, Hash::default());
process_transaction(&tx, &mut accounts).unwrap();
2018-09-17 14:29:20 -07:00
assert_eq!(accounts[0].tokens, 0);
assert_eq!(accounts[1].tokens, 1);
}
2018-09-19 17:25:57 -07:00
/// Detect binary changes in the serialized program userdata, which could have a downstream
/// affect on SDKs and DApps
#[test]
fn test_sdk_serialize() {
let keypair = Keypair::new();
2018-11-23 12:12:56 -07:00
use budget_program::BudgetProgram;
// CreateAccount
let tx = Transaction::system_create(
&keypair,
keypair.pubkey(),
Hash::default(),
111,
222,
2018-11-23 12:12:56 -07:00
BudgetProgram::id(),
0,
);
assert_eq!(
tx.userdata(0).to_vec(),
vec![
0, 0, 0, 0, 111, 0, 0, 0, 0, 0, 0, 0, 222, 0, 0, 0, 0, 0, 0, 0, 129, 0, 0, 0, 0, 0,
2018-09-20 13:38:28 -06:00
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
]
);
// CreateAccount
let tx = Transaction::system_create(
&keypair,
keypair.pubkey(),
Hash::default(),
111,
222,
2018-09-20 13:38:28 -06:00
Pubkey::default(),
0,
);
assert_eq!(
tx.userdata(0).to_vec(),
2018-09-20 13:38:28 -06:00
vec![
0, 0, 0, 0, 111, 0, 0, 0, 0, 0, 0, 0, 222, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
]
);
// Assign
2018-11-23 12:12:56 -07:00
let tx = Transaction::system_assign(&keypair, Hash::default(), BudgetProgram::id(), 0);
assert_eq!(
tx.userdata(0).to_vec(),
vec![
1, 0, 0, 0, 129, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0
]
);
// Move
let tx = Transaction::system_move(&keypair, keypair.pubkey(), 123, Hash::default(), 0);
assert_eq!(
tx.userdata(0).to_vec(),
vec![2, 0, 0, 0, 123, 0, 0, 0, 0, 0, 0, 0]
);
}
2018-09-17 14:29:20 -07:00
}