Specialize transaction assets to i64

Proof-of-history is generic, but now that we're using it entirely
for tokens, we can specialize the type and start doing more interesting
things than just Eq and Serialize operations.
This commit is contained in:
Greg Fitzgerald
2018-03-17 14:42:50 -06:00
parent e7da083c31
commit e5bae0604b
6 changed files with 35 additions and 40 deletions

View File

@ -31,7 +31,7 @@ pub struct Accountant {
pub balances: HashMap<PublicKey, i64>, pub balances: HashMap<PublicKey, i64>,
pub first_id: Hash, pub first_id: Hash,
pub last_id: Hash, pub last_id: Hash,
pending: HashMap<Signature, Plan<i64>>, pending: HashMap<Signature, Plan>,
time_sources: HashSet<PublicKey>, time_sources: HashSet<PublicKey>,
last_time: DateTime<Utc>, last_time: DateTime<Utc>,
} }
@ -84,7 +84,7 @@ impl Accountant {
self.last_id self.last_id
} }
fn is_deposit(allow_deposits: bool, from: &PublicKey, plan: &Plan<i64>) -> bool { fn is_deposit(allow_deposits: bool, from: &PublicKey, plan: &Plan) -> bool {
if let Plan::Action(Action::Pay(ref payment)) = *plan { if let Plan::Action(Action::Pay(ref payment)) = *plan {
allow_deposits && *from == payment.to allow_deposits && *from == payment.to
} else { } else {
@ -92,7 +92,7 @@ impl Accountant {
} }
} }
pub fn process_transaction(self: &mut Self, tr: Transaction<i64>) -> Result<()> { pub fn process_transaction(self: &mut Self, tr: Transaction) -> Result<()> {
if !tr.verify() { if !tr.verify() {
return Err(AccountingError::InvalidTransfer); return Err(AccountingError::InvalidTransfer);
} }
@ -113,7 +113,7 @@ impl Accountant {
} }
/// Commit funds to the 'to' party. /// Commit funds to the 'to' party.
fn complete_transaction(self: &mut Self, plan: &Plan<i64>) { fn complete_transaction(self: &mut Self, plan: &Plan) {
if let Plan::Action(Action::Pay(ref payment)) = *plan { if let Plan::Action(Action::Pay(ref payment)) = *plan {
if self.balances.contains_key(&payment.to) { if self.balances.contains_key(&payment.to) {
if let Some(x) = self.balances.get_mut(&payment.to) { if let Some(x) = self.balances.get_mut(&payment.to) {
@ -127,7 +127,7 @@ impl Accountant {
fn process_verified_transaction( fn process_verified_transaction(
self: &mut Self, self: &mut Self,
tr: &Transaction<i64>, tr: &Transaction,
allow_deposits: bool, allow_deposits: bool,
) -> Result<()> { ) -> Result<()> {
if !reserve_signature(&mut self.historian.signatures, &tr.sig) { if !reserve_signature(&mut self.historian.signatures, &tr.sig) {

View File

@ -19,7 +19,7 @@ pub struct AccountantSkel {
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
pub enum Request { pub enum Request {
Transaction(Transaction<i64>), Transaction(Transaction),
GetBalance { key: PublicKey }, GetBalance { key: PublicKey },
GetEntries { last_id: Hash }, GetEntries { last_id: Hash },
GetId { is_last: bool }, GetId { is_last: bool },

View File

@ -26,7 +26,7 @@ impl AccountantStub {
} }
} }
pub fn transfer_signed(&self, tr: Transaction<i64>) -> io::Result<usize> { pub fn transfer_signed(&self, tr: Transaction) -> io::Result<usize> {
let req = Request::Transaction(tr); let req = Request::Transaction(tr);
let data = serialize(&req).unwrap(); let data = serialize(&req).unwrap();
self.socket.send_to(&data, &self.addr) self.socket.send_to(&data, &self.addr)

View File

@ -7,7 +7,7 @@ use bincode::serialize;
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum Event { pub enum Event {
Transaction(Transaction<i64>), Transaction(Transaction),
Signature { Signature {
from: PublicKey, from: PublicKey,
tx_sig: Signature, tx_sig: Signature,

View File

@ -11,12 +11,12 @@ pub enum Condition {
} }
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum Action<T> { pub enum Action {
Pay(Payment<T>), Pay(Payment),
} }
impl<T: Clone> Action<T> { impl Action {
pub fn spendable(&self) -> T { pub fn spendable(&self) -> i64 {
match *self { match *self {
Action::Pay(ref payment) => payment.asset.clone(), Action::Pay(ref payment) => payment.asset.clone(),
} }
@ -24,22 +24,22 @@ impl<T: Clone> Action<T> {
} }
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct Payment<T> { pub struct Payment {
pub asset: T, pub asset: i64,
pub to: PublicKey, pub to: PublicKey,
} }
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub enum Plan<T> { pub enum Plan {
Action(Action<T>), Action(Action),
After(Condition, Box<Plan<T>>), After(Condition, Box<Plan>),
Race(Box<Plan<T>>, Box<Plan<T>>), Race(Box<Plan>, Box<Plan>),
} }
impl<T: Clone + Eq> Plan<T> { impl Plan {
pub fn verify(&self, spendable_assets: &T) -> bool { pub fn verify(&self, spendable_assets: i64) -> bool {
match *self { match *self {
Plan::Action(ref action) => action.spendable() == *spendable_assets, Plan::Action(ref action) => action.spendable() == spendable_assets,
Plan::Race(ref plan_a, ref plan_b) => { Plan::Race(ref plan_a, ref plan_b) => {
plan_a.verify(spendable_assets) && plan_b.verify(spendable_assets) plan_a.verify(spendable_assets) && plan_b.verify(spendable_assets)
} }

View File

@ -1,23 +1,22 @@
//! The `transaction` crate provides functionality for creating log transactions. //! The `transaction` crate provides functionality for creating log transactions.
use signature::{KeyPair, KeyPairUtil, PublicKey, Signature, SignatureUtil}; use signature::{KeyPair, KeyPairUtil, PublicKey, Signature, SignatureUtil};
use serde::Serialize;
use bincode::serialize; use bincode::serialize;
use hash::Hash; use hash::Hash;
use chrono::prelude::*; use chrono::prelude::*;
use plan::{Action, Condition, Payment, Plan}; use plan::{Action, Condition, Payment, Plan};
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
pub struct Transaction<T> { pub struct Transaction {
pub from: PublicKey, pub from: PublicKey,
pub plan: Plan<T>, pub plan: Plan,
pub asset: T, pub asset: i64,
pub last_id: Hash, pub last_id: Hash,
pub sig: Signature, pub sig: Signature,
} }
impl<T: Serialize + Clone + Eq> Transaction<T> { impl Transaction {
pub fn new(from_keypair: &KeyPair, to: PublicKey, asset: T, last_id: Hash) -> Self { pub fn new(from_keypair: &KeyPair, to: PublicKey, asset: i64, last_id: Hash) -> Self {
let from = from_keypair.pubkey(); let from = from_keypair.pubkey();
let plan = Plan::Action(Action::Pay(Payment { let plan = Plan::Action(Action::Pay(Payment {
asset: asset.clone(), asset: asset.clone(),
@ -38,7 +37,7 @@ impl<T: Serialize + Clone + Eq> Transaction<T> {
from_keypair: &KeyPair, from_keypair: &KeyPair,
to: PublicKey, to: PublicKey,
dt: DateTime<Utc>, dt: DateTime<Utc>,
asset: T, asset: i64,
last_id: Hash, last_id: Hash,
) -> Self { ) -> Self {
let from = from_keypair.pubkey(); let from = from_keypair.pubkey();
@ -79,7 +78,7 @@ impl<T: Serialize + Clone + Eq> Transaction<T> {
} }
pub fn verify(&self) -> bool { pub fn verify(&self) -> bool {
self.sig.verify(&self.from, &self.get_sign_data()) && self.plan.verify(&self.asset) self.sig.verify(&self.from, &self.get_sign_data()) && self.plan.verify(self.asset)
} }
} }
@ -87,14 +86,12 @@ impl<T: Serialize + Clone + Eq> Transaction<T> {
mod tests { mod tests {
use super::*; use super::*;
use bincode::{deserialize, serialize}; use bincode::{deserialize, serialize};
use hash::hash;
#[test] #[test]
fn test_claim() { fn test_claim() {
let keypair = KeyPair::new(); let keypair = KeyPair::new();
let asset = hash(b"hello, world");
let zero = Hash::default(); let zero = Hash::default();
let tr0 = Transaction::new(&keypair, keypair.pubkey(), asset, zero); let tr0 = Transaction::new(&keypair, keypair.pubkey(), 42, zero);
assert!(tr0.verify()); assert!(tr0.verify());
} }
@ -104,8 +101,7 @@ mod tests {
let keypair0 = KeyPair::new(); let keypair0 = KeyPair::new();
let keypair1 = KeyPair::new(); let keypair1 = KeyPair::new();
let pubkey1 = keypair1.pubkey(); let pubkey1 = keypair1.pubkey();
let asset = hash(b"hello, world"); let tr0 = Transaction::new(&keypair0, pubkey1, 42, zero);
let tr0 = Transaction::new(&keypair0, pubkey1, asset, zero);
assert!(tr0.verify()); assert!(tr0.verify());
} }
@ -118,12 +114,12 @@ mod tests {
let claim0 = Transaction { let claim0 = Transaction {
from: Default::default(), from: Default::default(),
plan, plan,
asset: 0u8, asset: 0,
last_id: Default::default(), last_id: Default::default(),
sig: Default::default(), sig: Default::default(),
}; };
let buf = serialize(&claim0).unwrap(); let buf = serialize(&claim0).unwrap();
let claim1: Transaction<u8> = deserialize(&buf).unwrap(); let claim1: Transaction = deserialize(&buf).unwrap();
assert_eq!(claim1, claim0); assert_eq!(claim1, claim0);
} }
@ -132,9 +128,9 @@ mod tests {
let zero = Hash::default(); let zero = Hash::default();
let keypair = KeyPair::new(); let keypair = KeyPair::new();
let pubkey = keypair.pubkey(); let pubkey = keypair.pubkey();
let mut tr = Transaction::new(&keypair, pubkey, hash(b"hello, world"), zero); let mut tr = Transaction::new(&keypair, pubkey, 42, zero);
tr.sign(&keypair); tr.sign(&keypair);
tr.asset = hash(b"goodbye cruel world"); // <-- attack! tr.asset = 1_000_000; // <-- attack!
assert!(!tr.verify()); assert!(!tr.verify());
} }
@ -145,8 +141,7 @@ mod tests {
let thief_keypair = KeyPair::new(); let thief_keypair = KeyPair::new();
let pubkey1 = keypair1.pubkey(); let pubkey1 = keypair1.pubkey();
let zero = Hash::default(); let zero = Hash::default();
let asset = hash(b"hello, world"); let mut tr = Transaction::new(&keypair0, pubkey1, 42, zero);
let mut tr = Transaction::new(&keypair0, pubkey1, asset, zero);
tr.sign(&keypair0); tr.sign(&keypair0);
if let Plan::Action(Action::Pay(ref mut payment)) = tr.plan { if let Plan::Action(Action::Pay(ref mut payment)) = tr.plan {
payment.to = thief_keypair.pubkey(); // <-- attack! payment.to = thief_keypair.pubkey(); // <-- attack!