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:
@ -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) {
|
||||||
|
@ -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 },
|
||||||
|
@ -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)
|
||||||
|
@ -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,
|
||||||
|
26
src/plan.rs
26
src/plan.rs
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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!
|
||||||
|
Reference in New Issue
Block a user