diff --git a/src/bank.rs b/src/bank.rs index 195addb112..09b7ac8191 100644 --- a/src/bank.rs +++ b/src/bank.rs @@ -6,7 +6,7 @@ use bincode::deserialize; use bincode::serialize; use bpf_loader; -use budget_program::BudgetProgram; +use budget_program; use counter::Counter; use entry::Entry; use itertools::Itertools; @@ -34,13 +34,13 @@ use std::result; use std::sync::atomic::{AtomicUsize, Ordering}; use std::sync::{Arc, Mutex, RwLock}; use std::time::Instant; -use storage_program::StorageProgram; -use system_program::{Error, SystemProgram}; +use storage_program; +use system_program; use system_transaction::SystemTransaction; use token_program; use tokio::prelude::Future; use transaction::Transaction; -use vote_program::VoteProgram; +use vote_program; /// The number of most recent `last_id` values that the bank will track the signatures /// of. Once the bank discards a `last_id`, it will reject any transactions that use @@ -736,7 +736,7 @@ impl Bank { // Verify the transaction // Make sure that program_id is still the same or this was just assigned by the system call contract - if *pre_program_id != account.owner && !SystemProgram::check_id(&program_id) { + if *pre_program_id != account.owner && !system_program::check_id(&program_id) { return Err(BankError::ModifiedContractId(instruction_index as u8)); } // For accounts unassigned to the contract, the individual balance of each accounts cannot decrease. @@ -817,29 +817,33 @@ impl Bank { // Call the contract method // It's up to the contract to implement its own rules on moving funds - if SystemProgram::check_id(&program_id) { + if system_program::check_id(&program_id) { if let Err(err) = - SystemProgram::process_transaction(&tx, instruction_index, program_accounts) + system_program::process_transaction(&tx, instruction_index, program_accounts) { let err = match err { - Error::ResultWithNegativeTokens(i) => BankError::ResultWithNegativeTokens(i), + system_program::Error::ResultWithNegativeTokens(i) => { + BankError::ResultWithNegativeTokens(i) + } _ => BankError::ProgramRuntimeError(instruction_index as u8), }; return Err(err); } - } else if BudgetProgram::check_id(&program_id) { - if BudgetProgram::process_transaction(&tx, instruction_index, program_accounts).is_err() - { - return Err(BankError::ProgramRuntimeError(instruction_index as u8)); - } - } else if StorageProgram::check_id(&program_id) { - if StorageProgram::process_transaction(&tx, instruction_index, program_accounts) + } else if budget_program::check_id(&program_id) { + if budget_program::process_transaction(&tx, instruction_index, program_accounts) .is_err() { return Err(BankError::ProgramRuntimeError(instruction_index as u8)); } - } else if VoteProgram::check_id(&program_id) { - if VoteProgram::process_transaction(&tx, instruction_index, program_accounts).is_err() { + } else if storage_program::check_id(&program_id) { + if storage_program::process_transaction(&tx, instruction_index, program_accounts) + .is_err() + { + return Err(BankError::ProgramRuntimeError(instruction_index as u8)); + } + } else if vote_program::check_id(&program_id) { + if vote_program::process_transaction(&tx, instruction_index, program_accounts).is_err() + { return Err(BankError::ProgramRuntimeError(instruction_index as u8)); } } else { @@ -1233,8 +1237,8 @@ impl Bank { { // Process the first transaction let tx = &entry1.transactions[0]; - assert!(SystemProgram::check_id(tx.program_id(0)), "Invalid ledger"); - assert!(SystemProgram::check_id(tx.program_id(1)), "Invalid ledger"); + assert!(system_program::check_id(tx.program_id(0)), "Invalid ledger"); + assert!(system_program::check_id(tx.program_id(1)), "Invalid ledger"); let mut instruction: SystemInstruction = deserialize(tx.userdata(0)).unwrap(); let mint_deposit = if let SystemInstruction::Move { tokens } = instruction { Some(tokens) @@ -1309,10 +1313,10 @@ impl Bank { } pub fn read_balance(account: &Account) -> u64 { - if SystemProgram::check_id(&account.owner) { - SystemProgram::get_balance(account) - } else if BudgetProgram::check_id(&account.owner) { - BudgetProgram::get_balance(account) + if system_program::check_id(&account.owner) { + system_program::get_balance(account) + } else if budget_program::check_id(&account.owner) { + budget_program::get_balance(account) } else { account.tokens } @@ -1498,7 +1502,6 @@ impl Bank { mod tests { use super::*; use bincode::serialize; - use budget_program::BudgetProgram; use entry::next_entry; use entry::Entry; use jsonrpc_macros::pubsub::{Subscriber, SubscriptionId}; @@ -1598,7 +1601,7 @@ mod tests { &[key1, key2], mint.last_id(), 0, - vec![SystemProgram::id()], + vec![system_program::id()], instructions, ); let res = bank.process_transactions(&vec![t1.clone()]); @@ -2021,7 +2024,7 @@ mod tests { last_id, 1, 16, - BudgetProgram::id(), + budget_program::id(), 0, ); bank.process_transaction(&tx).unwrap(); @@ -2253,26 +2256,26 @@ mod tests { 0, 0, 0, 0, ]); - assert_eq!(SystemProgram::id(), system); + assert_eq!(system_program::id(), system); assert_eq!(native_loader::id(), native); assert_eq!(bpf_loader::id(), bpf); - assert_eq!(BudgetProgram::id(), budget); - assert_eq!(StorageProgram::id(), storage); + assert_eq!(budget_program::id(), budget); + assert_eq!(storage_program::id(), storage); assert_eq!(token_program::id(), token); - assert_eq!(VoteProgram::id(), vote); + assert_eq!(vote_program::id(), vote); } #[test] fn test_program_id_uniqueness() { let mut unique = HashSet::new(); let ids = vec![ - SystemProgram::id(), + system_program::id(), native_loader::id(), bpf_loader::id(), - BudgetProgram::id(), - StorageProgram::id(), + budget_program::id(), + storage_program::id(), token_program::id(), - VoteProgram::id(), + vote_program::id(), ]; assert!(ids.into_iter().all(move |id| unique.insert(id))); } diff --git a/src/budget_program.rs b/src/budget_program.rs index 312e45fe3c..e7c729efa0 100644 --- a/src/budget_program.rs +++ b/src/budget_program.rs @@ -35,17 +35,122 @@ const BUDGET_PROGRAM_ID: [u8; 32] = [ 0, ]; +pub fn id() -> Pubkey { + Pubkey::new(&BUDGET_PROGRAM_ID) +} + +pub fn check_id(program_id: &Pubkey) -> bool { + program_id.as_ref() == BUDGET_PROGRAM_ID +} + +fn apply_debits( + tx: &Transaction, + instruction_index: usize, + accounts: &mut [&mut Account], + instruction: &Instruction, +) -> Result<(), BudgetError> { + if !accounts[0].userdata.is_empty() { + trace!("source is pending"); + return Err(BudgetError::SourceIsPendingContract); + } + match instruction { + Instruction::NewBudget(expr) => { + let expr = expr.clone(); + if let Some(payment) = expr.final_payment() { + accounts[1].tokens += payment.tokens; + Ok(()) + } else { + let existing = BudgetProgram::deserialize(&accounts[1].userdata).ok(); + if Some(true) == existing.map(|x| x.initialized) { + trace!("contract already exists"); + Err(BudgetError::ContractAlreadyExists) + } else { + let mut program = BudgetProgram::default(); + program.pending_budget = Some(expr); + accounts[1].tokens += accounts[0].tokens; + accounts[0].tokens = 0; + program.initialized = true; + program.serialize(&mut accounts[1].userdata) + } + } + } + Instruction::ApplyTimestamp(dt) => { + if let Ok(mut program) = BudgetProgram::deserialize(&accounts[1].userdata) { + if !program.is_pending() { + Err(BudgetError::ContractNotPending) + } else if !program.initialized { + trace!("contract is uninitialized"); + Err(BudgetError::UninitializedContract) + } else { + trace!("apply timestamp"); + program.apply_timestamp(tx, instruction_index, accounts, *dt)?; + trace!("apply timestamp committed"); + program.serialize(&mut accounts[1].userdata) + } + } else { + Err(BudgetError::UninitializedContract) + } + } + Instruction::ApplySignature => { + if let Ok(mut program) = BudgetProgram::deserialize(&accounts[1].userdata) { + if !program.is_pending() { + Err(BudgetError::ContractNotPending) + } else if !program.initialized { + trace!("contract is uninitialized"); + Err(BudgetError::UninitializedContract) + } else { + trace!("apply signature"); + program.apply_signature(tx, instruction_index, accounts)?; + trace!("apply signature committed"); + program.serialize(&mut accounts[1].userdata) + } + } else { + Err(BudgetError::UninitializedContract) + } + } + } +} + +/// Budget DSL contract interface +/// * tx - the transaction +/// * accounts[0] - The source of the tokens +/// * accounts[1] - The contract context. Once the contract has been completed, the tokens can +/// be spent from this account . +pub fn process_transaction( + tx: &Transaction, + instruction_index: usize, + accounts: &mut [&mut Account], +) -> Result<(), BudgetError> { + if let Ok(instruction) = deserialize(tx.userdata(instruction_index)) { + trace!("process_transaction: {:?}", instruction); + apply_debits(tx, instruction_index, accounts, &instruction) + } else { + info!( + "Invalid transaction userdata: {:?}", + tx.userdata(instruction_index) + ); + Err(BudgetError::UserdataDeserializeFailure) + } +} + +//TODO the contract needs to provide a "get_balance" introspection call of the userdata +pub fn get_balance(account: &Account) -> u64 { + if let Ok(program) = deserialize(&account.userdata) { + let program: BudgetProgram = program; + if program.is_pending() { + 0 + } else { + account.tokens + } + } else { + account.tokens + } +} + impl BudgetProgram { fn is_pending(&self) -> bool { self.pending_budget != None } - pub fn id() -> Pubkey { - Pubkey::new(&BUDGET_PROGRAM_ID) - } - pub fn check_id(program_id: &Pubkey) -> bool { - program_id.as_ref() == BUDGET_PROGRAM_ID - } - /// Process a Witness Signature. Any payment plans waiting on this signature /// will progress one step. fn apply_signature( @@ -109,73 +214,6 @@ impl BudgetProgram { Ok(()) } - fn apply_debits( - tx: &Transaction, - instruction_index: usize, - accounts: &mut [&mut Account], - instruction: &Instruction, - ) -> Result<(), BudgetError> { - if !accounts[0].userdata.is_empty() { - trace!("source is pending"); - return Err(BudgetError::SourceIsPendingContract); - } - match instruction { - Instruction::NewBudget(expr) => { - let expr = expr.clone(); - if let Some(payment) = expr.final_payment() { - accounts[1].tokens += payment.tokens; - Ok(()) - } else { - let existing = Self::deserialize(&accounts[1].userdata).ok(); - if Some(true) == existing.map(|x| x.initialized) { - trace!("contract already exists"); - Err(BudgetError::ContractAlreadyExists) - } else { - let mut program = BudgetProgram::default(); - program.pending_budget = Some(expr); - accounts[1].tokens += accounts[0].tokens; - accounts[0].tokens = 0; - program.initialized = true; - program.serialize(&mut accounts[1].userdata) - } - } - } - Instruction::ApplyTimestamp(dt) => { - if let Ok(mut program) = Self::deserialize(&accounts[1].userdata) { - if !program.is_pending() { - Err(BudgetError::ContractNotPending) - } else if !program.initialized { - trace!("contract is uninitialized"); - Err(BudgetError::UninitializedContract) - } else { - trace!("apply timestamp"); - program.apply_timestamp(tx, instruction_index, accounts, *dt)?; - trace!("apply timestamp committed"); - program.serialize(&mut accounts[1].userdata) - } - } else { - Err(BudgetError::UninitializedContract) - } - } - Instruction::ApplySignature => { - if let Ok(mut program) = Self::deserialize(&accounts[1].userdata) { - if !program.is_pending() { - Err(BudgetError::ContractNotPending) - } else if !program.initialized { - trace!("contract is uninitialized"); - Err(BudgetError::UninitializedContract) - } else { - trace!("apply signature"); - program.apply_signature(tx, instruction_index, accounts)?; - trace!("apply signature committed"); - program.serialize(&mut accounts[1].userdata) - } - } else { - Err(BudgetError::UninitializedContract) - } - } - } - } fn serialize(&self, output: &mut [u8]) -> Result<(), BudgetError> { let len = serialized_size(self).unwrap() as u64; if output.len() < len as usize { @@ -211,47 +249,11 @@ impl BudgetProgram { } deserialize(&input[8..8 + len as usize]) } - - /// Budget DSL contract interface - /// * tx - the transaction - /// * accounts[0] - The source of the tokens - /// * accounts[1] - The contract context. Once the contract has been completed, the tokens can - /// be spent from this account . - pub fn process_transaction( - tx: &Transaction, - instruction_index: usize, - accounts: &mut [&mut Account], - ) -> Result<(), BudgetError> { - if let Ok(instruction) = deserialize(tx.userdata(instruction_index)) { - trace!("process_transaction: {:?}", instruction); - Self::apply_debits(tx, instruction_index, accounts, &instruction) - } else { - info!( - "Invalid transaction userdata: {:?}", - tx.userdata(instruction_index) - ); - Err(BudgetError::UserdataDeserializeFailure) - } - } - - //TODO the contract needs to provide a "get_balance" introspection call of the userdata - pub fn get_balance(account: &Account) -> u64 { - if let Ok(program) = deserialize(&account.userdata) { - let program: BudgetProgram = program; - if program.is_pending() { - 0 - } else { - account.tokens - } - } else { - account.tokens - } - } } #[cfg(test)] mod test { + use super::*; use bincode::serialize; - use budget_program::{BudgetError, BudgetProgram}; use budget_transaction::BudgetTransaction; use chrono::prelude::{DateTime, NaiveDate, Utc}; use signature::{GenKeys, Keypair, KeypairUtil}; @@ -262,11 +264,11 @@ mod test { fn process_transaction(tx: &Transaction, accounts: &mut [Account]) -> Result<(), BudgetError> { let mut refs: Vec<&mut Account> = accounts.iter_mut().collect(); - BudgetProgram::process_transaction(&tx, 0, &mut refs[..]) + super::process_transaction(&tx, 0, &mut refs[..]) } #[test] fn test_serializer() { - let mut a = Account::new(0, 512, BudgetProgram::id()); + let mut a = Account::new(0, 512, id()); let b = BudgetProgram::default(); b.serialize(&mut a.userdata).unwrap(); let buf = serialize(&b).unwrap(); @@ -277,7 +279,7 @@ mod test { #[test] fn test_serializer_userdata_too_small() { - let mut a = Account::new(0, 1, BudgetProgram::id()); + let mut a = Account::new(0, 1, id()); let b = BudgetProgram::default(); assert_eq!( b.serialize(&mut a.userdata), @@ -286,17 +288,14 @@ mod test { } #[test] fn test_invalid_instruction() { - let mut accounts = vec![ - Account::new(1, 0, BudgetProgram::id()), - Account::new(0, 512, BudgetProgram::id()), - ]; + let mut accounts = vec![Account::new(1, 0, id()), Account::new(0, 512, id())]; let from = Keypair::new(); let contract = Keypair::new(); let userdata = (1u8, 2u8, 3u8); let tx = Transaction::new( &from, &[contract.pubkey()], - BudgetProgram::id(), + id(), &userdata, Hash::default(), 0, @@ -307,9 +306,9 @@ mod test { #[test] fn test_unsigned_witness_key() { let mut accounts = vec![ - Account::new(1, 0, BudgetProgram::id()), - Account::new(0, 512, BudgetProgram::id()), - Account::new(0, 0, BudgetProgram::id()), + Account::new(1, 0, id()), + Account::new(0, 512, id()), + Account::new(0, 0, id()), ]; // Initialize BudgetProgram @@ -346,9 +345,9 @@ mod test { #[test] fn test_unsigned_timestamp() { let mut accounts = vec![ - Account::new(1, 0, BudgetProgram::id()), - Account::new(0, 512, BudgetProgram::id()), - Account::new(0, 0, BudgetProgram::id()), + Account::new(1, 0, id()), + Account::new(0, 512, id()), + Account::new(0, 0, id()), ]; // Initialize BudgetProgram @@ -386,9 +385,9 @@ mod test { #[test] fn test_transfer_on_date() { let mut accounts = vec![ - Account::new(1, 0, BudgetProgram::id()), - Account::new(0, 512, BudgetProgram::id()), - Account::new(0, 0, BudgetProgram::id()), + Account::new(1, 0, id()), + Account::new(0, 512, id()), + Account::new(0, 0, id()), ]; let from_account = 0; let contract_account = 1; @@ -462,9 +461,9 @@ mod test { #[test] fn test_cancel_transfer() { let mut accounts = vec![ - Account::new(1, 0, BudgetProgram::id()), - Account::new(0, 512, BudgetProgram::id()), - Account::new(0, 0, BudgetProgram::id()), + Account::new(1, 0, id()), + Account::new(0, 512, id()), + Account::new(0, 0, id()), ]; let from_account = 0; let contract_account = 1; @@ -532,9 +531,9 @@ mod test { #[test] fn test_userdata_too_small() { let mut accounts = vec![ - Account::new(1, 0, BudgetProgram::id()), - Account::new(1, 0, BudgetProgram::id()), // <== userdata is 0, which is not enough - Account::new(1, 0, BudgetProgram::id()), + Account::new(1, 0, id()), + Account::new(1, 0, id()), // <== userdata is 0, which is not enough + Account::new(1, 0, id()), ]; let from = Keypair::new(); let contract = Keypair::new(); diff --git a/src/budget_transaction.rs b/src/budget_transaction.rs index 5ef8ad8fe3..4d3f4805fa 100644 --- a/src/budget_transaction.rs +++ b/src/budget_transaction.rs @@ -3,7 +3,7 @@ use bincode::deserialize; use budget_expr::{BudgetExpr, Condition}; use budget_instruction::Instruction; -use budget_program::BudgetProgram; +use budget_program; use chrono::prelude::*; use payment_plan::Payment; use signature::{Keypair, KeypairUtil}; @@ -85,7 +85,7 @@ impl BudgetTransaction for Transaction { }; let budget_instruction = Instruction::NewBudget(BudgetExpr::Pay(payment)); - let program_ids = vec![Pubkey::new(&SYSTEM_PROGRAM_ID), BudgetProgram::id()]; + let program_ids = vec![Pubkey::new(&SYSTEM_PROGRAM_ID), budget_program::id()]; let instructions = vec![ transaction::Instruction::new(0, &system_instruction, vec![0, 1]), @@ -119,7 +119,7 @@ impl BudgetTransaction for Transaction { Self::new( from_keypair, &[contract, to], - BudgetProgram::id(), + budget_program::id(), &instruction, last_id, 0, @@ -137,7 +137,7 @@ impl BudgetTransaction for Transaction { Self::new( from_keypair, &[contract, to], - BudgetProgram::id(), + budget_program::id(), &instruction, last_id, 0, @@ -167,7 +167,7 @@ impl BudgetTransaction for Transaction { Self::new( from_keypair, &[contract], - BudgetProgram::id(), + budget_program::id(), &instruction, last_id, 0, @@ -195,7 +195,7 @@ impl BudgetTransaction for Transaction { Self::new( from_keypair, &[contract], - BudgetProgram::id(), + budget_program::id(), &instruction, last_id, 0, diff --git a/src/compute_leader_finality_service.rs b/src/compute_leader_finality_service.rs index 0ab54e76e3..ea86ebdaeb 100644 --- a/src/compute_leader_finality_service.rs +++ b/src/compute_leader_finality_service.rs @@ -13,7 +13,7 @@ use std::sync::Arc; use std::thread::sleep; use std::thread::{self, Builder, JoinHandle}; use std::time::Duration; -use vote_program::VoteProgram; +use vote_program::{self, VoteProgram}; #[derive(Debug, PartialEq, Eq)] pub enum FinalityError { @@ -46,7 +46,7 @@ impl ComputeLeaderFinalityService { .filter_map(|account| { // Filter out any accounts that don't belong to the VoteProgram // by returning None - if VoteProgram::check_id(&account.owner) { + if vote_program::check_id(&account.owner) { if let Ok(vote_state) = VoteProgram::deserialize(&account.userdata) { let validator_stake = bank.get_stake(&vote_state.node_id); total_stake += validator_stake; diff --git a/src/leader_scheduler.rs b/src/leader_scheduler.rs index fb78ff1dee..03f30f0b49 100644 --- a/src/leader_scheduler.rs +++ b/src/leader_scheduler.rs @@ -14,7 +14,7 @@ use std::collections::HashSet; use std::io::Cursor; use system_transaction::SystemTransaction; use transaction::Transaction; -use vote_program::{Vote, VoteProgram}; +use vote_program::{self, Vote, VoteProgram}; use vote_transaction::VoteTransaction; pub const DEFAULT_BOOTSTRAP_HEIGHT: u64 = 1000; @@ -309,7 +309,7 @@ impl LeaderScheduler { .accounts .values() .filter_map(|account| { - if VoteProgram::check_id(&account.owner) { + if vote_program::check_id(&account.owner) { if let Ok(vote_state) = VoteProgram::deserialize(&account.userdata) { return vote_state .votes diff --git a/src/mint.rs b/src/mint.rs index 87f8fdf2ee..346f0ff3ff 100644 --- a/src/mint.rs +++ b/src/mint.rs @@ -109,14 +109,14 @@ mod tests { use bincode::deserialize; use ledger::Block; use solana_sdk::system_instruction::SystemInstruction; - use system_program::SystemProgram; + use system_program; #[test] fn test_create_transactions() { let mut transactions = Mint::new(100).create_transaction().into_iter(); let tx = transactions.next().unwrap(); assert_eq!(tx.instructions.len(), 1); - assert!(SystemProgram::check_id(tx.program_id(0))); + assert!(system_program::check_id(tx.program_id(0))); let instruction: SystemInstruction = deserialize(tx.userdata(0)).unwrap(); if let SystemInstruction::Move { tokens } = instruction { assert_eq!(tokens, 100); @@ -133,8 +133,8 @@ mod tests { .into_iter(); let tx = transactions.next().unwrap(); assert_eq!(tx.instructions.len(), 2); - assert!(SystemProgram::check_id(tx.program_id(0))); - assert!(SystemProgram::check_id(tx.program_id(1))); + assert!(system_program::check_id(tx.program_id(0))); + assert!(system_program::check_id(tx.program_id(1))); let instruction: SystemInstruction = deserialize(tx.userdata(0)).unwrap(); if let SystemInstruction::Move { tokens } = instruction { assert_eq!(tokens, 100); diff --git a/src/rpc_pubsub.rs b/src/rpc_pubsub.rs index 810ce9c86c..9541aeb84d 100644 --- a/src/rpc_pubsub.rs +++ b/src/rpc_pubsub.rs @@ -246,7 +246,7 @@ impl RpcSolPubSub for RpcSolPubSubImpl { #[cfg(test)] mod tests { use super::*; - use budget_program::BudgetProgram; + use budget_program; use budget_transaction::BudgetTransaction; use jsonrpc_core::futures::sync::mpsc; use mint::Mint; @@ -400,7 +400,7 @@ mod tests { let witness = Keypair::new(); let contract_funds = Keypair::new(); let contract_state = Keypair::new(); - let budget_program_id = BudgetProgram::id(); + let budget_program_id = budget_program::id(); let loader = Pubkey::default(); // TODO let executable = false; // TODO let bank = Bank::new(&alice); diff --git a/src/sigverify.rs b/src/sigverify.rs index 34a2f58ceb..1576273100 100644 --- a/src/sigverify.rs +++ b/src/sigverify.rs @@ -323,13 +323,13 @@ pub fn make_packet_from_transaction(tx: Transaction) -> Packet { #[cfg(test)] mod tests { use bincode::serialize; - use budget_program::BudgetProgram; + use budget_program; use packet::{Packet, SharedPackets}; use signature::{Keypair, KeypairUtil}; use sigverify; use solana_sdk::hash::Hash; use solana_sdk::system_instruction::SystemInstruction; - use system_program::SystemProgram; + use system_program; use system_transaction::{memfind, test_tx}; use transaction; use transaction::Transaction; @@ -429,7 +429,7 @@ mod tests { let system_instruction = SystemInstruction::Move { tokens }; - let program_ids = vec![SystemProgram::id(), BudgetProgram::id()]; + let program_ids = vec![system_program::id(), budget_program::id()]; let instructions = vec![transaction::Instruction::new( 0, diff --git a/src/storage_program.rs b/src/storage_program.rs index bced49441a..dd46deaa40 100644 --- a/src/storage_program.rs +++ b/src/storage_program.rs @@ -22,34 +22,32 @@ const STORAGE_PROGRAM_ID: [u8; 32] = [ 0, ]; -impl StorageProgram { - pub fn check_id(program_id: &Pubkey) -> bool { - program_id.as_ref() == STORAGE_PROGRAM_ID - } +pub fn check_id(program_id: &Pubkey) -> bool { + program_id.as_ref() == STORAGE_PROGRAM_ID +} - pub fn id() -> Pubkey { - Pubkey::new(&STORAGE_PROGRAM_ID) - } +pub fn id() -> Pubkey { + Pubkey::new(&STORAGE_PROGRAM_ID) +} - pub fn get_balance(account: &Account) -> u64 { - account.tokens - } +pub fn get_balance(account: &Account) -> u64 { + account.tokens +} - pub fn process_transaction( - tx: &Transaction, - pix: usize, - _accounts: &mut [&mut Account], - ) -> Result<(), StorageError> { - if let Ok(syscall) = deserialize(tx.userdata(pix)) { - match syscall { - StorageProgram::SubmitMiningProof { sha_state } => { - info!("Mining proof submitted with state {:?}", sha_state); - return Ok(()); - } +pub fn process_transaction( + tx: &Transaction, + pix: usize, + _accounts: &mut [&mut Account], +) -> Result<(), StorageError> { + if let Ok(syscall) = deserialize(tx.userdata(pix)) { + match syscall { + StorageProgram::SubmitMiningProof { sha_state } => { + info!("Mining proof submitted with state {:?}", sha_state); + return Ok(()); } - } else { - return Err(StorageError::InvalidUserData); } + } else { + return Err(StorageError::InvalidUserData); } } @@ -62,14 +60,7 @@ mod test { #[test] fn test_storage_tx() { let keypair = Keypair::new(); - let tx = Transaction::new( - &keypair, - &[], - StorageProgram::id(), - &(), - Default::default(), - 0, - ); - assert!(StorageProgram::process_transaction(&tx, 0, &mut []).is_err()); + let tx = Transaction::new(&keypair, &[], id(), &(), Default::default(), 0); + assert!(process_transaction(&tx, 0, &mut []).is_err()); } } diff --git a/src/storage_stage.rs b/src/storage_stage.rs index 5199c56351..874243ad05 100644 --- a/src/storage_stage.rs +++ b/src/storage_stage.rs @@ -18,7 +18,7 @@ use std::sync::mpsc::RecvTimeoutError; use std::sync::{Arc, RwLock}; use std::thread::{self, Builder, JoinHandle}; use std::time::Duration; -use vote_program::VoteProgram; +use vote_program; // Block of hash answers to validate against // Vec of [ledger blocks] x [keys] @@ -215,7 +215,7 @@ impl StorageStage { // the storage_keys with their signatures. for tx in entry.transactions { for program_id in tx.program_ids { - if VoteProgram::check_id(&program_id) { + if vote_program::check_id(&program_id) { debug!( "generating storage_keys from votes current_key_idx: {}", *current_key_idx diff --git a/src/storage_transaction.rs b/src/storage_transaction.rs index 70682c04f2..ccc1a450b2 100644 --- a/src/storage_transaction.rs +++ b/src/storage_transaction.rs @@ -1,6 +1,6 @@ use signature::{Keypair, KeypairUtil}; use solana_sdk::hash::Hash; -use storage_program::StorageProgram; +use storage_program::{self, StorageProgram}; use transaction::Transaction; pub trait StorageTransaction { @@ -13,7 +13,7 @@ impl StorageTransaction for Transaction { Transaction::new( from_keypair, &[from_keypair.pubkey()], - StorageProgram::id(), + storage_program::id(), &program, last_id, 0, diff --git a/src/system_program.rs b/src/system_program.rs index 7d3f65a90a..cbfb1c1b93 100644 --- a/src/system_program.rs +++ b/src/system_program.rs @@ -23,86 +23,82 @@ impl std::error::Error for Error {} pub type Result = std::result::Result; -pub struct SystemProgram {} - pub const SYSTEM_PROGRAM_ID: [u8; 32] = [0u8; 32]; -impl SystemProgram { - pub fn check_id(program_id: &Pubkey) -> bool { - program_id.as_ref() == SYSTEM_PROGRAM_ID - } +pub fn check_id(program_id: &Pubkey) -> bool { + program_id.as_ref() == SYSTEM_PROGRAM_ID +} - pub fn id() -> Pubkey { - 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)) { - trace!("process_transaction: {:?}", syscall); - match syscall { - SystemInstruction::CreateAccount { - tokens, - space, - program_id, - } => { - if !Self::check_id(&accounts[0].owner) { - info!("Invalid account[0] owner"); - Err(Error::InvalidArgument)?; - } +pub fn id() -> Pubkey { + 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)) { + trace!("process_transaction: {:?}", syscall); + match syscall { + SystemInstruction::CreateAccount { + tokens, + space, + program_id, + } => { + if !check_id(&accounts[0].owner) { + info!("Invalid account[0] owner"); + Err(Error::InvalidArgument)?; + } - if space > 0 - && (!accounts[1].userdata.is_empty() || !Self::check_id(&accounts[1].owner)) - { - info!("Invalid account[1]"); - Err(Error::InvalidArgument)?; - } - if tokens > accounts[0].tokens { - info!("Insufficient tokens in account[0]"); - Err(Error::ResultWithNegativeTokens(pix as u8))?; - } - accounts[0].tokens -= tokens; - accounts[1].tokens += tokens; - accounts[1].owner = program_id; - accounts[1].userdata = vec![0; space as usize]; - accounts[1].executable = false; - accounts[1].loader = Pubkey::default(); + if space > 0 && (!accounts[1].userdata.is_empty() || !check_id(&accounts[1].owner)) + { + info!("Invalid account[1]"); + Err(Error::InvalidArgument)?; } - SystemInstruction::Assign { program_id } => { - if !Self::check_id(&accounts[0].owner) { - Err(Error::AssignOfUnownedAccount)?; - } - accounts[0].owner = program_id; - } - SystemInstruction::Move { tokens } => { - //bank should be verifying correctness - if tokens > accounts[0].tokens { - info!("Insufficient tokens in account[0]"); - Err(Error::ResultWithNegativeTokens(pix as u8))?; - } - accounts[0].tokens -= tokens; - accounts[1].tokens += tokens; - } - 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]; + if tokens > accounts[0].tokens { + info!("Insufficient tokens in account[0]"); + Err(Error::ResultWithNegativeTokens(pix as u8))?; } + accounts[0].tokens -= tokens; + accounts[1].tokens += tokens; + accounts[1].owner = program_id; + accounts[1].userdata = vec![0; space as usize]; + accounts[1].executable = false; + accounts[1].loader = Pubkey::default(); + } + SystemInstruction::Assign { program_id } => { + if !check_id(&accounts[0].owner) { + Err(Error::AssignOfUnownedAccount)?; + } + accounts[0].owner = program_id; + } + SystemInstruction::Move { tokens } => { + //bank should be verifying correctness + if tokens > accounts[0].tokens { + info!("Insufficient tokens in account[0]"); + Err(Error::ResultWithNegativeTokens(pix as u8))?; + } + accounts[0].tokens -= tokens; + accounts[1].tokens += tokens; + } + 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(()) - } else { - info!("Invalid transaction userdata: {:?}", tx.userdata(pix)); - Err(Error::InvalidArgument) } + Ok(()) + } else { + info!("Invalid transaction userdata: {:?}", tx.userdata(pix)); + Err(Error::InvalidArgument) } } + #[cfg(test)] mod test { use super::*; @@ -110,13 +106,12 @@ mod test { use solana_sdk::account::Account; use solana_sdk::hash::Hash; use solana_sdk::pubkey::Pubkey; - 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[..]) + super::process_transaction(&tx, 0, &mut refs[..]) } #[test] @@ -246,7 +241,7 @@ mod test { #[test] fn test_sdk_serialize() { let keypair = Keypair::new(); - use budget_program::BudgetProgram; + use budget_program; // CreateAccount let tx = Transaction::system_create( @@ -255,7 +250,7 @@ mod test { Hash::default(), 111, 222, - BudgetProgram::id(), + budget_program::id(), 0, ); @@ -287,7 +282,7 @@ mod test { ); // Assign - let tx = Transaction::system_assign(&keypair, Hash::default(), BudgetProgram::id(), 0); + let tx = Transaction::system_assign(&keypair, Hash::default(), budget_program::id(), 0); assert_eq!( tx.userdata(0).to_vec(), vec![ diff --git a/src/system_transaction.rs b/src/system_transaction.rs index fcd0cc29e7..d614596491 100644 --- a/src/system_transaction.rs +++ b/src/system_transaction.rs @@ -4,7 +4,7 @@ use signature::{Keypair, KeypairUtil}; use solana_sdk::hash::Hash; use solana_sdk::pubkey::Pubkey; use solana_sdk::system_instruction::SystemInstruction; -use system_program::SystemProgram; +use system_program; use transaction::{Instruction, Transaction}; @@ -60,7 +60,7 @@ impl SystemTransaction for Transaction { Transaction::new( from_keypair, &[to], - SystemProgram::id(), + system_program::id(), &create, last_id, fee, @@ -72,7 +72,7 @@ impl SystemTransaction for Transaction { Transaction::new( from_keypair, &[], - SystemProgram::id(), + system_program::id(), &assign, last_id, fee, @@ -94,7 +94,7 @@ impl SystemTransaction for Transaction { Transaction::new( from_keypair, &[to], - SystemProgram::id(), + system_program::id(), &move_tokens, last_id, fee, @@ -116,14 +116,21 @@ impl SystemTransaction for Transaction { &to_keys, last_id, fee, - vec![SystemProgram::id()], + vec![system_program::id()], instructions, ) } /// Create and sign new SystemInstruction::Spawn transaction fn system_spawn(from_keypair: &Keypair, last_id: Hash, fee: u64) -> Self { let spawn = SystemInstruction::Spawn; - Transaction::new(from_keypair, &[], SystemProgram::id(), &spawn, last_id, fee) + Transaction::new( + from_keypair, + &[], + system_program::id(), + &spawn, + last_id, + fee, + ) } } diff --git a/src/vote_program.rs b/src/vote_program.rs index 1c7edf9736..7e9289a41e 100644 --- a/src/vote_program.rs +++ b/src/vote_program.rs @@ -56,15 +56,77 @@ const VOTE_PROGRAM_ID: [u8; 32] = [ 0, ]; +pub fn check_id(program_id: &Pubkey) -> bool { + program_id.as_ref() == VOTE_PROGRAM_ID +} + +pub fn id() -> Pubkey { + Pubkey::new(&VOTE_PROGRAM_ID) +} + +pub fn process_transaction( + tx: &Transaction, + instruction_index: usize, + accounts: &mut [&mut Account], +) -> Result<()> { + match deserialize(tx.userdata(instruction_index)) { + Ok(VoteInstruction::RegisterAccount) => { + // TODO: a single validator could register multiple "vote accounts" + // which would clutter the "accounts" structure. See github issue 1654. + accounts[1].owner = id(); + + let mut vote_state = VoteProgram { + votes: VecDeque::new(), + node_id: *tx.from(), + }; + + vote_state.serialize(&mut accounts[1].userdata)?; + + Ok(()) + } + Ok(VoteInstruction::NewVote(vote)) => { + if !check_id(&accounts[0].owner) { + error!("accounts[0] is not assigned to the VOTE_PROGRAM"); + Err(Error::InvalidArguments)?; + } + + let mut vote_state = VoteProgram::deserialize(&accounts[0].userdata)?; + + // TODO: Integrity checks + // a) Verify the vote's bank hash matches what is expected + // b) Verify vote is older than previous votes + + // Only keep around the most recent MAX_VOTE_HISTORY votes + if vote_state.votes.len() == MAX_VOTE_HISTORY { + vote_state.votes.pop_front(); + } + + vote_state.votes.push_back(vote); + vote_state.serialize(&mut accounts[0].userdata)?; + + Ok(()) + } + Err(_) => { + info!( + "Invalid vote transaction userdata: {:?}", + tx.userdata(instruction_index) + ); + Err(Error::UserdataDeserializeFailure) + } + } +} + +pub fn get_max_size() -> usize { + // Upper limit on the size of the Vote State. Equal to + // sizeof(VoteProgram) + MAX_VOTE_HISTORY * sizeof(Vote) + + // 32 (the size of the Pubkey) + 2 (2 bytes for the size) + mem::size_of::() + + MAX_VOTE_HISTORY * mem::size_of::() + + mem::size_of::() + + mem::size_of::() +} + impl VoteProgram { - pub fn check_id(program_id: &Pubkey) -> bool { - program_id.as_ref() == VOTE_PROGRAM_ID - } - - pub fn id() -> Pubkey { - Pubkey::new(&VOTE_PROGRAM_ID) - } - pub fn deserialize(input: &[u8]) -> Result { let len = LittleEndian::read_u16(&input[0..2]) as usize; @@ -95,68 +157,6 @@ impl VoteProgram { output[2..=serialized_len as usize + 1].clone_from_slice(&self_serialized); Ok(()) } - - pub fn process_transaction( - tx: &Transaction, - instruction_index: usize, - accounts: &mut [&mut Account], - ) -> Result<()> { - match deserialize(tx.userdata(instruction_index)) { - Ok(VoteInstruction::RegisterAccount) => { - // TODO: a single validator could register multiple "vote accounts" - // which would clutter the "accounts" structure. See github issue 1654. - accounts[1].owner = Self::id(); - - let mut vote_state = VoteProgram { - votes: VecDeque::new(), - node_id: *tx.from(), - }; - - vote_state.serialize(&mut accounts[1].userdata)?; - - Ok(()) - } - Ok(VoteInstruction::NewVote(vote)) => { - if !Self::check_id(&accounts[0].owner) { - error!("accounts[0] is not assigned to the VOTE_PROGRAM"); - Err(Error::InvalidArguments)?; - } - - let mut vote_state = Self::deserialize(&accounts[0].userdata)?; - - // TODO: Integrity checks - // a) Verify the vote's bank hash matches what is expected - // b) Verify vote is older than previous votes - - // Only keep around the most recent MAX_VOTE_HISTORY votes - if vote_state.votes.len() == MAX_VOTE_HISTORY { - vote_state.votes.pop_front(); - } - - vote_state.votes.push_back(vote); - vote_state.serialize(&mut accounts[0].userdata)?; - - Ok(()) - } - Err(_) => { - info!( - "Invalid vote transaction userdata: {:?}", - tx.userdata(instruction_index) - ); - Err(Error::UserdataDeserializeFailure) - } - } - } - - pub fn get_max_size() -> usize { - // Upper limit on the size of the Vote State. Equal to - // sizeof(VoteProgram) + MAX_VOTE_HISTORY * sizeof(Vote) + - // 32 (the size of the Pubkey) + 2 (2 bytes for the size) - mem::size_of::() - + MAX_VOTE_HISTORY * mem::size_of::() - + mem::size_of::() - + mem::size_of::() - } } #[cfg(test)] @@ -165,7 +165,7 @@ mod tests { #[test] fn test_serde() -> Result<()> { - let mut buffer: Vec = vec![0; VoteProgram::get_max_size()]; + let mut buffer: Vec = vec![0; get_max_size()]; let mut vote_program = VoteProgram::default(); vote_program.votes = (0..MAX_VOTE_HISTORY).map(|_| Vote::default()).collect(); vote_program.serialize(&mut buffer).unwrap(); diff --git a/src/vote_transaction.rs b/src/vote_transaction.rs index c53b6dc902..662a23e6c5 100644 --- a/src/vote_transaction.rs +++ b/src/vote_transaction.rs @@ -12,7 +12,7 @@ use solana_sdk::hash::Hash; use solana_sdk::pubkey::Pubkey; use system_transaction::SystemTransaction; use transaction::Transaction; -use vote_program::{Vote, VoteInstruction, VoteProgram}; +use vote_program::{self, Vote, VoteInstruction}; pub trait VoteTransaction { fn vote_new(vote_account: &Keypair, vote: Vote, last_id: Hash, fee: u64) -> Self; @@ -37,7 +37,7 @@ impl VoteTransaction for Transaction { Transaction::new( vote_account, &[], - VoteProgram::id(), + vote_program::id(), &instruction, last_id, fee, @@ -55,8 +55,8 @@ impl VoteTransaction for Transaction { new_vote_account_id, last_id, num_tokens, - VoteProgram::get_max_size() as u64, - VoteProgram::id(), + vote_program::get_max_size() as u64, + vote_program::id(), 0, ) } @@ -71,7 +71,7 @@ impl VoteTransaction for Transaction { Transaction::new( validator_id, &[vote_account_id], - VoteProgram::id(), + vote_program::id(), ®ister_tx, last_id, fee, @@ -82,7 +82,7 @@ impl VoteTransaction for Transaction { let mut votes = vec![]; for i in 0..self.instructions.len() { let tx_program_id = self.program_id(i); - if VoteProgram::check_id(&tx_program_id) { + if vote_program::check_id(&tx_program_id) { if let Ok(Some(VoteInstruction::NewVote(vote))) = deserialize(&self.userdata(i)) { votes.push((self.account_keys[0], vote, self.last_id)) } diff --git a/src/wallet.rs b/src/wallet.rs index baeacd0598..4264ba69fd 100644 --- a/src/wallet.rs +++ b/src/wallet.rs @@ -1,7 +1,7 @@ use bincode::serialize; use bpf_loader; use bs58; -use budget_program::BudgetProgram; +use budget_program; use budget_transaction::BudgetTransaction; use chrono::prelude::*; use clap::ArgMatches; @@ -497,7 +497,7 @@ pub fn process_command(config: &WalletConfig) -> Result Result