Skip to content
This repository was archived by the owner on Jan 22, 2025. It is now read-only.

Commit ad6665c

Browse files
committed
Complete timestamp and signature transactions
1 parent 923162a commit ad6665c

File tree

1 file changed

+88
-14
lines changed

1 file changed

+88
-14
lines changed

src/accountant.rs

+88-14
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
use hash::Hash;
66
use entry::Entry;
77
use event::Event;
8-
use transaction::Transaction;
8+
use transaction::{Condition, Transaction};
99
use signature::{KeyPair, PublicKey, Signature};
1010
use mint::Mint;
1111
use historian::{reserve_signature, Historian};
@@ -101,6 +101,7 @@ impl Accountant {
101101
Ok(())
102102
}
103103

104+
/// Commit funds to the 'to' party.
104105
fn complete_transaction(self: &mut Self, tr: &Transaction<i64>) {
105106
if self.balances.contains_key(&tr.to) {
106107
if let Some(x) = self.balances.get_mut(&tr.to) {
@@ -111,6 +112,13 @@ impl Accountant {
111112
}
112113
}
113114

115+
/// Return funds to the 'from' party.
116+
fn cancel_transaction(self: &mut Self, tr: &Transaction<i64>) {
117+
if let Some(x) = self.balances.get_mut(&tr.from) {
118+
*x += tr.asset;
119+
}
120+
}
121+
114122
fn process_verified_transaction(
115123
self: &mut Self,
116124
tr: &Transaction<i64>,
@@ -139,17 +147,32 @@ impl Accountant {
139147
Ok(())
140148
}
141149

142-
fn process_verified_sig(&mut self, _from: PublicKey, tx_sig: Signature) -> Result<()> {
143-
if self.pending.contains_key(&tx_sig) {
144-
if let Some(_tx) = self.pending.get_mut(&tx_sig) {
145-
// Cancel:
146-
// if Signature(from) is in unless_any, return funds to tx.from, and remove the tx from this map.
150+
fn process_verified_sig(&mut self, from: PublicKey, tx_sig: Signature) -> Result<()> {
151+
let mut cancel = false;
152+
if let Some(tr) = self.pending.get(&tx_sig) {
153+
// Cancel:
154+
// if Signature(from) is in unless_any, return funds to tx.from, and remove the tx from this map.
155+
156+
// TODO: Use find().
157+
for cond in &tr.unless_any {
158+
if let Condition::Signature(pubkey) = *cond {
159+
if from == pubkey {
160+
cancel = true;
161+
break;
162+
}
163+
}
164+
}
165+
}
147166

148-
// Process Multisig:
149-
// otherwise, if "Signature(from) is in if_all, remove it. If that causes that list
150-
// to be empty, add the asset to to, and remove the tx from this map.
167+
if cancel {
168+
if let Some(tr) = self.pending.remove(&tx_sig) {
169+
self.cancel_transaction(&tr);
151170
}
152171
}
172+
173+
// Process Multisig:
174+
// otherwise, if "Signature(from) is in if_all, remove it. If that causes that list
175+
// to be empty, add the asset to to, and remove the tx from this map.
153176
Ok(())
154177
}
155178

@@ -164,16 +187,40 @@ impl Accountant {
164187
if dt > self.last_time {
165188
self.last_time = dt;
166189
}
190+
} else {
191+
return Ok(());
167192
}
168193
// TODO: Lookup pending Transaction waiting on time, signed by a whitelisted PublicKey.
169194

170195
// Expire:
171196
// if a Timestamp after this DateTime is in unless_any, return funds to tx.from,
172197
// and remove the tx from this map.
173198

174-
// Process postponed:
175-
// otherwise, if "Timestamp(dt) >= self.last_time" is in if_all, remove it. If that causes that list
176-
// to be empty, add the asset to to, and remove the tx from this map.
199+
// Check to see if any timelocked transactions can be completed.
200+
let mut completed = vec![];
201+
for (key, tr) in &self.pending {
202+
for cond in &tr.if_all {
203+
if let Condition::Timestamp(dt) = *cond {
204+
if self.last_time >= dt {
205+
if tr.if_all.len() == 1 {
206+
completed.push(*key);
207+
}
208+
}
209+
}
210+
}
211+
// TODO: Add this in once we start removing constraints
212+
//if tr.if_all.is_empty() {
213+
// // TODO: Remove tr from pending
214+
// self.complete_transaction(tr);
215+
//}
216+
}
217+
218+
for key in completed {
219+
if let Some(tr) = self.pending.remove(&key) {
220+
self.complete_transaction(&tr);
221+
}
222+
}
223+
177224
Ok(())
178225
}
179226

@@ -296,8 +343,35 @@ mod tests {
296343
// Now, acknowledge the time in the condition occurred and
297344
// that bob's funds are now available.
298345
acc.process_verified_timestamp(alice.pubkey(), dt).unwrap();
346+
assert_eq!(acc.get_balance(&bob_pubkey), Some(1));
347+
348+
acc.process_verified_timestamp(alice.pubkey(), dt).unwrap(); // <-- Attack! Attempt to process completed transaction.
349+
assert_ne!(acc.get_balance(&bob_pubkey), Some(2));
350+
}
351+
352+
#[test]
353+
fn test_cancel_transfer() {
354+
let alice = Mint::new(1);
355+
let mut acc = Accountant::new(&alice, Some(2));
356+
let alice_keypair = alice.keypair();
357+
let bob_pubkey = KeyPair::new().pubkey();
358+
let dt = Utc::now();
359+
let sig = acc.transfer_on_date(1, &alice_keypair, bob_pubkey, dt)
360+
.unwrap();
361+
362+
// Alice's balance will be zero because all funds are locked up.
363+
assert_eq!(acc.get_balance(&alice.pubkey()), Some(0));
364+
365+
// Bob's balance will be None because the funds have not been
366+
// sent.
367+
assert_eq!(acc.get_balance(&bob_pubkey), None);
368+
369+
// Now, cancel the trancaction. Alice gets her funds back, Bob never sees them.
370+
acc.process_verified_sig(alice.pubkey(), sig).unwrap();
371+
assert_eq!(acc.get_balance(&alice.pubkey()), Some(1));
372+
assert_eq!(acc.get_balance(&bob_pubkey), None);
299373

300-
// TODO: Uncomment this once process_verified_timestamp is implemented.
301-
//assert_eq!(acc.get_balance(&bob_pubkey), Some(1));
374+
acc.process_verified_sig(alice.pubkey(), sig).unwrap(); // <-- Attack! Attempt to cancel completed transaction.
375+
assert_ne!(acc.get_balance(&alice.pubkey()), Some(2));
302376
}
303377
}

0 commit comments

Comments
 (0)