Hoist program static methods to top-level functions
This commit is contained in:
71
src/bank.rs
71
src/bank.rs
@ -6,7 +6,7 @@
|
|||||||
use bincode::deserialize;
|
use bincode::deserialize;
|
||||||
use bincode::serialize;
|
use bincode::serialize;
|
||||||
use bpf_loader;
|
use bpf_loader;
|
||||||
use budget_program::BudgetProgram;
|
use budget_program;
|
||||||
use counter::Counter;
|
use counter::Counter;
|
||||||
use entry::Entry;
|
use entry::Entry;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
@ -34,13 +34,13 @@ use std::result;
|
|||||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
use std::sync::{Arc, Mutex, RwLock};
|
use std::sync::{Arc, Mutex, RwLock};
|
||||||
use std::time::Instant;
|
use std::time::Instant;
|
||||||
use storage_program::StorageProgram;
|
use storage_program;
|
||||||
use system_program::{Error, SystemProgram};
|
use system_program;
|
||||||
use system_transaction::SystemTransaction;
|
use system_transaction::SystemTransaction;
|
||||||
use token_program;
|
use token_program;
|
||||||
use tokio::prelude::Future;
|
use tokio::prelude::Future;
|
||||||
use transaction::Transaction;
|
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
|
/// 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
|
/// of. Once the bank discards a `last_id`, it will reject any transactions that use
|
||||||
@ -736,7 +736,7 @@ impl Bank {
|
|||||||
// Verify the transaction
|
// Verify the transaction
|
||||||
|
|
||||||
// Make sure that program_id is still the same or this was just assigned by the system call contract
|
// 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));
|
return Err(BankError::ModifiedContractId(instruction_index as u8));
|
||||||
}
|
}
|
||||||
// For accounts unassigned to the contract, the individual balance of each accounts cannot decrease.
|
// For accounts unassigned to the contract, the individual balance of each accounts cannot decrease.
|
||||||
@ -817,29 +817,33 @@ impl Bank {
|
|||||||
|
|
||||||
// Call the contract method
|
// Call the contract method
|
||||||
// It's up to the contract to implement its own rules on moving funds
|
// 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) =
|
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 {
|
let err = match err {
|
||||||
Error::ResultWithNegativeTokens(i) => BankError::ResultWithNegativeTokens(i),
|
system_program::Error::ResultWithNegativeTokens(i) => {
|
||||||
|
BankError::ResultWithNegativeTokens(i)
|
||||||
|
}
|
||||||
_ => BankError::ProgramRuntimeError(instruction_index as u8),
|
_ => BankError::ProgramRuntimeError(instruction_index as u8),
|
||||||
};
|
};
|
||||||
return Err(err);
|
return Err(err);
|
||||||
}
|
}
|
||||||
} else if BudgetProgram::check_id(&program_id) {
|
} else if budget_program::check_id(&program_id) {
|
||||||
if BudgetProgram::process_transaction(&tx, instruction_index, program_accounts).is_err()
|
if budget_program::process_transaction(&tx, instruction_index, program_accounts)
|
||||||
{
|
|
||||||
return Err(BankError::ProgramRuntimeError(instruction_index as u8));
|
|
||||||
}
|
|
||||||
} else if StorageProgram::check_id(&program_id) {
|
|
||||||
if StorageProgram::process_transaction(&tx, instruction_index, program_accounts)
|
|
||||||
.is_err()
|
.is_err()
|
||||||
{
|
{
|
||||||
return Err(BankError::ProgramRuntimeError(instruction_index as u8));
|
return Err(BankError::ProgramRuntimeError(instruction_index as u8));
|
||||||
}
|
}
|
||||||
} else if VoteProgram::check_id(&program_id) {
|
} else if storage_program::check_id(&program_id) {
|
||||||
if VoteProgram::process_transaction(&tx, instruction_index, program_accounts).is_err() {
|
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));
|
return Err(BankError::ProgramRuntimeError(instruction_index as u8));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -1233,8 +1237,8 @@ impl Bank {
|
|||||||
{
|
{
|
||||||
// Process the first transaction
|
// Process the first transaction
|
||||||
let tx = &entry1.transactions[0];
|
let tx = &entry1.transactions[0];
|
||||||
assert!(SystemProgram::check_id(tx.program_id(0)), "Invalid ledger");
|
assert!(system_program::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(1)), "Invalid ledger");
|
||||||
let mut instruction: SystemInstruction = deserialize(tx.userdata(0)).unwrap();
|
let mut instruction: SystemInstruction = deserialize(tx.userdata(0)).unwrap();
|
||||||
let mint_deposit = if let SystemInstruction::Move { tokens } = instruction {
|
let mint_deposit = if let SystemInstruction::Move { tokens } = instruction {
|
||||||
Some(tokens)
|
Some(tokens)
|
||||||
@ -1309,10 +1313,10 @@ impl Bank {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn read_balance(account: &Account) -> u64 {
|
pub fn read_balance(account: &Account) -> u64 {
|
||||||
if SystemProgram::check_id(&account.owner) {
|
if system_program::check_id(&account.owner) {
|
||||||
SystemProgram::get_balance(account)
|
system_program::get_balance(account)
|
||||||
} else if BudgetProgram::check_id(&account.owner) {
|
} else if budget_program::check_id(&account.owner) {
|
||||||
BudgetProgram::get_balance(account)
|
budget_program::get_balance(account)
|
||||||
} else {
|
} else {
|
||||||
account.tokens
|
account.tokens
|
||||||
}
|
}
|
||||||
@ -1498,7 +1502,6 @@ impl Bank {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use bincode::serialize;
|
use bincode::serialize;
|
||||||
use budget_program::BudgetProgram;
|
|
||||||
use entry::next_entry;
|
use entry::next_entry;
|
||||||
use entry::Entry;
|
use entry::Entry;
|
||||||
use jsonrpc_macros::pubsub::{Subscriber, SubscriptionId};
|
use jsonrpc_macros::pubsub::{Subscriber, SubscriptionId};
|
||||||
@ -1598,7 +1601,7 @@ mod tests {
|
|||||||
&[key1, key2],
|
&[key1, key2],
|
||||||
mint.last_id(),
|
mint.last_id(),
|
||||||
0,
|
0,
|
||||||
vec![SystemProgram::id()],
|
vec![system_program::id()],
|
||||||
instructions,
|
instructions,
|
||||||
);
|
);
|
||||||
let res = bank.process_transactions(&vec![t1.clone()]);
|
let res = bank.process_transactions(&vec![t1.clone()]);
|
||||||
@ -2021,7 +2024,7 @@ mod tests {
|
|||||||
last_id,
|
last_id,
|
||||||
1,
|
1,
|
||||||
16,
|
16,
|
||||||
BudgetProgram::id(),
|
budget_program::id(),
|
||||||
0,
|
0,
|
||||||
);
|
);
|
||||||
bank.process_transaction(&tx).unwrap();
|
bank.process_transaction(&tx).unwrap();
|
||||||
@ -2253,26 +2256,26 @@ mod tests {
|
|||||||
0, 0, 0, 0,
|
0, 0, 0, 0,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
assert_eq!(SystemProgram::id(), system);
|
assert_eq!(system_program::id(), system);
|
||||||
assert_eq!(native_loader::id(), native);
|
assert_eq!(native_loader::id(), native);
|
||||||
assert_eq!(bpf_loader::id(), bpf);
|
assert_eq!(bpf_loader::id(), bpf);
|
||||||
assert_eq!(BudgetProgram::id(), budget);
|
assert_eq!(budget_program::id(), budget);
|
||||||
assert_eq!(StorageProgram::id(), storage);
|
assert_eq!(storage_program::id(), storage);
|
||||||
assert_eq!(token_program::id(), token);
|
assert_eq!(token_program::id(), token);
|
||||||
assert_eq!(VoteProgram::id(), vote);
|
assert_eq!(vote_program::id(), vote);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_program_id_uniqueness() {
|
fn test_program_id_uniqueness() {
|
||||||
let mut unique = HashSet::new();
|
let mut unique = HashSet::new();
|
||||||
let ids = vec![
|
let ids = vec![
|
||||||
SystemProgram::id(),
|
system_program::id(),
|
||||||
native_loader::id(),
|
native_loader::id(),
|
||||||
bpf_loader::id(),
|
bpf_loader::id(),
|
||||||
BudgetProgram::id(),
|
budget_program::id(),
|
||||||
StorageProgram::id(),
|
storage_program::id(),
|
||||||
token_program::id(),
|
token_program::id(),
|
||||||
VoteProgram::id(),
|
vote_program::id(),
|
||||||
];
|
];
|
||||||
assert!(ids.into_iter().all(move |id| unique.insert(id)));
|
assert!(ids.into_iter().all(move |id| unique.insert(id)));
|
||||||
}
|
}
|
||||||
|
@ -35,17 +35,122 @@ const BUDGET_PROGRAM_ID: [u8; 32] = [
|
|||||||
0,
|
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 {
|
impl BudgetProgram {
|
||||||
fn is_pending(&self) -> bool {
|
fn is_pending(&self) -> bool {
|
||||||
self.pending_budget != None
|
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
|
/// Process a Witness Signature. Any payment plans waiting on this signature
|
||||||
/// will progress one step.
|
/// will progress one step.
|
||||||
fn apply_signature(
|
fn apply_signature(
|
||||||
@ -109,73 +214,6 @@ impl BudgetProgram {
|
|||||||
Ok(())
|
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> {
|
fn serialize(&self, output: &mut [u8]) -> Result<(), BudgetError> {
|
||||||
let len = serialized_size(self).unwrap() as u64;
|
let len = serialized_size(self).unwrap() as u64;
|
||||||
if output.len() < len as usize {
|
if output.len() < len as usize {
|
||||||
@ -211,47 +249,11 @@ impl BudgetProgram {
|
|||||||
}
|
}
|
||||||
deserialize(&input[8..8 + len as usize])
|
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)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
|
use super::*;
|
||||||
use bincode::serialize;
|
use bincode::serialize;
|
||||||
use budget_program::{BudgetError, BudgetProgram};
|
|
||||||
use budget_transaction::BudgetTransaction;
|
use budget_transaction::BudgetTransaction;
|
||||||
use chrono::prelude::{DateTime, NaiveDate, Utc};
|
use chrono::prelude::{DateTime, NaiveDate, Utc};
|
||||||
use signature::{GenKeys, Keypair, KeypairUtil};
|
use signature::{GenKeys, Keypair, KeypairUtil};
|
||||||
@ -262,11 +264,11 @@ mod test {
|
|||||||
|
|
||||||
fn process_transaction(tx: &Transaction, accounts: &mut [Account]) -> Result<(), BudgetError> {
|
fn process_transaction(tx: &Transaction, accounts: &mut [Account]) -> Result<(), BudgetError> {
|
||||||
let mut refs: Vec<&mut Account> = accounts.iter_mut().collect();
|
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]
|
#[test]
|
||||||
fn test_serializer() {
|
fn test_serializer() {
|
||||||
let mut a = Account::new(0, 512, BudgetProgram::id());
|
let mut a = Account::new(0, 512, id());
|
||||||
let b = BudgetProgram::default();
|
let b = BudgetProgram::default();
|
||||||
b.serialize(&mut a.userdata).unwrap();
|
b.serialize(&mut a.userdata).unwrap();
|
||||||
let buf = serialize(&b).unwrap();
|
let buf = serialize(&b).unwrap();
|
||||||
@ -277,7 +279,7 @@ mod test {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_serializer_userdata_too_small() {
|
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();
|
let b = BudgetProgram::default();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
b.serialize(&mut a.userdata),
|
b.serialize(&mut a.userdata),
|
||||||
@ -286,17 +288,14 @@ mod test {
|
|||||||
}
|
}
|
||||||
#[test]
|
#[test]
|
||||||
fn test_invalid_instruction() {
|
fn test_invalid_instruction() {
|
||||||
let mut accounts = vec![
|
let mut accounts = vec![Account::new(1, 0, id()), Account::new(0, 512, id())];
|
||||||
Account::new(1, 0, BudgetProgram::id()),
|
|
||||||
Account::new(0, 512, BudgetProgram::id()),
|
|
||||||
];
|
|
||||||
let from = Keypair::new();
|
let from = Keypair::new();
|
||||||
let contract = Keypair::new();
|
let contract = Keypair::new();
|
||||||
let userdata = (1u8, 2u8, 3u8);
|
let userdata = (1u8, 2u8, 3u8);
|
||||||
let tx = Transaction::new(
|
let tx = Transaction::new(
|
||||||
&from,
|
&from,
|
||||||
&[contract.pubkey()],
|
&[contract.pubkey()],
|
||||||
BudgetProgram::id(),
|
id(),
|
||||||
&userdata,
|
&userdata,
|
||||||
Hash::default(),
|
Hash::default(),
|
||||||
0,
|
0,
|
||||||
@ -307,9 +306,9 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_unsigned_witness_key() {
|
fn test_unsigned_witness_key() {
|
||||||
let mut accounts = vec![
|
let mut accounts = vec![
|
||||||
Account::new(1, 0, BudgetProgram::id()),
|
Account::new(1, 0, id()),
|
||||||
Account::new(0, 512, BudgetProgram::id()),
|
Account::new(0, 512, id()),
|
||||||
Account::new(0, 0, BudgetProgram::id()),
|
Account::new(0, 0, id()),
|
||||||
];
|
];
|
||||||
|
|
||||||
// Initialize BudgetProgram
|
// Initialize BudgetProgram
|
||||||
@ -346,9 +345,9 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_unsigned_timestamp() {
|
fn test_unsigned_timestamp() {
|
||||||
let mut accounts = vec![
|
let mut accounts = vec![
|
||||||
Account::new(1, 0, BudgetProgram::id()),
|
Account::new(1, 0, id()),
|
||||||
Account::new(0, 512, BudgetProgram::id()),
|
Account::new(0, 512, id()),
|
||||||
Account::new(0, 0, BudgetProgram::id()),
|
Account::new(0, 0, id()),
|
||||||
];
|
];
|
||||||
|
|
||||||
// Initialize BudgetProgram
|
// Initialize BudgetProgram
|
||||||
@ -386,9 +385,9 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_transfer_on_date() {
|
fn test_transfer_on_date() {
|
||||||
let mut accounts = vec![
|
let mut accounts = vec![
|
||||||
Account::new(1, 0, BudgetProgram::id()),
|
Account::new(1, 0, id()),
|
||||||
Account::new(0, 512, BudgetProgram::id()),
|
Account::new(0, 512, id()),
|
||||||
Account::new(0, 0, BudgetProgram::id()),
|
Account::new(0, 0, id()),
|
||||||
];
|
];
|
||||||
let from_account = 0;
|
let from_account = 0;
|
||||||
let contract_account = 1;
|
let contract_account = 1;
|
||||||
@ -462,9 +461,9 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_cancel_transfer() {
|
fn test_cancel_transfer() {
|
||||||
let mut accounts = vec![
|
let mut accounts = vec![
|
||||||
Account::new(1, 0, BudgetProgram::id()),
|
Account::new(1, 0, id()),
|
||||||
Account::new(0, 512, BudgetProgram::id()),
|
Account::new(0, 512, id()),
|
||||||
Account::new(0, 0, BudgetProgram::id()),
|
Account::new(0, 0, id()),
|
||||||
];
|
];
|
||||||
let from_account = 0;
|
let from_account = 0;
|
||||||
let contract_account = 1;
|
let contract_account = 1;
|
||||||
@ -532,9 +531,9 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_userdata_too_small() {
|
fn test_userdata_too_small() {
|
||||||
let mut accounts = vec![
|
let mut accounts = vec![
|
||||||
Account::new(1, 0, BudgetProgram::id()),
|
Account::new(1, 0, id()),
|
||||||
Account::new(1, 0, BudgetProgram::id()), // <== userdata is 0, which is not enough
|
Account::new(1, 0, id()), // <== userdata is 0, which is not enough
|
||||||
Account::new(1, 0, BudgetProgram::id()),
|
Account::new(1, 0, id()),
|
||||||
];
|
];
|
||||||
let from = Keypair::new();
|
let from = Keypair::new();
|
||||||
let contract = Keypair::new();
|
let contract = Keypair::new();
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
use bincode::deserialize;
|
use bincode::deserialize;
|
||||||
use budget_expr::{BudgetExpr, Condition};
|
use budget_expr::{BudgetExpr, Condition};
|
||||||
use budget_instruction::Instruction;
|
use budget_instruction::Instruction;
|
||||||
use budget_program::BudgetProgram;
|
use budget_program;
|
||||||
use chrono::prelude::*;
|
use chrono::prelude::*;
|
||||||
use payment_plan::Payment;
|
use payment_plan::Payment;
|
||||||
use signature::{Keypair, KeypairUtil};
|
use signature::{Keypair, KeypairUtil};
|
||||||
@ -85,7 +85,7 @@ impl BudgetTransaction for Transaction {
|
|||||||
};
|
};
|
||||||
let budget_instruction = Instruction::NewBudget(BudgetExpr::Pay(payment));
|
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![
|
let instructions = vec![
|
||||||
transaction::Instruction::new(0, &system_instruction, vec![0, 1]),
|
transaction::Instruction::new(0, &system_instruction, vec![0, 1]),
|
||||||
@ -119,7 +119,7 @@ impl BudgetTransaction for Transaction {
|
|||||||
Self::new(
|
Self::new(
|
||||||
from_keypair,
|
from_keypair,
|
||||||
&[contract, to],
|
&[contract, to],
|
||||||
BudgetProgram::id(),
|
budget_program::id(),
|
||||||
&instruction,
|
&instruction,
|
||||||
last_id,
|
last_id,
|
||||||
0,
|
0,
|
||||||
@ -137,7 +137,7 @@ impl BudgetTransaction for Transaction {
|
|||||||
Self::new(
|
Self::new(
|
||||||
from_keypair,
|
from_keypair,
|
||||||
&[contract, to],
|
&[contract, to],
|
||||||
BudgetProgram::id(),
|
budget_program::id(),
|
||||||
&instruction,
|
&instruction,
|
||||||
last_id,
|
last_id,
|
||||||
0,
|
0,
|
||||||
@ -167,7 +167,7 @@ impl BudgetTransaction for Transaction {
|
|||||||
Self::new(
|
Self::new(
|
||||||
from_keypair,
|
from_keypair,
|
||||||
&[contract],
|
&[contract],
|
||||||
BudgetProgram::id(),
|
budget_program::id(),
|
||||||
&instruction,
|
&instruction,
|
||||||
last_id,
|
last_id,
|
||||||
0,
|
0,
|
||||||
@ -195,7 +195,7 @@ impl BudgetTransaction for Transaction {
|
|||||||
Self::new(
|
Self::new(
|
||||||
from_keypair,
|
from_keypair,
|
||||||
&[contract],
|
&[contract],
|
||||||
BudgetProgram::id(),
|
budget_program::id(),
|
||||||
&instruction,
|
&instruction,
|
||||||
last_id,
|
last_id,
|
||||||
0,
|
0,
|
||||||
|
@ -13,7 +13,7 @@ use std::sync::Arc;
|
|||||||
use std::thread::sleep;
|
use std::thread::sleep;
|
||||||
use std::thread::{self, Builder, JoinHandle};
|
use std::thread::{self, Builder, JoinHandle};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use vote_program::VoteProgram;
|
use vote_program::{self, VoteProgram};
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub enum FinalityError {
|
pub enum FinalityError {
|
||||||
@ -46,7 +46,7 @@ impl ComputeLeaderFinalityService {
|
|||||||
.filter_map(|account| {
|
.filter_map(|account| {
|
||||||
// Filter out any accounts that don't belong to the VoteProgram
|
// Filter out any accounts that don't belong to the VoteProgram
|
||||||
// by returning None
|
// 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) {
|
if let Ok(vote_state) = VoteProgram::deserialize(&account.userdata) {
|
||||||
let validator_stake = bank.get_stake(&vote_state.node_id);
|
let validator_stake = bank.get_stake(&vote_state.node_id);
|
||||||
total_stake += validator_stake;
|
total_stake += validator_stake;
|
||||||
|
@ -14,7 +14,7 @@ use std::collections::HashSet;
|
|||||||
use std::io::Cursor;
|
use std::io::Cursor;
|
||||||
use system_transaction::SystemTransaction;
|
use system_transaction::SystemTransaction;
|
||||||
use transaction::Transaction;
|
use transaction::Transaction;
|
||||||
use vote_program::{Vote, VoteProgram};
|
use vote_program::{self, Vote, VoteProgram};
|
||||||
use vote_transaction::VoteTransaction;
|
use vote_transaction::VoteTransaction;
|
||||||
|
|
||||||
pub const DEFAULT_BOOTSTRAP_HEIGHT: u64 = 1000;
|
pub const DEFAULT_BOOTSTRAP_HEIGHT: u64 = 1000;
|
||||||
@ -309,7 +309,7 @@ impl LeaderScheduler {
|
|||||||
.accounts
|
.accounts
|
||||||
.values()
|
.values()
|
||||||
.filter_map(|account| {
|
.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) {
|
if let Ok(vote_state) = VoteProgram::deserialize(&account.userdata) {
|
||||||
return vote_state
|
return vote_state
|
||||||
.votes
|
.votes
|
||||||
|
@ -109,14 +109,14 @@ mod tests {
|
|||||||
use bincode::deserialize;
|
use bincode::deserialize;
|
||||||
use ledger::Block;
|
use ledger::Block;
|
||||||
use solana_sdk::system_instruction::SystemInstruction;
|
use solana_sdk::system_instruction::SystemInstruction;
|
||||||
use system_program::SystemProgram;
|
use system_program;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_create_transactions() {
|
fn test_create_transactions() {
|
||||||
let mut transactions = Mint::new(100).create_transaction().into_iter();
|
let mut transactions = Mint::new(100).create_transaction().into_iter();
|
||||||
let tx = transactions.next().unwrap();
|
let tx = transactions.next().unwrap();
|
||||||
assert_eq!(tx.instructions.len(), 1);
|
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();
|
let instruction: SystemInstruction = deserialize(tx.userdata(0)).unwrap();
|
||||||
if let SystemInstruction::Move { tokens } = instruction {
|
if let SystemInstruction::Move { tokens } = instruction {
|
||||||
assert_eq!(tokens, 100);
|
assert_eq!(tokens, 100);
|
||||||
@ -133,8 +133,8 @@ mod tests {
|
|||||||
.into_iter();
|
.into_iter();
|
||||||
let tx = transactions.next().unwrap();
|
let tx = transactions.next().unwrap();
|
||||||
assert_eq!(tx.instructions.len(), 2);
|
assert_eq!(tx.instructions.len(), 2);
|
||||||
assert!(SystemProgram::check_id(tx.program_id(0)));
|
assert!(system_program::check_id(tx.program_id(0)));
|
||||||
assert!(SystemProgram::check_id(tx.program_id(1)));
|
assert!(system_program::check_id(tx.program_id(1)));
|
||||||
let instruction: SystemInstruction = deserialize(tx.userdata(0)).unwrap();
|
let instruction: SystemInstruction = deserialize(tx.userdata(0)).unwrap();
|
||||||
if let SystemInstruction::Move { tokens } = instruction {
|
if let SystemInstruction::Move { tokens } = instruction {
|
||||||
assert_eq!(tokens, 100);
|
assert_eq!(tokens, 100);
|
||||||
|
@ -246,7 +246,7 @@ impl RpcSolPubSub for RpcSolPubSubImpl {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use budget_program::BudgetProgram;
|
use budget_program;
|
||||||
use budget_transaction::BudgetTransaction;
|
use budget_transaction::BudgetTransaction;
|
||||||
use jsonrpc_core::futures::sync::mpsc;
|
use jsonrpc_core::futures::sync::mpsc;
|
||||||
use mint::Mint;
|
use mint::Mint;
|
||||||
@ -400,7 +400,7 @@ mod tests {
|
|||||||
let witness = Keypair::new();
|
let witness = Keypair::new();
|
||||||
let contract_funds = Keypair::new();
|
let contract_funds = Keypair::new();
|
||||||
let contract_state = 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 loader = Pubkey::default(); // TODO
|
||||||
let executable = false; // TODO
|
let executable = false; // TODO
|
||||||
let bank = Bank::new(&alice);
|
let bank = Bank::new(&alice);
|
||||||
|
@ -323,13 +323,13 @@ pub fn make_packet_from_transaction(tx: Transaction) -> Packet {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use bincode::serialize;
|
use bincode::serialize;
|
||||||
use budget_program::BudgetProgram;
|
use budget_program;
|
||||||
use packet::{Packet, SharedPackets};
|
use packet::{Packet, SharedPackets};
|
||||||
use signature::{Keypair, KeypairUtil};
|
use signature::{Keypair, KeypairUtil};
|
||||||
use sigverify;
|
use sigverify;
|
||||||
use solana_sdk::hash::Hash;
|
use solana_sdk::hash::Hash;
|
||||||
use solana_sdk::system_instruction::SystemInstruction;
|
use solana_sdk::system_instruction::SystemInstruction;
|
||||||
use system_program::SystemProgram;
|
use system_program;
|
||||||
use system_transaction::{memfind, test_tx};
|
use system_transaction::{memfind, test_tx};
|
||||||
use transaction;
|
use transaction;
|
||||||
use transaction::Transaction;
|
use transaction::Transaction;
|
||||||
@ -429,7 +429,7 @@ mod tests {
|
|||||||
|
|
||||||
let system_instruction = SystemInstruction::Move { tokens };
|
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(
|
let instructions = vec![transaction::Instruction::new(
|
||||||
0,
|
0,
|
||||||
|
@ -22,34 +22,32 @@ const STORAGE_PROGRAM_ID: [u8; 32] = [
|
|||||||
0,
|
0,
|
||||||
];
|
];
|
||||||
|
|
||||||
impl StorageProgram {
|
pub fn check_id(program_id: &Pubkey) -> bool {
|
||||||
pub fn check_id(program_id: &Pubkey) -> bool {
|
program_id.as_ref() == STORAGE_PROGRAM_ID
|
||||||
program_id.as_ref() == STORAGE_PROGRAM_ID
|
}
|
||||||
}
|
|
||||||
|
|
||||||
pub fn id() -> Pubkey {
|
pub fn id() -> Pubkey {
|
||||||
Pubkey::new(&STORAGE_PROGRAM_ID)
|
Pubkey::new(&STORAGE_PROGRAM_ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_balance(account: &Account) -> u64 {
|
pub fn get_balance(account: &Account) -> u64 {
|
||||||
account.tokens
|
account.tokens
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process_transaction(
|
pub fn process_transaction(
|
||||||
tx: &Transaction,
|
tx: &Transaction,
|
||||||
pix: usize,
|
pix: usize,
|
||||||
_accounts: &mut [&mut Account],
|
_accounts: &mut [&mut Account],
|
||||||
) -> Result<(), StorageError> {
|
) -> Result<(), StorageError> {
|
||||||
if let Ok(syscall) = deserialize(tx.userdata(pix)) {
|
if let Ok(syscall) = deserialize(tx.userdata(pix)) {
|
||||||
match syscall {
|
match syscall {
|
||||||
StorageProgram::SubmitMiningProof { sha_state } => {
|
StorageProgram::SubmitMiningProof { sha_state } => {
|
||||||
info!("Mining proof submitted with state {:?}", sha_state);
|
info!("Mining proof submitted with state {:?}", sha_state);
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
return Err(StorageError::InvalidUserData);
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
return Err(StorageError::InvalidUserData);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,14 +60,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_storage_tx() {
|
fn test_storage_tx() {
|
||||||
let keypair = Keypair::new();
|
let keypair = Keypair::new();
|
||||||
let tx = Transaction::new(
|
let tx = Transaction::new(&keypair, &[], id(), &(), Default::default(), 0);
|
||||||
&keypair,
|
assert!(process_transaction(&tx, 0, &mut []).is_err());
|
||||||
&[],
|
|
||||||
StorageProgram::id(),
|
|
||||||
&(),
|
|
||||||
Default::default(),
|
|
||||||
0,
|
|
||||||
);
|
|
||||||
assert!(StorageProgram::process_transaction(&tx, 0, &mut []).is_err());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ use std::sync::mpsc::RecvTimeoutError;
|
|||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
use std::thread::{self, Builder, JoinHandle};
|
use std::thread::{self, Builder, JoinHandle};
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use vote_program::VoteProgram;
|
use vote_program;
|
||||||
|
|
||||||
// Block of hash answers to validate against
|
// Block of hash answers to validate against
|
||||||
// Vec of [ledger blocks] x [keys]
|
// Vec of [ledger blocks] x [keys]
|
||||||
@ -215,7 +215,7 @@ impl StorageStage {
|
|||||||
// the storage_keys with their signatures.
|
// the storage_keys with their signatures.
|
||||||
for tx in entry.transactions {
|
for tx in entry.transactions {
|
||||||
for program_id in tx.program_ids {
|
for program_id in tx.program_ids {
|
||||||
if VoteProgram::check_id(&program_id) {
|
if vote_program::check_id(&program_id) {
|
||||||
debug!(
|
debug!(
|
||||||
"generating storage_keys from votes current_key_idx: {}",
|
"generating storage_keys from votes current_key_idx: {}",
|
||||||
*current_key_idx
|
*current_key_idx
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use signature::{Keypair, KeypairUtil};
|
use signature::{Keypair, KeypairUtil};
|
||||||
use solana_sdk::hash::Hash;
|
use solana_sdk::hash::Hash;
|
||||||
use storage_program::StorageProgram;
|
use storage_program::{self, StorageProgram};
|
||||||
use transaction::Transaction;
|
use transaction::Transaction;
|
||||||
|
|
||||||
pub trait StorageTransaction {
|
pub trait StorageTransaction {
|
||||||
@ -13,7 +13,7 @@ impl StorageTransaction for Transaction {
|
|||||||
Transaction::new(
|
Transaction::new(
|
||||||
from_keypair,
|
from_keypair,
|
||||||
&[from_keypair.pubkey()],
|
&[from_keypair.pubkey()],
|
||||||
StorageProgram::id(),
|
storage_program::id(),
|
||||||
&program,
|
&program,
|
||||||
last_id,
|
last_id,
|
||||||
0,
|
0,
|
||||||
|
@ -23,86 +23,82 @@ impl std::error::Error for Error {}
|
|||||||
|
|
||||||
pub type Result<T> = std::result::Result<T, Error>;
|
pub type Result<T> = std::result::Result<T, Error>;
|
||||||
|
|
||||||
pub struct SystemProgram {}
|
|
||||||
|
|
||||||
pub const SYSTEM_PROGRAM_ID: [u8; 32] = [0u8; 32];
|
pub const SYSTEM_PROGRAM_ID: [u8; 32] = [0u8; 32];
|
||||||
|
|
||||||
impl SystemProgram {
|
pub fn check_id(program_id: &Pubkey) -> bool {
|
||||||
pub fn check_id(program_id: &Pubkey) -> bool {
|
program_id.as_ref() == SYSTEM_PROGRAM_ID
|
||||||
program_id.as_ref() == SYSTEM_PROGRAM_ID
|
}
|
||||||
}
|
|
||||||
|
|
||||||
pub fn id() -> Pubkey {
|
pub fn id() -> Pubkey {
|
||||||
Pubkey::new(&SYSTEM_PROGRAM_ID)
|
Pubkey::new(&SYSTEM_PROGRAM_ID)
|
||||||
}
|
}
|
||||||
pub fn get_balance(account: &Account) -> u64 {
|
pub fn get_balance(account: &Account) -> u64 {
|
||||||
account.tokens
|
account.tokens
|
||||||
}
|
}
|
||||||
pub fn process_transaction(
|
pub fn process_transaction(
|
||||||
tx: &Transaction,
|
tx: &Transaction,
|
||||||
pix: usize,
|
pix: usize,
|
||||||
accounts: &mut [&mut Account],
|
accounts: &mut [&mut Account],
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
if let Ok(syscall) = deserialize(tx.userdata(pix)) {
|
if let Ok(syscall) = deserialize(tx.userdata(pix)) {
|
||||||
trace!("process_transaction: {:?}", syscall);
|
trace!("process_transaction: {:?}", syscall);
|
||||||
match syscall {
|
match syscall {
|
||||||
SystemInstruction::CreateAccount {
|
SystemInstruction::CreateAccount {
|
||||||
tokens,
|
tokens,
|
||||||
space,
|
space,
|
||||||
program_id,
|
program_id,
|
||||||
} => {
|
} => {
|
||||||
if !Self::check_id(&accounts[0].owner) {
|
if !check_id(&accounts[0].owner) {
|
||||||
info!("Invalid account[0] owner");
|
info!("Invalid account[0] owner");
|
||||||
Err(Error::InvalidArgument)?;
|
Err(Error::InvalidArgument)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
if space > 0
|
if space > 0 && (!accounts[1].userdata.is_empty() || !check_id(&accounts[1].owner))
|
||||||
&& (!accounts[1].userdata.is_empty() || !Self::check_id(&accounts[1].owner))
|
{
|
||||||
{
|
info!("Invalid account[1]");
|
||||||
info!("Invalid account[1]");
|
Err(Error::InvalidArgument)?;
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
SystemInstruction::Assign { program_id } => {
|
if tokens > accounts[0].tokens {
|
||||||
if !Self::check_id(&accounts[0].owner) {
|
info!("Insufficient tokens in account[0]");
|
||||||
Err(Error::AssignOfUnownedAccount)?;
|
Err(Error::ResultWithNegativeTokens(pix as u8))?;
|
||||||
}
|
|
||||||
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];
|
|
||||||
}
|
}
|
||||||
|
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)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::*;
|
use super::*;
|
||||||
@ -110,13 +106,12 @@ mod test {
|
|||||||
use solana_sdk::account::Account;
|
use solana_sdk::account::Account;
|
||||||
use solana_sdk::hash::Hash;
|
use solana_sdk::hash::Hash;
|
||||||
use solana_sdk::pubkey::Pubkey;
|
use solana_sdk::pubkey::Pubkey;
|
||||||
use system_program::SystemProgram;
|
|
||||||
use system_transaction::SystemTransaction;
|
use system_transaction::SystemTransaction;
|
||||||
use transaction::Transaction;
|
use transaction::Transaction;
|
||||||
|
|
||||||
fn process_transaction(tx: &Transaction, accounts: &mut [Account]) -> Result<()> {
|
fn process_transaction(tx: &Transaction, accounts: &mut [Account]) -> Result<()> {
|
||||||
let mut refs: Vec<&mut Account> = accounts.iter_mut().collect();
|
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]
|
#[test]
|
||||||
@ -246,7 +241,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_sdk_serialize() {
|
fn test_sdk_serialize() {
|
||||||
let keypair = Keypair::new();
|
let keypair = Keypair::new();
|
||||||
use budget_program::BudgetProgram;
|
use budget_program;
|
||||||
|
|
||||||
// CreateAccount
|
// CreateAccount
|
||||||
let tx = Transaction::system_create(
|
let tx = Transaction::system_create(
|
||||||
@ -255,7 +250,7 @@ mod test {
|
|||||||
Hash::default(),
|
Hash::default(),
|
||||||
111,
|
111,
|
||||||
222,
|
222,
|
||||||
BudgetProgram::id(),
|
budget_program::id(),
|
||||||
0,
|
0,
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -287,7 +282,7 @@ mod test {
|
|||||||
);
|
);
|
||||||
|
|
||||||
// Assign
|
// 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!(
|
assert_eq!(
|
||||||
tx.userdata(0).to_vec(),
|
tx.userdata(0).to_vec(),
|
||||||
vec![
|
vec![
|
||||||
|
@ -4,7 +4,7 @@ use signature::{Keypair, KeypairUtil};
|
|||||||
use solana_sdk::hash::Hash;
|
use solana_sdk::hash::Hash;
|
||||||
use solana_sdk::pubkey::Pubkey;
|
use solana_sdk::pubkey::Pubkey;
|
||||||
use solana_sdk::system_instruction::SystemInstruction;
|
use solana_sdk::system_instruction::SystemInstruction;
|
||||||
use system_program::SystemProgram;
|
use system_program;
|
||||||
|
|
||||||
use transaction::{Instruction, Transaction};
|
use transaction::{Instruction, Transaction};
|
||||||
|
|
||||||
@ -60,7 +60,7 @@ impl SystemTransaction for Transaction {
|
|||||||
Transaction::new(
|
Transaction::new(
|
||||||
from_keypair,
|
from_keypair,
|
||||||
&[to],
|
&[to],
|
||||||
SystemProgram::id(),
|
system_program::id(),
|
||||||
&create,
|
&create,
|
||||||
last_id,
|
last_id,
|
||||||
fee,
|
fee,
|
||||||
@ -72,7 +72,7 @@ impl SystemTransaction for Transaction {
|
|||||||
Transaction::new(
|
Transaction::new(
|
||||||
from_keypair,
|
from_keypair,
|
||||||
&[],
|
&[],
|
||||||
SystemProgram::id(),
|
system_program::id(),
|
||||||
&assign,
|
&assign,
|
||||||
last_id,
|
last_id,
|
||||||
fee,
|
fee,
|
||||||
@ -94,7 +94,7 @@ impl SystemTransaction for Transaction {
|
|||||||
Transaction::new(
|
Transaction::new(
|
||||||
from_keypair,
|
from_keypair,
|
||||||
&[to],
|
&[to],
|
||||||
SystemProgram::id(),
|
system_program::id(),
|
||||||
&move_tokens,
|
&move_tokens,
|
||||||
last_id,
|
last_id,
|
||||||
fee,
|
fee,
|
||||||
@ -116,14 +116,21 @@ impl SystemTransaction for Transaction {
|
|||||||
&to_keys,
|
&to_keys,
|
||||||
last_id,
|
last_id,
|
||||||
fee,
|
fee,
|
||||||
vec![SystemProgram::id()],
|
vec![system_program::id()],
|
||||||
instructions,
|
instructions,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
/// Create and sign new SystemInstruction::Spawn transaction
|
/// Create and sign new SystemInstruction::Spawn transaction
|
||||||
fn system_spawn(from_keypair: &Keypair, last_id: Hash, fee: u64) -> Self {
|
fn system_spawn(from_keypair: &Keypair, last_id: Hash, fee: u64) -> Self {
|
||||||
let spawn = SystemInstruction::Spawn;
|
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,
|
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 {
|
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> {
|
pub fn deserialize(input: &[u8]) -> Result<VoteProgram> {
|
||||||
let len = LittleEndian::read_u16(&input[0..2]) as usize;
|
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);
|
output[2..=serialized_len as usize + 1].clone_from_slice(&self_serialized);
|
||||||
Ok(())
|
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)]
|
#[cfg(test)]
|
||||||
@ -165,7 +165,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_serde() -> Result<()> {
|
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();
|
let mut vote_program = VoteProgram::default();
|
||||||
vote_program.votes = (0..MAX_VOTE_HISTORY).map(|_| Vote::default()).collect();
|
vote_program.votes = (0..MAX_VOTE_HISTORY).map(|_| Vote::default()).collect();
|
||||||
vote_program.serialize(&mut buffer).unwrap();
|
vote_program.serialize(&mut buffer).unwrap();
|
||||||
|
@ -12,7 +12,7 @@ use solana_sdk::hash::Hash;
|
|||||||
use solana_sdk::pubkey::Pubkey;
|
use solana_sdk::pubkey::Pubkey;
|
||||||
use system_transaction::SystemTransaction;
|
use system_transaction::SystemTransaction;
|
||||||
use transaction::Transaction;
|
use transaction::Transaction;
|
||||||
use vote_program::{Vote, VoteInstruction, VoteProgram};
|
use vote_program::{self, Vote, VoteInstruction};
|
||||||
|
|
||||||
pub trait VoteTransaction {
|
pub trait VoteTransaction {
|
||||||
fn vote_new(vote_account: &Keypair, vote: Vote, last_id: Hash, fee: u64) -> Self;
|
fn vote_new(vote_account: &Keypair, vote: Vote, last_id: Hash, fee: u64) -> Self;
|
||||||
@ -37,7 +37,7 @@ impl VoteTransaction for Transaction {
|
|||||||
Transaction::new(
|
Transaction::new(
|
||||||
vote_account,
|
vote_account,
|
||||||
&[],
|
&[],
|
||||||
VoteProgram::id(),
|
vote_program::id(),
|
||||||
&instruction,
|
&instruction,
|
||||||
last_id,
|
last_id,
|
||||||
fee,
|
fee,
|
||||||
@ -55,8 +55,8 @@ impl VoteTransaction for Transaction {
|
|||||||
new_vote_account_id,
|
new_vote_account_id,
|
||||||
last_id,
|
last_id,
|
||||||
num_tokens,
|
num_tokens,
|
||||||
VoteProgram::get_max_size() as u64,
|
vote_program::get_max_size() as u64,
|
||||||
VoteProgram::id(),
|
vote_program::id(),
|
||||||
0,
|
0,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -71,7 +71,7 @@ impl VoteTransaction for Transaction {
|
|||||||
Transaction::new(
|
Transaction::new(
|
||||||
validator_id,
|
validator_id,
|
||||||
&[vote_account_id],
|
&[vote_account_id],
|
||||||
VoteProgram::id(),
|
vote_program::id(),
|
||||||
®ister_tx,
|
®ister_tx,
|
||||||
last_id,
|
last_id,
|
||||||
fee,
|
fee,
|
||||||
@ -82,7 +82,7 @@ impl VoteTransaction for Transaction {
|
|||||||
let mut votes = vec![];
|
let mut votes = vec![];
|
||||||
for i in 0..self.instructions.len() {
|
for i in 0..self.instructions.len() {
|
||||||
let tx_program_id = self.program_id(i);
|
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)) {
|
if let Ok(Some(VoteInstruction::NewVote(vote))) = deserialize(&self.userdata(i)) {
|
||||||
votes.push((self.account_keys[0], vote, self.last_id))
|
votes.push((self.account_keys[0], vote, self.last_id))
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use bincode::serialize;
|
use bincode::serialize;
|
||||||
use bpf_loader;
|
use bpf_loader;
|
||||||
use bs58;
|
use bs58;
|
||||||
use budget_program::BudgetProgram;
|
use budget_program;
|
||||||
use budget_transaction::BudgetTransaction;
|
use budget_transaction::BudgetTransaction;
|
||||||
use chrono::prelude::*;
|
use chrono::prelude::*;
|
||||||
use clap::ArgMatches;
|
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_funds = Keypair::new();
|
||||||
let contract_state = 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
|
// Create account for contract funds
|
||||||
let tx = Transaction::system_create(
|
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_funds = Keypair::new();
|
||||||
let contract_state = 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
|
// Create account for contract funds
|
||||||
let tx = Transaction::system_create(
|
let tx = Transaction::system_create(
|
||||||
|
Reference in New Issue
Block a user