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