Hoist program static methods to top-level functions
This commit is contained in:
parent
e2373ff51a
commit
cd488b7d07
71
src/bank.rs
71
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)));
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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,
|
||||
|
@ -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![
|
||||
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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))
|
||||
}
|
||||
|
@ -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(
|
||||
|
Loading…
x
Reference in New Issue
Block a user