Hoist program static methods to top-level functions

This commit is contained in:
Greg Fitzgerald 2018-11-23 13:45:34 -07:00
parent e2373ff51a
commit cd488b7d07
16 changed files with 379 additions and 384 deletions

View File

@ -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)));
}

View File

@ -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();

View File

@ -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,

View File

@ -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;

View File

@ -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

View File

@ -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);

View File

@ -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);

View File

@ -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,

View File

@ -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());
}
}

View File

@ -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

View File

@ -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,

View File

@ -23,86 +23,82 @@ impl std::error::Error for Error {}
pub type Result<T> = std::result::Result<T, Error>;
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![

View File

@ -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,
)
}
}

View File

@ -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::<VoteProgram>()
+ MAX_VOTE_HISTORY * mem::size_of::<Vote>()
+ mem::size_of::<Pubkey>()
+ mem::size_of::<u16>()
}
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<VoteProgram> {
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::<VoteProgram>()
+ MAX_VOTE_HISTORY * mem::size_of::<Vote>()
+ mem::size_of::<Pubkey>()
+ mem::size_of::<u16>()
}
}
#[cfg(test)]
@ -165,7 +165,7 @@ mod tests {
#[test]
fn test_serde() -> Result<()> {
let mut buffer: Vec<u8> = vec![0; VoteProgram::get_max_size()];
let mut buffer: Vec<u8> = 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();

View File

@ -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(),
&register_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))
}

View File

@ -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<String, Box<error::Error
let contract_funds = Keypair::new();
let contract_state = Keypair::new();
let budget_program_id = BudgetProgram::id();
let budget_program_id = budget_program::id();
// Create account for contract funds
let tx = Transaction::system_create(
@ -553,7 +553,7 @@ pub fn process_command(config: &WalletConfig) -> Result<String, Box<error::Error
let contract_funds = Keypair::new();
let contract_state = Keypair::new();
let budget_program_id = BudgetProgram::id();
let budget_program_id = budget_program::id();
// Create account for contract funds
let tx = Transaction::system_create(