Complete timestamp and signature transactions
This commit is contained in:
		| @@ -5,7 +5,7 @@ | ||||
| use hash::Hash; | ||||
| use entry::Entry; | ||||
| use event::Event; | ||||
| use transaction::Transaction; | ||||
| use transaction::{Condition, Transaction}; | ||||
| use signature::{KeyPair, PublicKey, Signature}; | ||||
| use mint::Mint; | ||||
| use historian::{reserve_signature, Historian}; | ||||
| @@ -101,6 +101,7 @@ impl Accountant { | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     /// Commit funds to the 'to' party. | ||||
|     fn complete_transaction(self: &mut Self, tr: &Transaction<i64>) { | ||||
|         if self.balances.contains_key(&tr.to) { | ||||
|             if let Some(x) = self.balances.get_mut(&tr.to) { | ||||
| @@ -111,6 +112,13 @@ impl Accountant { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// Return funds to the 'from' party. | ||||
|     fn cancel_transaction(self: &mut Self, tr: &Transaction<i64>) { | ||||
|         if let Some(x) = self.balances.get_mut(&tr.from) { | ||||
|             *x += tr.asset; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fn process_verified_transaction( | ||||
|         self: &mut Self, | ||||
|         tr: &Transaction<i64>, | ||||
| @@ -139,17 +147,32 @@ impl Accountant { | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
|     fn process_verified_sig(&mut self, _from: PublicKey, tx_sig: Signature) -> Result<()> { | ||||
|         if self.pending.contains_key(&tx_sig) { | ||||
|             if let Some(_tx) = self.pending.get_mut(&tx_sig) { | ||||
|                 // Cancel: | ||||
|                 // if Signature(from) is in unless_any, return funds to tx.from, and remove the tx from this map. | ||||
|     fn process_verified_sig(&mut self, from: PublicKey, tx_sig: Signature) -> Result<()> { | ||||
|         let mut cancel = false; | ||||
|         if let Some(tr) = self.pending.get(&tx_sig) { | ||||
|             // Cancel: | ||||
|             // if Signature(from) is in unless_any, return funds to tx.from, and remove the tx from this map. | ||||
|  | ||||
|                 // Process Multisig: | ||||
|                 // otherwise, if "Signature(from) is in if_all, remove it. If that causes that list | ||||
|                 // to be empty, add the asset to to, and remove the tx from this map. | ||||
|             // TODO: Use find(). | ||||
|             for cond in &tr.unless_any { | ||||
|                 if let Condition::Signature(pubkey) = *cond { | ||||
|                     if from == pubkey { | ||||
|                         cancel = true; | ||||
|                         break; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if cancel { | ||||
|             if let Some(tr) = self.pending.remove(&tx_sig) { | ||||
|                 self.cancel_transaction(&tr); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Process Multisig: | ||||
|         // otherwise, if "Signature(from) is in if_all, remove it. If that causes that list | ||||
|         // to be empty, add the asset to to, and remove the tx from this map. | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
| @@ -164,6 +187,8 @@ impl Accountant { | ||||
|             if dt > self.last_time { | ||||
|                 self.last_time = dt; | ||||
|             } | ||||
|         } else { | ||||
|             return Ok(()); | ||||
|         } | ||||
|         // TODO: Lookup pending Transaction waiting on time, signed by a whitelisted PublicKey. | ||||
|  | ||||
| @@ -171,9 +196,31 @@ impl Accountant { | ||||
|         // if a Timestamp after this DateTime is in unless_any, return funds to tx.from, | ||||
|         // and remove the tx from this map. | ||||
|  | ||||
|         // Process postponed: | ||||
|         // otherwise, if "Timestamp(dt) >= self.last_time" is in if_all, remove it. If that causes that list | ||||
|         // to be empty, add the asset to to, and remove the tx from this map. | ||||
|         // Check to see if any timelocked transactions can be completed. | ||||
|         let mut completed = vec![]; | ||||
|         for (key, tr) in &self.pending { | ||||
|             for cond in &tr.if_all { | ||||
|                 if let Condition::Timestamp(dt) = *cond { | ||||
|                     if self.last_time >= dt { | ||||
|                         if tr.if_all.len() == 1 { | ||||
|                             completed.push(*key); | ||||
|                         } | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|             // TODO: Add this in once we start removing constraints | ||||
|             //if tr.if_all.is_empty() { | ||||
|             //    // TODO: Remove tr from pending | ||||
|             //    self.complete_transaction(tr); | ||||
|             //} | ||||
|         } | ||||
|  | ||||
|         for key in completed { | ||||
|             if let Some(tr) = self.pending.remove(&key) { | ||||
|                 self.complete_transaction(&tr); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         Ok(()) | ||||
|     } | ||||
|  | ||||
| @@ -296,8 +343,35 @@ mod tests { | ||||
|         // Now, acknowledge the time in the condition occurred and | ||||
|         // that bob's funds are now available. | ||||
|         acc.process_verified_timestamp(alice.pubkey(), dt).unwrap(); | ||||
|         assert_eq!(acc.get_balance(&bob_pubkey), Some(1)); | ||||
|  | ||||
|         // TODO: Uncomment this once process_verified_timestamp is implemented. | ||||
|         //assert_eq!(acc.get_balance(&bob_pubkey), Some(1)); | ||||
|         acc.process_verified_timestamp(alice.pubkey(), dt).unwrap(); // <-- Attack! Attempt to process completed transaction. | ||||
|         assert_ne!(acc.get_balance(&bob_pubkey), Some(2)); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn test_cancel_transfer() { | ||||
|         let alice = Mint::new(1); | ||||
|         let mut acc = Accountant::new(&alice, Some(2)); | ||||
|         let alice_keypair = alice.keypair(); | ||||
|         let bob_pubkey = KeyPair::new().pubkey(); | ||||
|         let dt = Utc::now(); | ||||
|         let sig = acc.transfer_on_date(1, &alice_keypair, bob_pubkey, dt) | ||||
|             .unwrap(); | ||||
|  | ||||
|         // Alice's balance will be zero because all funds are locked up. | ||||
|         assert_eq!(acc.get_balance(&alice.pubkey()), Some(0)); | ||||
|  | ||||
|         // Bob's balance will be None because the funds have not been | ||||
|         // sent. | ||||
|         assert_eq!(acc.get_balance(&bob_pubkey), None); | ||||
|  | ||||
|         // Now, cancel the trancaction. Alice gets her funds back, Bob never sees them. | ||||
|         acc.process_verified_sig(alice.pubkey(), sig).unwrap(); | ||||
|         assert_eq!(acc.get_balance(&alice.pubkey()), Some(1)); | ||||
|         assert_eq!(acc.get_balance(&bob_pubkey), None); | ||||
|  | ||||
|         acc.process_verified_sig(alice.pubkey(), sig).unwrap(); // <-- Attack! Attempt to cancel completed transaction. | ||||
|         assert_ne!(acc.get_balance(&alice.pubkey()), Some(2)); | ||||
|     } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user