diff --git a/src/accountant.rs b/src/accountant.rs index 0b2aab422d..57284cac0c 100644 --- a/src/accountant.rs +++ b/src/accountant.rs @@ -83,8 +83,12 @@ impl Accountant { self.last_id } - fn is_deposit(allow_deposits: bool, from: &PublicKey, to: &PublicKey) -> bool { - allow_deposits && from == to + fn is_deposit(allow_deposits: bool, from: &PublicKey, plan: &Plan) -> bool { + if let Plan::Action(Action::Pay(ref payment)) = *plan { + allow_deposits && *from == payment.to + } else { + false + } } pub fn process_transaction(self: &mut Self, tr: Transaction) -> Result<()> { @@ -109,38 +113,49 @@ impl Accountant { /// Commit funds to the 'to' party. fn complete_transaction(self: &mut Self, plan: &Plan) { - let Action::Pay(ref payment) = plan.if_all.1; - let to = payment.to; - if self.balances.contains_key(&to) { - if let Some(x) = self.balances.get_mut(&to) { - *x += payment.asset; + let payment = match *plan { + Plan::Action(Action::Pay(ref payment)) => Some(payment), + Plan::After(_, Action::Pay(ref payment)) => Some(payment), + Plan::Race(ref plan_a, _) => { + if let Plan::After(_, Action::Pay(ref payment)) = **plan_a { + Some(payment) + } else { + None + } + } + }; + if let Some(payment) = payment { + if self.balances.contains_key(&payment.to) { + if let Some(x) = self.balances.get_mut(&payment.to) { + *x += payment.asset; + } + } else { + self.balances.insert(payment.to, payment.asset); } - } else { - self.balances.insert(to, payment.asset); } } /// Return funds to the 'from' party. fn cancel_transaction(self: &mut Self, plan: &Plan) { - let Action::Pay(ref payment) = plan.unless_any.1; - if let Some(x) = self.balances.get_mut(&payment.to) { - *x += payment.asset; + if let Plan::Race(_, ref cancel_plan) = *plan { + if let Plan::After(_, Action::Pay(ref payment)) = **cancel_plan { + if let Some(x) = self.balances.get_mut(&payment.to) { + *x += payment.asset; + } + } } } // TODO: Move this to transaction.rs - fn all_satisfied(&self, conds: &[Condition]) -> bool { - let mut satisfied = true; - for cond in conds { - if let &Condition::Timestamp(dt) = cond { - if dt > self.last_time { - satisfied = false; - } - } else { - satisfied = false; + fn all_satisfied(&self, plan: &Plan) -> bool { + match *plan { + Plan::Action(_) => true, + Plan::After(Condition::Timestamp(dt), _) => dt <= self.last_time, + Plan::After(Condition::Signature(_), _) => false, + Plan::Race(ref plan_a, ref plan_b) => { + self.all_satisfied(plan_a) || self.all_satisfied(plan_b) } } - satisfied } fn process_verified_transaction( @@ -152,17 +167,13 @@ impl Accountant { return Err(AccountingError::InvalidTransferSignature); } - 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) { if let Some(x) = self.balances.get_mut(&tr.from) { *x -= tr.asset; } } - if !self.all_satisfied(&tr.plan.if_all.0) { + if !self.all_satisfied(&tr.plan) { self.pending.insert(tr.sig, tr.plan.clone()); return Ok(()); } @@ -178,11 +189,10 @@ 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 &plan.unless_any.0 { - if let Condition::Signature(pubkey) = *cond { + if let Plan::Race(_, ref plan_b) = *plan { + if let Plan::After(Condition::Signature(pubkey), _) = **plan_b { if from == pubkey { cancel = true; - break; } } } @@ -223,12 +233,14 @@ impl Accountant { // Check to see if any timelocked transactions can be completed. let mut completed = vec![]; for (key, plan) in &self.pending { - for cond in &plan.if_all.0 { - if let Condition::Timestamp(dt) = *cond { + if let Plan::After(Condition::Timestamp(dt), _) = *plan { + if self.last_time >= dt { + completed.push(*key); + } + } else if let Plan::Race(ref plan_a, _) = *plan { + if let Plan::After(Condition::Timestamp(dt), _) = **plan_a { if self.last_time >= dt { - if plan.if_all.0.len() == 1 { - completed.push(*key); - } + completed.push(*key); } } } diff --git a/src/mint.rs b/src/mint.rs index bb14ffe374..ff21fc3b5d 100644 --- a/src/mint.rs +++ b/src/mint.rs @@ -58,12 +58,15 @@ impl Mint { mod tests { use super::*; use log::verify_slice; + use transaction::{Action, Plan}; #[test] 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()); + if let Plan::Action(Action::Pay(payment)) = tr.plan { + assert_eq!(tr.from, payment.to); + } } assert_eq!(events.next(), None); } diff --git a/src/transaction.rs b/src/transaction.rs index 8e6359231c..f606c4b3ac 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -24,16 +24,10 @@ pub struct Payment { } #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] -pub struct Plan { - pub if_all: (Vec, Action), - pub unless_any: (Vec, Action), -} - -impl Plan { - pub fn to(&self) -> PublicKey { - let Action::Pay(ref payment) = self.if_all.1; - payment.to - } +pub enum Plan { + Action(Action), + After(Condition, Action), + Race(Box>, Box>), } #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)] @@ -48,22 +42,10 @@ pub struct Transaction { impl Transaction { pub fn new(from_keypair: &KeyPair, to: PublicKey, asset: T, last_id: Hash) -> Self { let from = from_keypair.pubkey(); - let plan = Plan { - if_all: ( - vec![], - Action::Pay(Payment { - asset: asset.clone(), - to, - }), - ), - unless_any: ( - vec![Condition::Signature(from)], - Action::Pay(Payment { - asset: asset.clone(), - to: from, - }), - ), - }; + let plan = Plan::Action(Action::Pay(Payment { + asset: asset.clone(), + to, + })); let mut tr = Transaction { from, plan, @@ -83,22 +65,22 @@ impl Transaction { last_id: Hash, ) -> Self { let from = from_keypair.pubkey(); - let plan = Plan { - if_all: ( - vec![Condition::Timestamp(dt)], + let plan = Plan::Race( + Box::new(Plan::After( + Condition::Timestamp(dt), Action::Pay(Payment { asset: asset.clone(), to, }), - ), - unless_any: ( - vec![Condition::Signature(from)], + )), + Box::new(Plan::After( + Condition::Signature(from), Action::Pay(Payment { asset: asset.clone(), to: from, }), - ), - }; + )), + ); let mut tr = Transaction { from, plan, @@ -111,14 +93,7 @@ impl Transaction { } fn get_sign_data(&self) -> Vec { - let plan = &self.plan; - serialize(&( - &self.from, - &plan.if_all, - &plan.unless_any, - &self.asset, - &self.last_id, - )).unwrap() + serialize(&(&self.from, &self.plan, &self.asset, &self.last_id)).unwrap() } pub fn sign(&mut self, keypair: &KeyPair) { @@ -159,22 +134,10 @@ mod tests { #[test] fn test_serialize_claim() { - let plan = Plan { - 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 plan = Plan::Action(Action::Pay(Payment { + asset: 0, + to: Default::default(), + })); let claim0 = Transaction { from: Default::default(), plan, @@ -208,10 +171,9 @@ mod tests { let asset = hash(b"hello, world"); let mut tr = Transaction::new(&keypair0, pubkey1, asset, zero); tr.sign(&keypair0); - tr.plan.if_all.1 = Action::Pay(Payment { - asset, - to: thief_keypair.pubkey(), - }); // <-- attack! + if let Plan::Action(Action::Pay(ref mut payment)) = tr.plan { + payment.to = thief_keypair.pubkey(); // <-- attack! + }; assert!(!tr.verify()); } }