From cc907ba69dceff2b37c780a26388bfca6d735d42 Mon Sep 17 00:00:00 2001 From: Greg Fitzgerald Date: Tue, 22 May 2018 21:42:04 -0600 Subject: [PATCH] Add Instruction type --- src/bank.rs | 66 ++++++++++++++++++++++++++++----------------- src/bin/testnode.rs | 7 ++++- src/mint.rs | 7 +++-- src/plan.rs | 1 + src/thin_client.rs | 7 +++-- src/transaction.rs | 56 ++++++++++++++++++++++++++------------ 6 files changed, 97 insertions(+), 47 deletions(-) diff --git a/src/bank.rs b/src/bank.rs index 3a7c7540e8..2b1fe113cc 100644 --- a/src/bank.rs +++ b/src/bank.rs @@ -18,7 +18,7 @@ use std::collections::{HashMap, HashSet, VecDeque}; use std::result; use std::sync::RwLock; use std::sync::atomic::{AtomicIsize, AtomicUsize, Ordering}; -use transaction::Transaction; +use transaction::{Instruction, Transaction}; pub const MAX_ENTRY_IDS: usize = 1024 * 4; @@ -160,7 +160,9 @@ impl Bank { /// Deduct tokens from the 'from' address the account has sufficient /// funds and isn't a duplicate. pub fn process_verified_transaction_debits(&self, tr: &Transaction) -> Result<()> { - info!("Transaction {}", tr.contract.tokens); + if let Instruction::NewContract(contract) = &tr.instruction { + info!("Transaction {}", contract.tokens); + } let bals = self.balances .read() .expect("'balances' read lock in process_verified_transaction_debits"); @@ -175,20 +177,24 @@ impl Bank { } loop { - let bal = option.expect("assignment of option to bal"); - let current = bal.load(Ordering::Relaxed) as i64; + let result = if let Instruction::NewContract(contract) = &tr.instruction { + let bal = option.expect("assignment of option to bal"); + let current = bal.load(Ordering::Relaxed) as i64; - if current < tr.contract.tokens { - self.forget_signature_with_last_id(&tr.sig, &tr.last_id); - return Err(BankError::InsufficientFunds(tr.from)); - } + if current < contract.tokens { + self.forget_signature_with_last_id(&tr.sig, &tr.last_id); + return Err(BankError::InsufficientFunds(tr.from)); + } - let result = bal.compare_exchange( - current as isize, - (current - tr.contract.tokens) as isize, - Ordering::Relaxed, - Ordering::Relaxed, - ); + bal.compare_exchange( + current as isize, + (current - contract.tokens) as isize, + Ordering::Relaxed, + Ordering::Relaxed, + ) + } else { + Ok(0) + }; match result { Ok(_) => { @@ -201,18 +207,28 @@ impl Bank { } pub fn process_verified_transaction_credits(&self, tr: &Transaction) { - let mut plan = tr.contract.plan.clone(); - plan.apply_witness(&Witness::Timestamp(*self.last_time - .read() - .expect("timestamp creation in process_verified_transaction_credits"))); + match &tr.instruction { + Instruction::NewContract(contract) => { + let mut plan = contract.plan.clone(); + plan.apply_witness(&Witness::Timestamp(*self.last_time + .read() + .expect("timestamp creation in process_verified_transaction_credits"))); - if let Some(ref payment) = plan.final_payment() { - apply_payment(&self.balances, payment); - } else { - let mut pending = self.pending - .write() - .expect("'pending' write lock in process_verified_transaction_credits"); - pending.insert(tr.sig, plan); + if let Some(ref payment) = plan.final_payment() { + apply_payment(&self.balances, payment); + } else { + let mut pending = self.pending + .write() + .expect("'pending' write lock in process_verified_transaction_credits"); + pending.insert(tr.sig, plan); + } + } + Instruction::ApplyTimestamp(dt) => { + let _ = self.process_verified_timestamp(tr.from, *dt); + } + Instruction::ApplySignature(tx_sig) => { + let _ = self.process_verified_sig(tr.from, *tx_sig); + } } } diff --git a/src/bin/testnode.rs b/src/bin/testnode.rs index dcf52c82a3..fb7717cf5a 100644 --- a/src/bin/testnode.rs +++ b/src/bin/testnode.rs @@ -14,6 +14,7 @@ use solana::entry::Entry; use solana::event::Event; use solana::server::Server; use solana::signature::{KeyPair, KeyPairUtil}; +use solana::transaction::Instruction; use std::env; use std::fs::File; use std::io::{stdin, stdout, Read}; @@ -97,7 +98,11 @@ fn main() { // transfer to oneself. let entry1: Entry = entries.next().unwrap(); let deposit = if let Event::Transaction(ref tr) = entry1.events[0] { - tr.contract.plan.final_payment() + if let Instruction::NewContract(contract) = &tr.instruction { + contract.plan.final_payment() + } else { + None + } } else { None }; diff --git a/src/mint.rs b/src/mint.rs index 8b1cb2ed9c..cac64f1b46 100644 --- a/src/mint.rs +++ b/src/mint.rs @@ -71,13 +71,16 @@ mod tests { use super::*; use ledger::Block; use plan::Plan; + use transaction::Instruction; #[test] fn test_create_events() { let mut events = Mint::new(100).create_events().into_iter(); if let Event::Transaction(tr) = events.next().unwrap() { - if let Plan::Pay(payment) = tr.contract.plan { - assert_eq!(tr.from, payment.to); + if let Instruction::NewContract(contract) = tr.instruction { + if let Plan::Pay(payment) = contract.plan { + assert_eq!(tr.from, payment.to); + } } } assert_eq!(events.next(), None); diff --git a/src/plan.rs b/src/plan.rs index 72aa41f1c8..12a7023606 100644 --- a/src/plan.rs +++ b/src/plan.rs @@ -7,6 +7,7 @@ use chrono::prelude::*; use signature::PublicKey; use std::mem; +#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] pub enum Witness { Timestamp(DateTime), Signature(PublicKey), diff --git a/src/thin_client.rs b/src/thin_client.rs index c2ca17d9f0..d6065aad23 100644 --- a/src/thin_client.rs +++ b/src/thin_client.rs @@ -192,6 +192,7 @@ mod tests { use std::thread::sleep; use std::time::Duration; use streamer::default_window; + use transaction::Instruction; use tvu::tests::TestNode; #[test] @@ -284,8 +285,10 @@ mod tests { let last_id = client.get_last_id().wait().unwrap(); let mut tr2 = Transaction::new(&alice.keypair(), bob_pubkey, 501, last_id); - tr2.contract.tokens = 502; - tr2.contract.plan = Plan::new_payment(502, bob_pubkey); + if let Instruction::NewContract(contract) = &mut tr2.instruction { + contract.tokens = 502; + contract.plan = Plan::new_payment(502, bob_pubkey); + } let _sig = client.transfer_signed(tr2).unwrap(); let balance = poll_get_balance(&mut client, &bob_pubkey); diff --git a/src/transaction.rs b/src/transaction.rs index 0c08d032b0..7b3f9c85dc 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -17,11 +17,18 @@ pub struct Contract { pub plan: Plan, } +#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] +pub enum Instruction { + NewContract(Contract), + ApplyTimestamp(DateTime), + ApplySignature(Signature), +} + #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] pub struct Transaction { pub sig: Signature, pub from: PublicKey, - pub contract: Contract, + pub instruction: Instruction, pub last_id: Hash, } @@ -30,9 +37,10 @@ impl Transaction { pub fn new(from_keypair: &KeyPair, to: PublicKey, tokens: i64, last_id: Hash) -> Self { let from = from_keypair.pubkey(); let plan = Plan::Pay(Payment { tokens, to }); + let instruction = Instruction::NewContract(Contract { plan, tokens }); let mut tr = Transaction { sig: Signature::default(), - contract: Contract { plan, tokens }, + instruction, last_id, from, }; @@ -53,8 +61,9 @@ impl Transaction { (Condition::Timestamp(dt), Payment { tokens, to }), (Condition::Signature(from), Payment { tokens, to: from }), ); + let instruction = Instruction::NewContract(Contract { plan, tokens }); let mut tr = Transaction { - contract: Contract { plan, tokens }, + instruction, from, last_id, sig: Signature::default(), @@ -64,7 +73,7 @@ impl Transaction { } fn get_sign_data(&self) -> Vec { - let mut data = serialize(&(&self.contract)).expect("serialize Contract"); + let mut data = serialize(&(&self.instruction)).expect("serialize Contract"); let last_id_data = serialize(&(&self.last_id)).expect("serialize last_id"); data.extend_from_slice(&last_id_data); data @@ -81,7 +90,11 @@ impl Transaction { } pub fn verify_plan(&self) -> bool { - self.contract.plan.verify(self.contract.tokens) + if let Instruction::NewContract(contract) = &self.instruction { + contract.plan.verify(contract.tokens) + } else { + true + } } } @@ -149,8 +162,9 @@ mod tests { tokens: 0, to: Default::default(), }); + let instruction = Instruction::NewContract(Contract { plan, tokens: 0 }); let claim0 = Transaction { - contract: Contract { plan, tokens: 0 }, + instruction, from: Default::default(), last_id: Default::default(), sig: Default::default(), @@ -166,10 +180,12 @@ mod tests { let keypair = KeyPair::new(); let pubkey = keypair.pubkey(); let mut tr = Transaction::new(&keypair, pubkey, 42, zero); - tr.contract.tokens = 1_000_000; // <-- attack, part 1! - if let Plan::Pay(ref mut payment) = tr.contract.plan { - payment.tokens = tr.contract.tokens; // <-- attack, part 2! - }; + if let Instruction::NewContract(contract) = &mut tr.instruction { + contract.tokens = 1_000_000; // <-- attack, part 1! + if let Plan::Pay(ref mut payment) = contract.plan { + payment.tokens = contract.tokens; // <-- attack, part 2! + } + } assert!(tr.verify_plan()); assert!(!tr.verify_sig()); } @@ -182,9 +198,11 @@ mod tests { let pubkey1 = keypair1.pubkey(); let zero = Hash::default(); let mut tr = Transaction::new(&keypair0, pubkey1, 42, zero); - if let Plan::Pay(ref mut payment) = tr.contract.plan { - payment.to = thief_keypair.pubkey(); // <-- attack! - }; + if let Instruction::NewContract(contract) = &mut tr.instruction { + if let Plan::Pay(ref mut payment) = contract.plan { + payment.to = thief_keypair.pubkey(); // <-- attack! + } + } assert!(tr.verify_plan()); assert!(!tr.verify_sig()); } @@ -204,14 +222,18 @@ mod tests { let keypair1 = KeyPair::new(); let zero = Hash::default(); let mut tr = Transaction::new(&keypair0, keypair1.pubkey(), 1, zero); - if let Plan::Pay(ref mut payment) = tr.contract.plan { - payment.tokens = 2; // <-- attack! + if let Instruction::NewContract(contract) = &mut tr.instruction { + if let Plan::Pay(ref mut payment) = contract.plan { + payment.tokens = 2; // <-- attack! + } } assert!(!tr.verify_plan()); // Also, ensure all branchs of the plan spend all tokens - if let Plan::Pay(ref mut payment) = tr.contract.plan { - payment.tokens = 0; // <-- whoops! + if let Instruction::NewContract(contract) = &mut tr.instruction { + if let Plan::Pay(ref mut payment) = contract.plan { + payment.tokens = 0; // <-- whoops! + } } assert!(!tr.verify_plan()); }