From 8c40d1bd72d910277d986ccc8298733598e70575 Mon Sep 17 00:00:00 2001 From: Greg Fitzgerald Date: Sat, 10 Mar 2018 17:11:12 -0700 Subject: [PATCH] Move spending endpoints into expressions --- src/accountant.rs | 21 +++++------ src/mint.rs | 2 +- src/transaction.rs | 90 ++++++++++++++++++++++++++++++++++++---------- 3 files changed, 84 insertions(+), 29 deletions(-) diff --git a/src/accountant.rs b/src/accountant.rs index 88e8bbc6fa..d9f5203536 100644 --- a/src/accountant.rs +++ b/src/accountant.rs @@ -109,12 +109,13 @@ impl Accountant { /// Commit funds to the 'to' party. fn complete_transaction(self: &mut Self, tr: &Transaction) { - if self.balances.contains_key(&tr.plan.to) { - if let Some(x) = self.balances.get_mut(&tr.plan.to) { + let to = tr.plan.to(); + if self.balances.contains_key(&to) { + if let Some(x) = self.balances.get_mut(&to) { *x += tr.asset; } } else { - self.balances.insert(tr.plan.to, tr.asset); + self.balances.insert(to, tr.asset); } } @@ -149,17 +150,17 @@ impl Accountant { return Err(AccountingError::InvalidTransferSignature); } - if !tr.plan.unless_any.is_empty() { + if !tr.plan.unless_any.0.is_empty() { // TODO: Check to see if the transaction is expired. } - if !Self::is_deposit(allow_deposits, &tr.from, &tr.plan.to) { + if !Self::is_deposit(allow_deposits, &tr.from, &tr.plan.to()) { if let Some(x) = self.balances.get_mut(&tr.from) { *x -= tr.asset; } } - if !self.all_satisfied(&tr.plan.if_all) { + if !self.all_satisfied(&tr.plan.if_all.0) { self.pending.insert(tr.sig, tr.clone()); return Ok(()); } @@ -175,7 +176,7 @@ impl Accountant { // if Signature(from) is in unless_any, return funds to tx.from, and remove the tx from this map. // TODO: Use find(). - for cond in &tr.plan.unless_any { + for cond in &tr.plan.unless_any.0 { if let Condition::Signature(pubkey) = *cond { if from == pubkey { cancel = true; @@ -220,17 +221,17 @@ impl Accountant { // Check to see if any timelocked transactions can be completed. let mut completed = vec![]; for (key, tr) in &self.pending { - for cond in &tr.plan.if_all { + for cond in &tr.plan.if_all.0 { if let Condition::Timestamp(dt) = *cond { if self.last_time >= dt { - if tr.plan.if_all.len() == 1 { + if tr.plan.if_all.0.len() == 1 { completed.push(*key); } } } } // TODO: Add this in once we start removing constraints - //if tr.plan.if_all.is_empty() { + //if tr.plan.if_all.0.is_empty() { // // TODO: Remove tr from pending // self.complete_transaction(tr); //} diff --git a/src/mint.rs b/src/mint.rs index 0fd1e0b835..bb14ffe374 100644 --- a/src/mint.rs +++ b/src/mint.rs @@ -63,7 +63,7 @@ mod tests { fn test_create_events() { let mut events = Mint::new(100).create_events().into_iter(); if let Event::Transaction(tr) = events.next().unwrap() { - assert_eq!(tr.from, tr.plan.to); + assert_eq!(tr.from, tr.plan.to()); } assert_eq!(events.next(), None); } diff --git a/src/transaction.rs b/src/transaction.rs index 86a2f3f756..47570e226a 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -13,30 +13,59 @@ pub enum Condition { } #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] -pub struct SpendingPlan { +pub enum Action { + Pay(Payment), +} + +#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] +pub struct Payment { + pub asset: T, pub to: PublicKey, - pub if_all: Vec, - pub unless_any: Vec, +} + +#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] +pub struct SpendingPlan { + pub if_all: (Vec, Action), + pub unless_any: (Vec, Action), +} + +impl SpendingPlan { + pub fn to(&self) -> PublicKey { + let Action::Pay(ref payment) = self.if_all.1; + payment.to + } } #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] pub struct Transaction { pub from: PublicKey, - pub plan: SpendingPlan, + pub plan: SpendingPlan, pub asset: T, pub last_id: Hash, pub sig: Signature, } -impl Transaction { +impl Transaction { pub fn new(from_keypair: &KeyPair, to: PublicKey, asset: T, last_id: Hash) -> Self { + let from = from_keypair.pubkey(); let plan = SpendingPlan { - to, - if_all: vec![], - unless_any: vec![], + if_all: ( + vec![], + Action::Pay(Payment { + asset: asset.clone(), + to, + }), + ), + unless_any: ( + vec![], + Action::Pay(Payment { + asset: asset.clone(), + to: from, + }), + ), }; let mut tr = Transaction { - from: from_keypair.pubkey(), + from, plan, asset, last_id, @@ -55,9 +84,20 @@ impl Transaction { ) -> Self { let from = from_keypair.pubkey(); let plan = SpendingPlan { - to, - if_all: vec![Condition::Timestamp(dt)], - unless_any: vec![Condition::Signature(from)], + if_all: ( + vec![Condition::Timestamp(dt)], + Action::Pay(Payment { + asset: asset.clone(), + to, + }), + ), + unless_any: ( + vec![Condition::Signature(from)], + Action::Pay(Payment { + asset: asset.clone(), + to: from, + }), + ), }; let mut tr = Transaction { from, @@ -74,7 +114,6 @@ impl Transaction { let plan = &self.plan; serialize(&( &self.from, - &plan.to, &plan.if_all, &plan.unless_any, &self.asset, @@ -121,9 +160,20 @@ mod tests { #[test] fn test_serialize_claim() { let plan = SpendingPlan { - to: Default::default(), - if_all: Default::default(), - unless_any: Default::default(), + if_all: ( + Default::default(), + Action::Pay(Payment { + asset: 0, + to: Default::default(), + }), + ), + unless_any: ( + Default::default(), + Action::Pay(Payment { + asset: 0, + to: Default::default(), + }), + ), }; let claim0 = Transaction { from: Default::default(), @@ -155,9 +205,13 @@ mod tests { let thief_keypair = KeyPair::new(); let pubkey1 = keypair1.pubkey(); let zero = Hash::default(); - let mut tr = Transaction::new(&keypair0, pubkey1, hash(b"hello, world"), zero); + let asset = hash(b"hello, world"); + let mut tr = Transaction::new(&keypair0, pubkey1, asset, zero); tr.sign(&keypair0); - tr.plan.to = thief_keypair.pubkey(); // <-- attack! + tr.plan.if_all.1 = Action::Pay(Payment { + asset, + to: thief_keypair.pubkey(), + }); // <-- attack! assert!(!tr.verify()); } }