5
5
use hash:: Hash ;
6
6
use entry:: Entry ;
7
7
use event:: Event ;
8
- use transaction:: Transaction ;
8
+ use transaction:: { Condition , Transaction } ;
9
9
use signature:: { KeyPair , PublicKey , Signature } ;
10
10
use mint:: Mint ;
11
11
use historian:: { reserve_signature, Historian } ;
@@ -101,6 +101,7 @@ impl Accountant {
101
101
Ok ( ( ) )
102
102
}
103
103
104
+ /// Commit funds to the 'to' party.
104
105
fn complete_transaction ( self : & mut Self , tr : & Transaction < i64 > ) {
105
106
if self . balances . contains_key ( & tr. to ) {
106
107
if let Some ( x) = self . balances . get_mut ( & tr. to ) {
@@ -111,6 +112,13 @@ impl Accountant {
111
112
}
112
113
}
113
114
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
+
114
122
fn process_verified_transaction (
115
123
self : & mut Self ,
116
124
tr : & Transaction < i64 > ,
@@ -139,17 +147,32 @@ impl Accountant {
139
147
Ok ( ( ) )
140
148
}
141
149
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
+ }
147
166
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 ) ;
151
170
}
152
171
}
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.
153
176
Ok ( ( ) )
154
177
}
155
178
@@ -164,16 +187,40 @@ impl Accountant {
164
187
if dt > self . last_time {
165
188
self . last_time = dt;
166
189
}
190
+ } else {
191
+ return Ok ( ( ) ) ;
167
192
}
168
193
// TODO: Lookup pending Transaction waiting on time, signed by a whitelisted PublicKey.
169
194
170
195
// Expire:
171
196
// if a Timestamp after this DateTime is in unless_any, return funds to tx.from,
172
197
// and remove the tx from this map.
173
198
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
+
177
224
Ok ( ( ) )
178
225
}
179
226
@@ -296,8 +343,35 @@ mod tests {
296
343
// Now, acknowledge the time in the condition occurred and
297
344
// that bob's funds are now available.
298
345
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 ) ;
299
373
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 ) ) ;
302
376
}
303
377
}
0 commit comments