From 4379fabf163e411adb1324f0ac02a6b5f4fa94e8 Mon Sep 17 00:00:00 2001 From: Greg Fitzgerald Date: Tue, 20 Mar 2018 15:25:48 -0600 Subject: [PATCH 01/10] PlanEvent -> Witness The term used by the Simplicity smart contract language --- src/accountant.rs | 8 ++++---- src/plan.rs | 28 +++++++++++++--------------- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/src/accountant.rs b/src/accountant.rs index 78fefa8f9..03f451f9b 100644 --- a/src/accountant.rs +++ b/src/accountant.rs @@ -5,7 +5,7 @@ use hash::Hash; use entry::Entry; use event::Event; -use plan::{Action, Plan, PlanEvent}; +use plan::{Action, Plan, Witness}; use transaction::Transaction; use signature::{KeyPair, PublicKey, Signature}; use mint::Mint; @@ -141,7 +141,7 @@ impl Accountant { } let mut plan = tr.plan.clone(); - let actionable = plan.process_event(PlanEvent::Timestamp(self.last_time)); + let actionable = plan.process_witness(Witness::Timestamp(self.last_time)); if !actionable { self.pending.insert(tr.sig, plan); @@ -154,7 +154,7 @@ impl Accountant { fn process_verified_sig(&mut self, from: PublicKey, tx_sig: Signature) -> Result<()> { let actionable = if let Some(plan) = self.pending.get_mut(&tx_sig) { - plan.process_event(PlanEvent::Signature(from)) + plan.process_witness(Witness::Signature(from)) } else { false }; @@ -186,7 +186,7 @@ impl Accountant { // Check to see if any timelocked transactions can be completed. let mut completed = vec![]; for (key, plan) in &mut self.pending { - if plan.process_event(PlanEvent::Timestamp(self.last_time)) { + if plan.process_witness(Witness::Timestamp(self.last_time)) { completed.push(key.clone()); } } diff --git a/src/plan.rs b/src/plan.rs index dcaf72cbc..2542a2a97 100644 --- a/src/plan.rs +++ b/src/plan.rs @@ -10,18 +10,16 @@ pub enum Condition { Signature(PublicKey), } -pub enum PlanEvent { +pub enum Witness { Timestamp(DateTime), Signature(PublicKey), } impl Condition { - pub fn is_satisfied(&self, event: &PlanEvent) -> bool { + pub fn is_satisfied(&self, event: &Witness) -> bool { match (self, event) { - (&Condition::Signature(ref pubkey), &PlanEvent::Signature(ref from)) => pubkey == from, - (&Condition::Timestamp(ref dt), &PlanEvent::Timestamp(ref last_time)) => { - dt <= last_time - } + (&Condition::Signature(ref pubkey), &Witness::Signature(ref from)) => pubkey == from, + (&Condition::Timestamp(ref dt), &Witness::Timestamp(ref last_time)) => dt <= last_time, _ => false, } } @@ -100,7 +98,7 @@ impl Plan { } } - pub fn process_event(&mut self, event: PlanEvent) -> bool { + pub fn process_witness(&mut self, event: Witness) -> bool { let mut new_action = None; match *self { Plan::Action(_) => return true, @@ -134,16 +132,16 @@ mod tests { #[test] fn test_signature_satisfied() { let sig = PublicKey::default(); - assert!(Condition::Signature(sig).is_satisfied(&PlanEvent::Signature(sig))); + assert!(Condition::Signature(sig).is_satisfied(&Witness::Signature(sig))); } #[test] fn test_timestamp_satisfied() { let dt1 = Utc.ymd(2014, 11, 14).and_hms(8, 9, 10); let dt2 = Utc.ymd(2014, 11, 14).and_hms(10, 9, 8); - assert!(Condition::Timestamp(dt1).is_satisfied(&PlanEvent::Timestamp(dt1))); - assert!(Condition::Timestamp(dt1).is_satisfied(&PlanEvent::Timestamp(dt2))); - assert!(!Condition::Timestamp(dt2).is_satisfied(&PlanEvent::Timestamp(dt1))); + assert!(Condition::Timestamp(dt1).is_satisfied(&Witness::Timestamp(dt1))); + assert!(Condition::Timestamp(dt1).is_satisfied(&Witness::Timestamp(dt2))); + assert!(!Condition::Timestamp(dt2).is_satisfied(&Witness::Timestamp(dt1))); } #[test] @@ -163,7 +161,7 @@ mod tests { let to = PublicKey::default(); let mut plan = Plan::new_authorized_payment(from, 42, to); - assert!(plan.process_event(PlanEvent::Signature(from))); + assert!(plan.process_witness(Witness::Signature(from))); assert_eq!(plan, Plan::new_payment(42, to)); } @@ -173,7 +171,7 @@ mod tests { let to = PublicKey::default(); let mut plan = Plan::new_future_payment(dt, 42, to); - assert!(plan.process_event(PlanEvent::Timestamp(dt))); + assert!(plan.process_witness(Witness::Timestamp(dt))); assert_eq!(plan, Plan::new_payment(42, to)); } @@ -184,11 +182,11 @@ mod tests { let to = PublicKey::default(); let mut plan = Plan::new_cancelable_future_payment(dt, from, 42, to); - assert!(plan.process_event(PlanEvent::Timestamp(dt))); + assert!(plan.process_witness(Witness::Timestamp(dt))); assert_eq!(plan, Plan::new_payment(42, to)); let mut plan = Plan::new_cancelable_future_payment(dt, from, 42, to); - assert!(plan.process_event(PlanEvent::Signature(from))); + assert!(plan.process_witness(Witness::Signature(from))); assert_eq!(plan, Plan::new_payment(42, from)); } } From 6f509a8a1ee5ef8808f11d77234320e3bf086191 Mon Sep 17 00:00:00 2001 From: Greg Fitzgerald Date: Tue, 20 Mar 2018 15:31:28 -0600 Subject: [PATCH 02/10] Reorder --- src/plan.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plan.rs b/src/plan.rs index 2542a2a97..2bd325746 100644 --- a/src/plan.rs +++ b/src/plan.rs @@ -4,13 +4,13 @@ use signature::PublicKey; use chrono::prelude::*; use std::mem; -#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] -pub enum Condition { +pub enum Witness { Timestamp(DateTime), Signature(PublicKey), } -pub enum Witness { +#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] +pub enum Condition { Timestamp(DateTime), Signature(PublicKey), } From 6be3d62d8950e8eb98d7e12d79b411d5b931a36a Mon Sep 17 00:00:00 2001 From: Greg Fitzgerald Date: Tue, 20 Mar 2018 15:43:04 -0600 Subject: [PATCH 03/10] Remove Action from spending plans --- src/accountant.rs | 10 ++++---- src/mint.rs | 4 +-- src/plan.rs | 63 ++++++++++++++-------------------------------- src/transaction.rs | 20 ++++++--------- 4 files changed, 33 insertions(+), 64 deletions(-) diff --git a/src/accountant.rs b/src/accountant.rs index 03f451f9b..cd50d00ef 100644 --- a/src/accountant.rs +++ b/src/accountant.rs @@ -5,7 +5,7 @@ use hash::Hash; use entry::Entry; use event::Event; -use plan::{Action, Plan, Witness}; +use plan::{Plan, Witness}; use transaction::Transaction; use signature::{KeyPair, PublicKey, Signature}; use mint::Mint; @@ -85,7 +85,7 @@ impl Accountant { } fn is_deposit(allow_deposits: bool, from: &PublicKey, plan: &Plan) -> bool { - if let Plan::Action(Action::Pay(ref payment)) = *plan { + if let Plan::Pay(ref payment) = *plan { allow_deposits && *from == payment.to } else { false @@ -114,7 +114,7 @@ impl Accountant { /// Commit funds to the 'to' party. fn complete_transaction(self: &mut Self, plan: &Plan) { - if let Plan::Action(Action::Pay(ref payment)) = *plan { + if let Plan::Pay(ref payment) = *plan { if self.balances.contains_key(&payment.to) { if let Some(x) = self.balances.get_mut(&payment.to) { *x += payment.tokens; @@ -288,7 +288,7 @@ mod tests { let mut acc = Accountant::new(&alice, None); let bob_pubkey = KeyPair::new().pubkey(); let mut tr = Transaction::new(&alice.keypair(), bob_pubkey, 1, alice.seed()); - if let Plan::Action(Action::Pay(ref mut payment)) = tr.plan { + if let Plan::Pay(ref mut payment) = tr.plan { payment.tokens = 2; // <-- attack! } assert_eq!( @@ -297,7 +297,7 @@ mod tests { ); // Also, ensure all branchs of the plan spend all tokens - if let Plan::Action(Action::Pay(ref mut payment)) = tr.plan { + if let Plan::Pay(ref mut payment) = tr.plan { payment.tokens = 0; // <-- whoops! } assert_eq!( diff --git a/src/mint.rs b/src/mint.rs index f2a91da87..74339cd69 100644 --- a/src/mint.rs +++ b/src/mint.rs @@ -58,13 +58,13 @@ impl Mint { mod tests { use super::*; use ledger::verify_slice; - use plan::{Action, Plan}; + use plan::Plan; #[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::Action(Action::Pay(payment)) = tr.plan { + if let Plan::Pay(payment) = tr.plan { assert_eq!(tr.from, payment.to); } } diff --git a/src/plan.rs b/src/plan.rs index 2bd325746..168f788c0 100644 --- a/src/plan.rs +++ b/src/plan.rs @@ -25,19 +25,6 @@ impl Condition { } } -#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] -pub enum Action { - Pay(Payment), -} - -impl Action { - pub fn spendable(&self) -> i64 { - match *self { - Action::Pay(ref payment) => payment.tokens, - } - } -} - #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] pub struct Payment { pub tokens: i64, @@ -46,28 +33,22 @@ pub struct Payment { #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] pub enum Plan { - Action(Action), - After(Condition, Action), - Race((Condition, Action), (Condition, Action)), + Pay(Payment), + After(Condition, Payment), + Race((Condition, Payment), (Condition, Payment)), } impl Plan { pub fn new_payment(tokens: i64, to: PublicKey) -> Self { - Plan::Action(Action::Pay(Payment { tokens, to })) + Plan::Pay(Payment { tokens, to }) } pub fn new_authorized_payment(from: PublicKey, tokens: i64, to: PublicKey) -> Self { - Plan::After( - Condition::Signature(from), - Action::Pay(Payment { tokens, to }), - ) + Plan::After(Condition::Signature(from), Payment { tokens, to }) } pub fn new_future_payment(dt: DateTime, tokens: i64, to: PublicKey) -> Self { - Plan::After( - Condition::Timestamp(dt), - Action::Pay(Payment { tokens, to }), - ) + Plan::After(Condition::Timestamp(dt), Payment { tokens, to }) } pub fn new_cancelable_future_payment( @@ -77,47 +58,41 @@ impl Plan { to: PublicKey, ) -> Self { Plan::Race( - ( - Condition::Timestamp(dt), - Action::Pay(Payment { tokens, to }), - ), - ( - Condition::Signature(from), - Action::Pay(Payment { tokens, to: from }), - ), + (Condition::Timestamp(dt), Payment { tokens, to }), + (Condition::Signature(from), Payment { tokens, to: from }), ) } pub fn verify(&self, spendable_tokens: i64) -> bool { match *self { - Plan::Action(ref action) => action.spendable() == spendable_tokens, - Plan::After(_, ref action) => action.spendable() == spendable_tokens, + Plan::Pay(ref payment) => payment.tokens == spendable_tokens, + Plan::After(_, ref payment) => payment.tokens == spendable_tokens, Plan::Race(ref a, ref b) => { - a.1.spendable() == spendable_tokens && b.1.spendable() == spendable_tokens + a.1.tokens == spendable_tokens && b.1.tokens == spendable_tokens } } } pub fn process_witness(&mut self, event: Witness) -> bool { - let mut new_action = None; + let mut new_payment = None; match *self { - Plan::Action(_) => return true, - Plan::After(ref cond, ref action) => { + Plan::Pay(_) => return true, + Plan::After(ref cond, ref payment) => { if cond.is_satisfied(&event) { - new_action = Some(action.clone()); + new_payment = Some(payment.clone()); } } Plan::Race(ref a, ref b) => { if a.0.is_satisfied(&event) { - new_action = Some(a.1.clone()); + new_payment = Some(a.1.clone()); } else if b.0.is_satisfied(&event) { - new_action = Some(b.1.clone()); + new_payment = Some(b.1.clone()); } } } - if let Some(action) = new_action { - mem::replace(self, Plan::Action(action)); + if let Some(payment) = new_payment { + mem::replace(self, Plan::Pay(payment)); true } else { false diff --git a/src/transaction.rs b/src/transaction.rs index 9ee7d4565..482bdddf5 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -4,7 +4,7 @@ use signature::{KeyPair, KeyPairUtil, PublicKey, Signature, SignatureUtil}; use bincode::serialize; use hash::Hash; use chrono::prelude::*; -use plan::{Action, Condition, Payment, Plan}; +use plan::{Condition, Payment, Plan}; #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] pub struct Transaction { @@ -18,7 +18,7 @@ pub struct Transaction { impl Transaction { pub fn new(from_keypair: &KeyPair, to: PublicKey, tokens: i64, last_id: Hash) -> Self { let from = from_keypair.pubkey(); - let plan = Plan::Action(Action::Pay(Payment { tokens, to })); + let plan = Plan::Pay(Payment { tokens, to }); let mut tr = Transaction { from, plan, @@ -39,14 +39,8 @@ impl Transaction { ) -> Self { let from = from_keypair.pubkey(); let plan = Plan::Race( - ( - Condition::Timestamp(dt), - Action::Pay(Payment { tokens, to }), - ), - ( - Condition::Signature(from), - Action::Pay(Payment { tokens, to: from }), - ), + (Condition::Timestamp(dt), Payment { tokens, to }), + (Condition::Signature(from), Payment { tokens, to: from }), ); let mut tr = Transaction { from, @@ -98,10 +92,10 @@ mod tests { #[test] fn test_serialize_claim() { - let plan = Plan::Action(Action::Pay(Payment { + let plan = Plan::Pay(Payment { tokens: 0, to: Default::default(), - })); + }); let claim0 = Transaction { from: Default::default(), plan, @@ -134,7 +128,7 @@ mod tests { let zero = Hash::default(); let mut tr = Transaction::new(&keypair0, pubkey1, 42, zero); tr.sign(&keypair0); - if let Plan::Action(Action::Pay(ref mut payment)) = tr.plan { + if let Plan::Pay(ref mut payment) = tr.plan { payment.to = thief_keypair.pubkey(); // <-- attack! }; assert!(!tr.verify()); From 7c7e3931a0fc55fb5d2d7d28790a7ae26e101b0f Mon Sep 17 00:00:00 2001 From: Greg Fitzgerald Date: Tue, 20 Mar 2018 15:52:46 -0600 Subject: [PATCH 04/10] Better docs --- src/plan.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/plan.rs b/src/plan.rs index 168f788c0..f07bf913b 100644 --- a/src/plan.rs +++ b/src/plan.rs @@ -1,4 +1,7 @@ -//! The `plan` crate provides functionality for creating spending plans. +//! A domain-specific language for payment plans. Users create Plan objects that +//! are given to an interpreter. The interpreter listens for `Witness` events, +//! which it uses to reduce the payment plan. When the plan is reduced to a +//! `Payment`, the payment is executed. use signature::PublicKey; use chrono::prelude::*; From f7032f7d9a401e9828c24239c0bf48e7ef647d35 Mon Sep 17 00:00:00 2001 From: Greg Fitzgerald Date: Tue, 20 Mar 2018 16:52:47 -0600 Subject: [PATCH 05/10] Cleanup: replace bool retval with is_complete() method --- src/accountant.rs | 14 ++++++++------ src/plan.rs | 22 +++++++++++++--------- 2 files changed, 21 insertions(+), 15 deletions(-) diff --git a/src/accountant.rs b/src/accountant.rs index cd50d00ef..01b797538 100644 --- a/src/accountant.rs +++ b/src/accountant.rs @@ -141,20 +141,21 @@ impl Accountant { } let mut plan = tr.plan.clone(); - let actionable = plan.process_witness(Witness::Timestamp(self.last_time)); + plan.process_witness(Witness::Timestamp(self.last_time)); - if !actionable { + if plan.is_complete() { + self.complete_transaction(&plan); + } else { self.pending.insert(tr.sig, plan); - return Ok(()); } - self.complete_transaction(&plan); Ok(()) } fn process_verified_sig(&mut self, from: PublicKey, tx_sig: Signature) -> Result<()> { let actionable = if let Some(plan) = self.pending.get_mut(&tx_sig) { - plan.process_witness(Witness::Signature(from)) + plan.process_witness(Witness::Signature(from)); + plan.is_complete() } else { false }; @@ -186,7 +187,8 @@ impl Accountant { // Check to see if any timelocked transactions can be completed. let mut completed = vec![]; for (key, plan) in &mut self.pending { - if plan.process_witness(Witness::Timestamp(self.last_time)) { + plan.process_witness(Witness::Timestamp(self.last_time)); + if plan.is_complete() { completed.push(key.clone()); } } diff --git a/src/plan.rs b/src/plan.rs index f07bf913b..cb8cc2701 100644 --- a/src/plan.rs +++ b/src/plan.rs @@ -66,6 +66,13 @@ impl Plan { ) } + pub fn is_complete(&self) -> bool { + match *self { + Plan::Pay(_) => true, + _ => false, + } + } + pub fn verify(&self, spendable_tokens: i64) -> bool { match *self { Plan::Pay(ref payment) => payment.tokens == spendable_tokens, @@ -76,10 +83,10 @@ impl Plan { } } - pub fn process_witness(&mut self, event: Witness) -> bool { + pub fn process_witness(&mut self, event: Witness) { let mut new_payment = None; match *self { - Plan::Pay(_) => return true, + Plan::Pay(_) => (), Plan::After(ref cond, ref payment) => { if cond.is_satisfied(&event) { new_payment = Some(payment.clone()); @@ -96,9 +103,6 @@ impl Plan { if let Some(payment) = new_payment { mem::replace(self, Plan::Pay(payment)); - true - } else { - false } } } @@ -139,7 +143,7 @@ mod tests { let to = PublicKey::default(); let mut plan = Plan::new_authorized_payment(from, 42, to); - assert!(plan.process_witness(Witness::Signature(from))); + plan.process_witness(Witness::Signature(from)); assert_eq!(plan, Plan::new_payment(42, to)); } @@ -149,7 +153,7 @@ mod tests { let to = PublicKey::default(); let mut plan = Plan::new_future_payment(dt, 42, to); - assert!(plan.process_witness(Witness::Timestamp(dt))); + plan.process_witness(Witness::Timestamp(dt)); assert_eq!(plan, Plan::new_payment(42, to)); } @@ -160,11 +164,11 @@ mod tests { let to = PublicKey::default(); let mut plan = Plan::new_cancelable_future_payment(dt, from, 42, to); - assert!(plan.process_witness(Witness::Timestamp(dt))); + plan.process_witness(Witness::Timestamp(dt)); assert_eq!(plan, Plan::new_payment(42, to)); let mut plan = Plan::new_cancelable_future_payment(dt, from, 42, to); - assert!(plan.process_witness(Witness::Signature(from))); + plan.process_witness(Witness::Signature(from)); assert_eq!(plan, Plan::new_payment(42, from)); } } From 4da89ac8a9c899442449053434721bd96446953f Mon Sep 17 00:00:00 2001 From: Greg Fitzgerald Date: Tue, 20 Mar 2018 16:53:41 -0600 Subject: [PATCH 06/10] Cleanup naming --- src/accountant.rs | 6 +++--- src/plan.rs | 20 ++++++++++---------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/accountant.rs b/src/accountant.rs index 01b797538..7d61ff1d0 100644 --- a/src/accountant.rs +++ b/src/accountant.rs @@ -141,7 +141,7 @@ impl Accountant { } let mut plan = tr.plan.clone(); - plan.process_witness(Witness::Timestamp(self.last_time)); + plan.apply_witness(Witness::Timestamp(self.last_time)); if plan.is_complete() { self.complete_transaction(&plan); @@ -154,7 +154,7 @@ impl Accountant { fn process_verified_sig(&mut self, from: PublicKey, tx_sig: Signature) -> Result<()> { let actionable = if let Some(plan) = self.pending.get_mut(&tx_sig) { - plan.process_witness(Witness::Signature(from)); + plan.apply_witness(Witness::Signature(from)); plan.is_complete() } else { false @@ -187,7 +187,7 @@ impl Accountant { // Check to see if any timelocked transactions can be completed. let mut completed = vec![]; for (key, plan) in &mut self.pending { - plan.process_witness(Witness::Timestamp(self.last_time)); + plan.apply_witness(Witness::Timestamp(self.last_time)); if plan.is_complete() { completed.push(key.clone()); } diff --git a/src/plan.rs b/src/plan.rs index cb8cc2701..960c969c4 100644 --- a/src/plan.rs +++ b/src/plan.rs @@ -19,8 +19,8 @@ pub enum Condition { } impl Condition { - pub fn is_satisfied(&self, event: &Witness) -> bool { - match (self, event) { + pub fn is_satisfied(&self, witness: &Witness) -> bool { + match (self, witness) { (&Condition::Signature(ref pubkey), &Witness::Signature(ref from)) => pubkey == from, (&Condition::Timestamp(ref dt), &Witness::Timestamp(ref last_time)) => dt <= last_time, _ => false, @@ -83,19 +83,19 @@ impl Plan { } } - pub fn process_witness(&mut self, event: Witness) { + pub fn apply_witness(&mut self, witness: Witness) { let mut new_payment = None; match *self { Plan::Pay(_) => (), Plan::After(ref cond, ref payment) => { - if cond.is_satisfied(&event) { + if cond.is_satisfied(&witness) { new_payment = Some(payment.clone()); } } Plan::Race(ref a, ref b) => { - if a.0.is_satisfied(&event) { + if a.0.is_satisfied(&witness) { new_payment = Some(a.1.clone()); - } else if b.0.is_satisfied(&event) { + } else if b.0.is_satisfied(&witness) { new_payment = Some(b.1.clone()); } } @@ -143,7 +143,7 @@ mod tests { let to = PublicKey::default(); let mut plan = Plan::new_authorized_payment(from, 42, to); - plan.process_witness(Witness::Signature(from)); + plan.apply_witness(Witness::Signature(from)); assert_eq!(plan, Plan::new_payment(42, to)); } @@ -153,7 +153,7 @@ mod tests { let to = PublicKey::default(); let mut plan = Plan::new_future_payment(dt, 42, to); - plan.process_witness(Witness::Timestamp(dt)); + plan.apply_witness(Witness::Timestamp(dt)); assert_eq!(plan, Plan::new_payment(42, to)); } @@ -164,11 +164,11 @@ mod tests { let to = PublicKey::default(); let mut plan = Plan::new_cancelable_future_payment(dt, from, 42, to); - plan.process_witness(Witness::Timestamp(dt)); + plan.apply_witness(Witness::Timestamp(dt)); assert_eq!(plan, Plan::new_payment(42, to)); let mut plan = Plan::new_cancelable_future_payment(dt, from, 42, to); - plan.process_witness(Witness::Signature(from)); + plan.apply_witness(Witness::Signature(from)); assert_eq!(plan, Plan::new_payment(42, from)); } } From d0e745038936a7219b31ac21de01ff24f3cf7e91 Mon Sep 17 00:00:00 2001 From: Greg Fitzgerald Date: Tue, 20 Mar 2018 16:58:14 -0600 Subject: [PATCH 07/10] Add docs --- src/plan.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/plan.rs b/src/plan.rs index 960c969c4..f15197d82 100644 --- a/src/plan.rs +++ b/src/plan.rs @@ -83,6 +83,8 @@ impl Plan { } } + /// Apply a witness to the spending plan to see if the plan can be reduced. + /// If so, modify the plan in-place. pub fn apply_witness(&mut self, witness: Witness) { let mut new_payment = None; match *self { From bff32bf7bc129d80c0056c2f2966f1cc4c62ac95 Mon Sep 17 00:00:00 2001 From: Greg Fitzgerald Date: Tue, 20 Mar 2018 17:32:02 -0600 Subject: [PATCH 08/10] Cleanup --- src/plan.rs | 22 ++++++---------------- 1 file changed, 6 insertions(+), 16 deletions(-) diff --git a/src/plan.rs b/src/plan.rs index f15197d82..e455218ea 100644 --- a/src/plan.rs +++ b/src/plan.rs @@ -86,22 +86,12 @@ impl Plan { /// Apply a witness to the spending plan to see if the plan can be reduced. /// If so, modify the plan in-place. pub fn apply_witness(&mut self, witness: Witness) { - let mut new_payment = None; - match *self { - Plan::Pay(_) => (), - Plan::After(ref cond, ref payment) => { - if cond.is_satisfied(&witness) { - new_payment = Some(payment.clone()); - } - } - Plan::Race(ref a, ref b) => { - if a.0.is_satisfied(&witness) { - new_payment = Some(a.1.clone()); - } else if b.0.is_satisfied(&witness) { - new_payment = Some(b.1.clone()); - } - } - } + let new_payment = match *self { + Plan::After(ref cond, ref payment) if cond.is_satisfied(&witness) => Some(payment), + Plan::Race((ref cond, ref payment), _) if cond.is_satisfied(&witness) => Some(payment), + Plan::Race(_, (ref cond, ref payment)) if cond.is_satisfied(&witness) => Some(payment), + _ => None, + }.map(|x| x.clone()); if let Some(payment) = new_payment { mem::replace(self, Plan::Pay(payment)); From c584a25ec98ed195a467c87c6bb5e5df30829396 Mon Sep 17 00:00:00 2001 From: Greg Fitzgerald Date: Tue, 20 Mar 2018 17:47:55 -0600 Subject: [PATCH 09/10] Move complete_transaction from method to function So that we can hold separate mutable references to the pending queue and the map of balances. --- src/accountant.rs | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/accountant.rs b/src/accountant.rs index 7d61ff1d0..eb9abb9b5 100644 --- a/src/accountant.rs +++ b/src/accountant.rs @@ -26,6 +26,19 @@ pub enum AccountingError { pub type Result = result::Result; +/// Commit funds to the 'to' party. +fn complete_transaction(balances: &mut HashMap, plan: &Plan) { + if let Plan::Pay(ref payment) = *plan { + if balances.contains_key(&payment.to) { + if let Some(x) = balances.get_mut(&payment.to) { + *x += payment.tokens; + } + } else { + balances.insert(payment.to, payment.tokens); + } + } +} + pub struct Accountant { pub historian: Historian, pub balances: HashMap, @@ -112,19 +125,6 @@ impl Accountant { Ok(()) } - /// Commit funds to the 'to' party. - fn complete_transaction(self: &mut Self, plan: &Plan) { - if let Plan::Pay(ref payment) = *plan { - if self.balances.contains_key(&payment.to) { - if let Some(x) = self.balances.get_mut(&payment.to) { - *x += payment.tokens; - } - } else { - self.balances.insert(payment.to, payment.tokens); - } - } - } - fn process_verified_transaction( self: &mut Self, tr: &Transaction, @@ -144,7 +144,7 @@ impl Accountant { plan.apply_witness(Witness::Timestamp(self.last_time)); if plan.is_complete() { - self.complete_transaction(&plan); + complete_transaction(&mut self.balances, &plan); } else { self.pending.insert(tr.sig, plan); } @@ -155,15 +155,16 @@ impl Accountant { fn process_verified_sig(&mut self, from: PublicKey, tx_sig: Signature) -> Result<()> { let actionable = if let Some(plan) = self.pending.get_mut(&tx_sig) { plan.apply_witness(Witness::Signature(from)); + if plan.is_complete() { + complete_transaction(&mut self.balances, &plan); + } plan.is_complete() } else { false }; if actionable { - if let Some(plan) = self.pending.remove(&tx_sig) { - self.complete_transaction(&plan); - } + self.pending.remove(&tx_sig); } Ok(()) @@ -189,14 +190,13 @@ impl Accountant { for (key, plan) in &mut self.pending { plan.apply_witness(Witness::Timestamp(self.last_time)); if plan.is_complete() { + complete_transaction(&mut self.balances, &plan); completed.push(key.clone()); } } for key in completed { - if let Some(plan) = self.pending.remove(&key) { - self.complete_transaction(&plan); - } + self.pending.remove(&key); } Ok(()) From 9a7cac1e079363fba3525fc2e0b375392fb83987 Mon Sep 17 00:00:00 2001 From: Greg Fitzgerald Date: Tue, 20 Mar 2018 18:07:54 -0600 Subject: [PATCH 10/10] Use the Entry API to remove the double lookup --- src/accountant.rs | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/src/accountant.rs b/src/accountant.rs index eb9abb9b5..aaf206e34 100644 --- a/src/accountant.rs +++ b/src/accountant.rs @@ -13,6 +13,7 @@ use historian::{reserve_signature, Historian}; use recorder::Signal; use std::sync::mpsc::SendError; use std::collections::{HashMap, HashSet}; +use std::collections::hash_map::Entry::Occupied; use std::result; use chrono::prelude::*; @@ -153,20 +154,14 @@ impl Accountant { } fn process_verified_sig(&mut self, from: PublicKey, tx_sig: Signature) -> Result<()> { - let actionable = if let Some(plan) = self.pending.get_mut(&tx_sig) { - plan.apply_witness(Witness::Signature(from)); - if plan.is_complete() { - complete_transaction(&mut self.balances, &plan); + if let Occupied(mut e) = self.pending.entry(tx_sig) { + e.get_mut().apply_witness(Witness::Signature(from)); + if e.get().is_complete() { + complete_transaction(&mut self.balances, e.get()); + e.remove_entry(); } - plan.is_complete() - } else { - false }; - if actionable { - self.pending.remove(&tx_sig); - } - Ok(()) }