Replace transaction traits with structs

Also:
* SystemTransaction::new -> new_account
* SystemTransaction::new_create -> new_program_account
This commit is contained in:
Greg Fitzgerald
2019-02-01 08:36:35 -07:00
committed by Grimes
parent 1b3e7f734a
commit dad0bfe447
34 changed files with 347 additions and 419 deletions

View File

@ -13,68 +13,17 @@ use crate::transaction::{self, Transaction};
use bincode::deserialize;
use chrono::prelude::*;
pub trait BudgetTransaction {
fn budget_new_taxed(
from_keypair: &Keypair,
to: Pubkey,
tokens: u64,
fee: u64,
last_id: Hash,
) -> Self;
pub struct BudgetTransaction {}
fn budget_new(from_keypair: &Keypair, to: Pubkey, tokens: u64, last_id: Hash) -> Self;
fn budget_new_timestamp(
from_keypair: &Keypair,
contract: Pubkey,
to: Pubkey,
dt: DateTime<Utc>,
last_id: Hash,
) -> Self;
fn budget_new_signature(
from_keypair: &Keypair,
contract: Pubkey,
to: Pubkey,
last_id: Hash,
) -> Self;
fn budget_new_on_date(
from_keypair: &Keypair,
to: Pubkey,
contract: Pubkey,
dt: DateTime<Utc>,
dt_pubkey: Pubkey,
cancelable: Option<Pubkey>,
tokens: u64,
last_id: Hash,
) -> Self;
fn budget_new_when_signed(
from_keypair: &Keypair,
to: Pubkey,
contract: Pubkey,
witness: Pubkey,
cancelable: Option<Pubkey>,
tokens: u64,
last_id: Hash,
) -> Self;
fn instruction(&self, program_index: usize) -> Option<Instruction>;
fn system_instruction(&self, program_index: usize) -> Option<SystemInstruction>;
fn verify_plan(&self) -> bool;
}
impl BudgetTransaction for Transaction {
impl BudgetTransaction {
/// Create and sign a new Transaction. Used for unit-testing.
fn budget_new_taxed(
pub fn new_payment(
from_keypair: &Keypair,
to: Pubkey,
tokens: u64,
fee: u64,
last_id: Hash,
) -> Self {
fee: u64,
) -> Transaction {
let contract = Keypair::new().pubkey();
let keys = vec![from_keypair.pubkey(), contract];
@ -93,7 +42,7 @@ impl BudgetTransaction for Transaction {
transaction::Instruction::new(1, &budget_instruction, vec![1]),
];
Self::new_with_instructions(
Transaction::new_with_instructions(
&[from_keypair],
&keys,
last_id,
@ -104,20 +53,21 @@ impl BudgetTransaction for Transaction {
}
/// Create and sign a new Transaction. Used for unit-testing.
fn budget_new(from_keypair: &Keypair, to: Pubkey, tokens: u64, last_id: Hash) -> Self {
Self::budget_new_taxed(from_keypair, to, tokens, 0, last_id)
#[allow(clippy::new_ret_no_self)]
pub fn new(from_keypair: &Keypair, to: Pubkey, tokens: u64, last_id: Hash) -> Transaction {
Self::new_payment(from_keypair, to, tokens, last_id, 0)
}
/// Create and sign a new Witness Timestamp. Used for unit-testing.
fn budget_new_timestamp(
pub fn new_timestamp(
from_keypair: &Keypair,
contract: Pubkey,
to: Pubkey,
dt: DateTime<Utc>,
last_id: Hash,
) -> Self {
) -> Transaction {
let instruction = Instruction::ApplyTimestamp(dt);
Self::new(
Transaction::new(
from_keypair,
&[contract, to],
budget_program::id(),
@ -128,14 +78,14 @@ impl BudgetTransaction for Transaction {
}
/// Create and sign a new Witness Signature. Used for unit-testing.
fn budget_new_signature(
pub fn new_signature(
from_keypair: &Keypair,
contract: Pubkey,
to: Pubkey,
last_id: Hash,
) -> Self {
) -> Transaction {
let instruction = Instruction::ApplySignature;
Self::new(
Transaction::new(
from_keypair,
&[contract, to],
budget_program::id(),
@ -146,7 +96,7 @@ impl BudgetTransaction for Transaction {
}
/// Create and sign a postdated Transaction. Used for unit-testing.
fn budget_new_on_date(
pub fn new_on_date(
from_keypair: &Keypair,
to: Pubkey,
contract: Pubkey,
@ -155,7 +105,7 @@ impl BudgetTransaction for Transaction {
cancelable: Option<Pubkey>,
tokens: u64,
last_id: Hash,
) -> Self {
) -> Transaction {
let expr = if let Some(from) = cancelable {
BudgetExpr::Or(
(
@ -174,7 +124,7 @@ impl BudgetTransaction for Transaction {
)
};
let instruction = Instruction::NewBudget(expr);
Self::new(
Transaction::new(
from_keypair,
&[contract],
budget_program::id(),
@ -184,7 +134,7 @@ impl BudgetTransaction for Transaction {
)
}
/// Create and sign a multisig Transaction.
fn budget_new_when_signed(
pub fn new_when_signed(
from_keypair: &Keypair,
to: Pubkey,
contract: Pubkey,
@ -192,7 +142,7 @@ impl BudgetTransaction for Transaction {
cancelable: Option<Pubkey>,
tokens: u64,
last_id: Hash,
) -> Self {
) -> Transaction {
let expr = if let Some(from) = cancelable {
BudgetExpr::Or(
(
@ -211,7 +161,7 @@ impl BudgetTransaction for Transaction {
)
};
let instruction = Instruction::NewBudget(expr);
Self::new(
Transaction::new(
from_keypair,
&[contract],
budget_program::id(),
@ -221,19 +171,19 @@ impl BudgetTransaction for Transaction {
)
}
fn instruction(&self, instruction_index: usize) -> Option<Instruction> {
deserialize(&self.userdata(instruction_index)).ok()
pub fn system_instruction(tx: &Transaction, index: usize) -> Option<SystemInstruction> {
deserialize(&tx.userdata(index)).ok()
}
fn system_instruction(&self, instruction_index: usize) -> Option<SystemInstruction> {
deserialize(&self.userdata(instruction_index)).ok()
pub fn instruction(tx: &Transaction, index: usize) -> Option<Instruction> {
deserialize(&tx.userdata(index)).ok()
}
/// Verify only the payment plan.
fn verify_plan(&self) -> bool {
if let Some(SystemInstruction::Move { tokens }) = self.system_instruction(0) {
if let Some(Instruction::NewBudget(expr)) = self.instruction(1) {
if !(self.fee <= tokens && expr.verify(tokens - self.fee)) {
pub fn verify_plan(tx: &Transaction) -> bool {
if let Some(SystemInstruction::Move { tokens }) = Self::system_instruction(tx, 0) {
if let Some(Instruction::NewBudget(expr)) = BudgetTransaction::instruction(&tx, 1) {
if !(tx.fee <= tokens && expr.verify(tokens - tx.fee)) {
return false;
}
}
@ -251,26 +201,27 @@ mod tests {
fn test_claim() {
let keypair = Keypair::new();
let zero = Hash::default();
let tx0 = Transaction::budget_new(&keypair, keypair.pubkey(), 42, zero);
assert!(tx0.verify_plan());
let tx0 = BudgetTransaction::new(&keypair, keypair.pubkey(), 42, zero);
assert!(BudgetTransaction::verify_plan(&tx0));
}
#[test]
fn test_transfer() {
fn test_payment() {
let zero = Hash::default();
let keypair0 = Keypair::new();
let keypair1 = Keypair::new();
let pubkey1 = keypair1.pubkey();
let tx0 = Transaction::budget_new(&keypair0, pubkey1, 42, zero);
assert!(tx0.verify_plan());
let tx0 = BudgetTransaction::new(&keypair0, pubkey1, 42, zero);
assert!(BudgetTransaction::verify_plan(&tx0));
}
#[test]
fn test_transfer_with_fee() {
fn test_payment_with_fee() {
let zero = Hash::default();
let keypair0 = Keypair::new();
let pubkey1 = Keypair::new().pubkey();
assert!(Transaction::budget_new_taxed(&keypair0, pubkey1, 1, 1, zero).verify_plan());
let tx0 = BudgetTransaction::new_payment(&keypair0, pubkey1, 1, zero, 1);
assert!(BudgetTransaction::verify_plan(&tx0));
}
#[test]
@ -299,11 +250,11 @@ mod tests {
let zero = Hash::default();
let keypair = Keypair::new();
let pubkey = keypair.pubkey();
let mut tx = Transaction::budget_new(&keypair, pubkey, 42, zero);
let mut system_instruction = tx.system_instruction(0).unwrap();
let mut tx = BudgetTransaction::new(&keypair, pubkey, 42, zero);
let mut system_instruction = BudgetTransaction::system_instruction(&tx, 0).unwrap();
if let SystemInstruction::Move { ref mut tokens } = system_instruction {
*tokens = 1_000_000; // <-- attack, part 1!
let mut instruction = tx.instruction(1).unwrap();
let mut instruction = BudgetTransaction::instruction(&tx, 1).unwrap();
if let Instruction::NewBudget(ref mut expr) = instruction {
if let BudgetExpr::Pay(ref mut payment) = expr {
payment.tokens = *tokens; // <-- attack, part 2!
@ -312,7 +263,7 @@ mod tests {
tx.instructions[1].userdata = serialize(&instruction).unwrap();
}
tx.instructions[0].userdata = serialize(&system_instruction).unwrap();
assert!(tx.verify_plan());
assert!(BudgetTransaction::verify_plan(&tx));
assert!(!tx.verify_signature());
}
@ -323,15 +274,15 @@ mod tests {
let thief_keypair = Keypair::new();
let pubkey1 = keypair1.pubkey();
let zero = Hash::default();
let mut tx = Transaction::budget_new(&keypair0, pubkey1, 42, zero);
let mut instruction = tx.instruction(1);
let mut tx = BudgetTransaction::new(&keypair0, pubkey1, 42, zero);
let mut instruction = BudgetTransaction::instruction(&tx, 1);
if let Some(Instruction::NewBudget(ref mut expr)) = instruction {
if let BudgetExpr::Pay(ref mut payment) = expr {
payment.to = thief_keypair.pubkey(); // <-- attack!
}
}
tx.instructions[1].userdata = serialize(&instruction).unwrap();
assert!(tx.verify_plan());
assert!(BudgetTransaction::verify_plan(&tx));
assert!(!tx.verify_signature());
}
@ -340,24 +291,24 @@ mod tests {
let keypair0 = Keypair::new();
let keypair1 = Keypair::new();
let zero = Hash::default();
let mut tx = Transaction::budget_new(&keypair0, keypair1.pubkey(), 1, zero);
let mut instruction = tx.instruction(1).unwrap();
let mut tx = BudgetTransaction::new(&keypair0, keypair1.pubkey(), 1, zero);
let mut instruction = BudgetTransaction::instruction(&tx, 1).unwrap();
if let Instruction::NewBudget(ref mut expr) = instruction {
if let BudgetExpr::Pay(ref mut payment) = expr {
payment.tokens = 2; // <-- attack!
}
}
tx.instructions[1].userdata = serialize(&instruction).unwrap();
assert!(!tx.verify_plan());
assert!(!BudgetTransaction::verify_plan(&tx));
// Also, ensure all branchs of the plan spend all tokens
let mut instruction = tx.instruction(1).unwrap();
let mut instruction = BudgetTransaction::instruction(&tx, 1).unwrap();
if let Instruction::NewBudget(ref mut expr) = instruction {
if let BudgetExpr::Pay(ref mut payment) = expr {
payment.tokens = 0; // <-- whoops!
}
}
tx.instructions[1].userdata = serialize(&instruction).unwrap();
assert!(!tx.verify_plan());
assert!(!BudgetTransaction::verify_plan(&tx));
}
}

View File

@ -6,33 +6,27 @@ use crate::pubkey::Pubkey;
use crate::signature::Keypair;
use crate::transaction::Transaction;
pub trait LoaderTransaction {
fn loader_write(
pub struct LoaderTransaction {}
impl LoaderTransaction {
pub fn new_write(
from_keypair: &Keypair,
loader: Pubkey,
offset: u32,
bytes: Vec<u8>,
last_id: Hash,
fee: u64,
) -> Self;
fn loader_finalize(from_keypair: &Keypair, loader: Pubkey, last_id: Hash, fee: u64) -> Self;
}
impl LoaderTransaction for Transaction {
fn loader_write(
from_keypair: &Keypair,
loader: Pubkey,
offset: u32,
bytes: Vec<u8>,
last_id: Hash,
fee: u64,
) -> Self {
) -> Transaction {
let instruction = LoaderInstruction::Write { offset, bytes };
Transaction::new(from_keypair, &[], loader, &instruction, last_id, fee)
}
fn loader_finalize(from_keypair: &Keypair, loader: Pubkey, last_id: Hash, fee: u64) -> Self {
pub fn new_finalize(
from_keypair: &Keypair,
loader: Pubkey,
last_id: Hash,
fee: u64,
) -> Transaction {
let instruction = LoaderInstruction::Finalize;
Transaction::new(from_keypair, &[], loader, &instruction, last_id, fee)
}

View File

@ -83,40 +83,16 @@ pub fn system_id() -> Pubkey {
Pubkey::new(&STORAGE_SYSTEM_ACCOUNT_ID)
}
pub trait StorageTransaction {
fn storage_new_mining_proof(
pub struct StorageTransaction {}
impl StorageTransaction {
pub fn new_mining_proof(
from_keypair: &Keypair,
sha_state: Hash,
last_id: Hash,
entry_height: u64,
signature: Signature,
) -> Self;
fn storage_new_advertise_last_id(
from_keypair: &Keypair,
storage_last_id: Hash,
last_id: Hash,
entry_height: u64,
) -> Self;
fn storage_new_proof_validation(
from_keypair: &Keypair,
last_id: Hash,
entry_height: u64,
proof_mask: Vec<ProofStatus>,
) -> Self;
fn storage_new_reward_claim(from_keypair: &Keypair, last_id: Hash, entry_height: u64) -> Self;
}
impl StorageTransaction for Transaction {
fn storage_new_mining_proof(
from_keypair: &Keypair,
sha_state: Hash,
last_id: Hash,
entry_height: u64,
signature: Signature,
) -> Self {
) -> Transaction {
let program = StorageProgram::SubmitMiningProof {
sha_state,
entry_height,
@ -132,12 +108,12 @@ impl StorageTransaction for Transaction {
)
}
fn storage_new_advertise_last_id(
pub fn new_advertise_last_id(
from_keypair: &Keypair,
storage_id: Hash,
last_id: Hash,
entry_height: u64,
) -> Self {
) -> Transaction {
let program = StorageProgram::AdvertiseStorageLastId {
id: storage_id,
entry_height,
@ -152,12 +128,12 @@ impl StorageTransaction for Transaction {
)
}
fn storage_new_proof_validation(
pub fn new_proof_validation(
from_keypair: &Keypair,
last_id: Hash,
entry_height: u64,
proof_mask: Vec<ProofStatus>,
) -> Self {
) -> Transaction {
let program = StorageProgram::ProofValidation {
entry_height,
proof_mask,
@ -172,7 +148,11 @@ impl StorageTransaction for Transaction {
)
}
fn storage_new_reward_claim(from_keypair: &Keypair, last_id: Hash, entry_height: u64) -> Self {
pub fn new_reward_claim(
from_keypair: &Keypair,
last_id: Hash,
entry_height: u64,
) -> Transaction {
let program = StorageProgram::ClaimStorageReward { entry_height };
Transaction::new(
from_keypair,

View File

@ -7,42 +7,11 @@ use crate::system_instruction::SystemInstruction;
use crate::system_program;
use crate::transaction::{Instruction, Transaction};
pub trait SystemTransaction {
fn system_create(
from_keypair: &Keypair,
to: Pubkey,
last_id: Hash,
tokens: u64,
space: u64,
program_id: Pubkey,
fee: u64,
) -> Self;
pub struct SystemTransaction {}
fn system_assign(from_keypair: &Keypair, last_id: Hash, program_id: Pubkey, fee: u64) -> Self;
fn system_new(from_keypair: &Keypair, to: Pubkey, tokens: u64, last_id: Hash) -> Self;
fn system_move(
from_keypair: &Keypair,
to: Pubkey,
tokens: u64,
last_id: Hash,
fee: u64,
) -> Self;
fn system_move_many(
from_keypair: &Keypair,
moves: &[(Pubkey, u64)],
last_id: Hash,
fee: u64,
) -> Self;
fn system_spawn(from_keypair: &Keypair, last_id: Hash, fee: u64) -> Self;
}
impl SystemTransaction for Transaction {
impl SystemTransaction {
/// Create and sign new SystemInstruction::CreateAccount transaction
fn system_create(
pub fn new_program_account(
from_keypair: &Keypair,
to: Pubkey,
last_id: Hash,
@ -50,7 +19,7 @@ impl SystemTransaction for Transaction {
space: u64,
program_id: Pubkey,
fee: u64,
) -> Self {
) -> Transaction {
let create = SystemInstruction::CreateAccount {
tokens, //TODO, the tokens to allocate might need to be higher then 0 in the future
space,
@ -65,8 +34,25 @@ impl SystemTransaction for Transaction {
fee,
)
}
/// Create and sign a transaction to create a system account
pub fn new_account(
from_keypair: &Keypair,
to: Pubkey,
tokens: u64,
last_id: Hash,
fee: u64,
) -> Transaction {
let program_id = system_program::id();
Self::new_program_account(from_keypair, to, last_id, tokens, 0, program_id, fee)
}
/// Create and sign new SystemInstruction::Assign transaction
fn system_assign(from_keypair: &Keypair, last_id: Hash, program_id: Pubkey, fee: u64) -> Self {
pub fn new_assign(
from_keypair: &Keypair,
last_id: Hash,
program_id: Pubkey,
fee: u64,
) -> Transaction {
let assign = SystemInstruction::Assign { program_id };
Transaction::new(
from_keypair,
@ -77,18 +63,14 @@ impl SystemTransaction for Transaction {
fee,
)
}
/// Create and sign new SystemInstruction::CreateAccount transaction with some defaults
fn system_new(from_keypair: &Keypair, to: Pubkey, tokens: u64, last_id: Hash) -> Self {
Transaction::system_create(from_keypair, to, last_id, tokens, 0, Pubkey::default(), 0)
}
/// Create and sign new SystemInstruction::Move transaction
fn system_move(
pub fn new_move(
from_keypair: &Keypair,
to: Pubkey,
tokens: u64,
last_id: Hash,
fee: u64,
) -> Self {
) -> Transaction {
let move_tokens = SystemInstruction::Move { tokens };
Transaction::new(
from_keypair,
@ -100,7 +82,12 @@ impl SystemTransaction for Transaction {
)
}
/// Create and sign new SystemInstruction::Move transaction to many destinations
fn system_move_many(from: &Keypair, moves: &[(Pubkey, u64)], last_id: Hash, fee: u64) -> Self {
pub fn new_move_many(
from: &Keypair,
moves: &[(Pubkey, u64)],
last_id: Hash,
fee: u64,
) -> Transaction {
let instructions: Vec<_> = moves
.iter()
.enumerate()
@ -121,7 +108,7 @@ impl SystemTransaction for Transaction {
)
}
/// Create and sign new SystemInstruction::Spawn transaction
fn system_spawn(from_keypair: &Keypair, last_id: Hash, fee: u64) -> Self {
pub fn new_spawn(from_keypair: &Keypair, last_id: Hash, fee: u64) -> Transaction {
let spawn = SystemInstruction::Spawn;
Transaction::new(
from_keypair,
@ -146,7 +133,7 @@ mod tests {
let t2 = Keypair::new();
let moves = vec![(t1.pubkey(), 1), (t2.pubkey(), 2)];
let tx = Transaction::system_move_many(&from, &moves, Default::default(), 0);
let tx = SystemTransaction::new_move_many(&from, &moves, Default::default(), 0);
assert_eq!(tx.account_keys[0], from.pubkey());
assert_eq!(tx.account_keys[1], t1.pubkey());
assert_eq!(tx.account_keys[2], t2.pubkey());

View File

@ -174,6 +174,7 @@ impl Transaction {
pub fn userdata(&self, instruction_index: usize) -> &[u8] {
&self.instructions[instruction_index].userdata
}
fn key_index(&self, instruction_index: usize, accounts_index: usize) -> Option<usize> {
self.instructions
.get(instruction_index)

View File

@ -9,31 +9,15 @@ use crate::transaction::{Instruction, Transaction};
use crate::vote_program::{self, Vote, VoteInstruction};
use bincode::deserialize;
pub trait VoteTransaction {
fn vote_new<T: KeypairUtil>(
pub struct VoteTransaction {}
impl VoteTransaction {
pub fn new_vote<T: KeypairUtil>(
vote_account: &T,
tick_height: u64,
last_id: Hash,
fee: u64,
) -> Self;
fn vote_account_new(
validator_id: &Keypair,
vote_account_id: Pubkey,
last_id: Hash,
num_tokens: u64,
fee: u64,
) -> Self;
fn get_votes(&self) -> Vec<(Pubkey, Vote, Hash)>;
}
impl VoteTransaction for Transaction {
fn vote_new<T: KeypairUtil>(
vote_account: &T,
tick_height: u64,
last_id: Hash,
fee: u64,
) -> Self {
) -> Transaction {
let vote = Vote { tick_height };
let instruction = VoteInstruction::NewVote(vote);
Transaction::new(
@ -46,13 +30,13 @@ impl VoteTransaction for Transaction {
)
}
fn vote_account_new(
pub fn new_account(
validator_id: &Keypair,
vote_account_id: Pubkey,
last_id: Hash,
num_tokens: u64,
fee: u64,
) -> Self {
) -> Transaction {
Transaction::new_with_instructions(
&[validator_id],
&[vote_account_id],
@ -74,13 +58,13 @@ impl VoteTransaction for Transaction {
)
}
fn get_votes(&self) -> Vec<(Pubkey, Vote, Hash)> {
pub fn get_votes(tx: &Transaction) -> Vec<(Pubkey, Vote, Hash)> {
let mut votes = vec![];
for i in 0..self.instructions.len() {
let tx_program_id = self.program_id(i);
for i in 0..tx.instructions.len() {
let tx_program_id = tx.program_id(i);
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))
if let Ok(Some(VoteInstruction::NewVote(vote))) = deserialize(&tx.userdata(i)) {
votes.push((tx.account_keys[0], vote, tx.last_id))
}
}
}