Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2017-2024 Taler Systems SA
4 :
5 : TALER is free software; you can redistribute it and/or modify it under the
6 : terms of the GNU General Public License as published by the Free Software
7 : Foundation; either version 3, or (at your option) any later version.
8 :
9 : TALER is distributed in the hope that it will be useful, but WITHOUT ANY
10 : WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 : A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 :
13 : You should have received a copy of the GNU General Public License along with
14 : TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
15 : */
16 : /**
17 : * @file auditor/taler-helper-auditor-wire-debit.c
18 : * @brief audits that wire outgoing transfers match those from an exchange
19 : * database.
20 : * @author Christian Grothoff
21 : * @author Özgür Kesim
22 : *
23 : * - We check that the outgoing wire transfers match those
24 : * given in the 'wire_out' and 'reserve_closures' tables;
25 : * any outgoing transfer MUST have a prior justification,
26 : * so if one is missing we flag it (and never remove it).
27 : * - We check that all wire transfers that should
28 : * have been made, were actually made. If any were not made,
29 : * we flag those, but may remove those flags if we later
30 : * find that the wire transfers were made (wire transfers
31 : * could be delayed due to AML/KYC or core-banking issues).
32 : */
33 : #include "platform.h"
34 : #include <gnunet/gnunet_util_lib.h>
35 : #include <gnunet/gnunet_curl_lib.h>
36 : #include "taler_auditordb_plugin.h"
37 : #include "taler_exchangedb_lib.h"
38 : #include "taler_json_lib.h"
39 : #include "taler_bank_service.h"
40 : #include "taler_signatures.h"
41 : #include "report-lib.h"
42 : #include "taler_dbevents.h"
43 :
44 :
45 : /**
46 : * Maximum number of wire transfers we process per
47 : * (database) transaction.
48 : */
49 : #define MAX_PER_TRANSACTION 1024
50 :
51 : /**
52 : * How much do we allow the bank and the exchange to disagree about
53 : * timestamps? Should be sufficiently large to avoid bogus reports from deltas
54 : * created by imperfect clock synchronization and network delay.
55 : */
56 : #define TIME_TOLERANCE GNUNET_TIME_relative_multiply ( \
57 : GNUNET_TIME_UNIT_MINUTES, \
58 : 15)
59 :
60 :
61 : /**
62 : * How long do we try to long-poll for bank wire transfers?
63 : */
64 : #define MAX_LONGPOLL_DELAY GNUNET_TIME_relative_multiply ( \
65 : GNUNET_TIME_UNIT_HOURS, \
66 : 1)
67 :
68 :
69 : /**
70 : * How long do we wait between polling for bank wire transfers at the minimum?
71 : */
72 : #define MIN_LONGPOLL_DELAY GNUNET_TIME_relative_multiply ( \
73 : GNUNET_TIME_UNIT_MINUTES, \
74 : 5)
75 :
76 :
77 : /**
78 : * Run in test mode. Exit when idle instead of
79 : * going to sleep and waiting for more work.
80 : */
81 : static int test_mode;
82 :
83 :
84 : /**
85 : * Information we keep for each supported account.
86 : */
87 : struct WireAccount
88 : {
89 : /**
90 : * Accounts are kept in a DLL.
91 : */
92 : struct WireAccount *next;
93 :
94 : /**
95 : * Plugins are kept in a DLL.
96 : */
97 : struct WireAccount *prev;
98 :
99 : /**
100 : * Account details.
101 : */
102 : const struct TALER_EXCHANGEDB_AccountInfo *ai;
103 :
104 : /**
105 : * Active wire request for the transaction history.
106 : */
107 : struct TALER_BANK_DebitHistoryHandle *dhh;
108 :
109 : /**
110 : * Task to trigger @e dhh long-polling.
111 : */
112 : struct GNUNET_SCHEDULER_Task *dhh_task;
113 :
114 : /**
115 : * Time when we expect the current @e dhh long-poll
116 : * to finish and we thus could begin another one.
117 : */
118 : struct GNUNET_TIME_Absolute dhh_next;
119 :
120 : /**
121 : * Progress point for this account.
122 : */
123 : uint64_t last_wire_out_serial_id;
124 :
125 : /**
126 : * Initial progress point for this account.
127 : */
128 : uint64_t start_wire_out_serial_id;
129 :
130 : /**
131 : * Where we are in the outbound transaction history.
132 : */
133 : uint64_t wire_off_out;
134 :
135 : /**
136 : * Label under which we store our pp's reserve_in_serial_id.
137 : */
138 : char *label_wire_out_serial_id;
139 :
140 : /**
141 : * Label under which we store our wire_off_out.
142 : */
143 : char *label_wire_off_out;
144 : };
145 :
146 :
147 : /**
148 : * Information we track for a reserve being closed.
149 : */
150 : struct ReserveClosure
151 : {
152 : /**
153 : * Row in the reserves_closed table for this action.
154 : */
155 : uint64_t rowid;
156 :
157 : /**
158 : * When was the reserve closed?
159 : */
160 : struct GNUNET_TIME_Timestamp execution_date;
161 :
162 : /**
163 : * Amount transferred (amount remaining minus fee).
164 : */
165 : struct TALER_Amount amount;
166 :
167 : /**
168 : * Target account where the money was sent.
169 : */
170 : struct TALER_FullPayto receiver_account;
171 :
172 : /**
173 : * Wire transfer subject used.
174 : */
175 : struct TALER_WireTransferIdentifierRawP wtid;
176 : };
177 :
178 :
179 : /**
180 : * Map from H(wtid,receiver_account) to `struct ReserveClosure` entries.
181 : */
182 : static struct GNUNET_CONTAINER_MultiHashMap *reserve_closures;
183 :
184 : /**
185 : * Return value from main().
186 : */
187 : static int global_ret;
188 :
189 : /**
190 : * State of the current database transaction with
191 : * the auditor DB.
192 : */
193 : static enum GNUNET_DB_QueryStatus global_qs;
194 :
195 : /**
196 : * Map with information about outgoing wire transfers.
197 : * Maps hashes of the wire subjects (in binary encoding)
198 : * to `struct ReserveOutInfo`s.
199 : */
200 : static struct GNUNET_CONTAINER_MultiHashMap *out_map;
201 :
202 : /**
203 : * Head of list of wire accounts we still need to look at.
204 : */
205 : static struct WireAccount *wa_head;
206 :
207 : /**
208 : * Tail of list of wire accounts we still need to look at.
209 : */
210 : static struct WireAccount *wa_tail;
211 :
212 : /**
213 : * Last reserve_out / wire_out serial IDs seen.
214 : */
215 : static TALER_ARL_DEF_PP (wire_reserve_close_id);
216 :
217 : /**
218 : * Total amount that was transferred too much from the exchange.
219 : */
220 : static TALER_ARL_DEF_AB (total_bad_amount_out_plus);
221 :
222 : /**
223 : * Total amount that was transferred too little from the exchange.
224 : */
225 : static TALER_ARL_DEF_AB (total_bad_amount_out_minus);
226 :
227 : /**
228 : * Total amount of reserve closures which the exchange did not transfer in time.
229 : */
230 : static TALER_ARL_DEF_AB (total_closure_amount_lag);
231 :
232 : /**
233 : * Total amount affected by duplicate wire transfer
234 : * subjects.
235 : */
236 : static TALER_ARL_DEF_AB (wire_debit_duplicate_transfer_subject_total);
237 :
238 : /**
239 : * Total amount debited to exchange accounts.
240 : */
241 : static TALER_ARL_DEF_AB (total_wire_out);
242 :
243 : /**
244 : * Total amount of profits drained.
245 : */
246 : static TALER_ARL_DEF_AB (total_drained);
247 :
248 : /**
249 : * Amount of zero in our currency.
250 : */
251 : static struct TALER_Amount zero;
252 :
253 : /**
254 : * Handle to the context for interacting with the bank.
255 : */
256 : static struct GNUNET_CURL_Context *ctx;
257 :
258 : /**
259 : * Scheduler context for running the @e ctx.
260 : */
261 : static struct GNUNET_CURL_RescheduleContext *rctx;
262 :
263 : /**
264 : * Should we run checks that only work for exchange-internal audits?
265 : */
266 : static int internal_checks;
267 :
268 : /**
269 : * Should we ignore if the bank does not know our bank
270 : * account?
271 : */
272 : static int ignore_account_404;
273 :
274 : /**
275 : * Database event handler to wake us up again.
276 : */
277 : static struct GNUNET_DB_EventHandler *eh;
278 :
279 : /**
280 : * The auditors's configuration.
281 : */
282 : static const struct GNUNET_CONFIGURATION_Handle *cfg;
283 :
284 :
285 : /**
286 : * Entry in map with wire information we expect to obtain from the
287 : * #TALER_ARL_edb later.
288 : */
289 : struct WireTransferOutInfo
290 : {
291 :
292 : /**
293 : * Hash of the wire transfer subject.
294 : */
295 : struct GNUNET_HashCode subject_hash;
296 :
297 : /**
298 : * Expected details about the wire transfer.
299 : */
300 : struct TALER_BANK_DebitDetails details;
301 :
302 : };
303 :
304 :
305 : /**
306 : * Free entry in #out_map.
307 : *
308 : * @param cls NULL
309 : * @param key unused key
310 : * @param value the `struct WireTransferOutInfo` to free
311 : * @return #GNUNET_OK
312 : */
313 : static enum GNUNET_GenericReturnValue
314 16 : free_roi (void *cls,
315 : const struct GNUNET_HashCode *key,
316 : void *value)
317 : {
318 16 : struct WireTransferOutInfo *roi = value;
319 :
320 : (void) cls;
321 16 : GNUNET_assert (GNUNET_YES ==
322 : GNUNET_CONTAINER_multihashmap_remove (out_map,
323 : key,
324 : roi));
325 16 : GNUNET_free (roi);
326 16 : return GNUNET_OK;
327 : }
328 :
329 :
330 : /**
331 : * Free entry in #reserve_closures.
332 : *
333 : * @param cls NULL
334 : * @param key unused key
335 : * @param value the `struct ReserveClosure` to free
336 : * @return #GNUNET_OK
337 : */
338 : static enum GNUNET_GenericReturnValue
339 2 : free_rc (void *cls,
340 : const struct GNUNET_HashCode *key,
341 : void *value)
342 : {
343 2 : struct ReserveClosure *rc = value;
344 :
345 : (void) cls;
346 2 : GNUNET_assert (GNUNET_YES ==
347 : GNUNET_CONTAINER_multihashmap_remove (reserve_closures,
348 : key,
349 : rc));
350 2 : GNUNET_free (rc->receiver_account.full_payto);
351 2 : GNUNET_free (rc);
352 2 : return GNUNET_OK;
353 : }
354 :
355 :
356 : /**
357 : * Task run on shutdown.
358 : *
359 : * @param cls NULL
360 : */
361 : static void
362 68 : do_shutdown (void *cls)
363 : {
364 : struct WireAccount *wa;
365 :
366 : (void) cls;
367 68 : if (NULL != eh)
368 : {
369 0 : TALER_ARL_adb->event_listen_cancel (eh);
370 0 : eh = NULL;
371 : }
372 68 : TALER_ARL_done ();
373 68 : if (NULL != reserve_closures)
374 : {
375 68 : GNUNET_CONTAINER_multihashmap_iterate (reserve_closures,
376 : &free_rc,
377 : NULL);
378 68 : GNUNET_CONTAINER_multihashmap_destroy (reserve_closures);
379 68 : reserve_closures = NULL;
380 : }
381 68 : if (NULL != out_map)
382 : {
383 68 : GNUNET_CONTAINER_multihashmap_iterate (out_map,
384 : &free_roi,
385 : NULL);
386 68 : GNUNET_CONTAINER_multihashmap_destroy (out_map);
387 68 : out_map = NULL;
388 : }
389 136 : while (NULL != (wa = wa_head))
390 : {
391 68 : if (NULL != wa->dhh_task)
392 : {
393 68 : GNUNET_SCHEDULER_cancel (wa->dhh_task);
394 68 : wa->dhh_task = NULL;
395 : }
396 68 : if (NULL != wa->dhh)
397 : {
398 0 : TALER_BANK_debit_history_cancel (wa->dhh);
399 0 : wa->dhh = NULL;
400 : }
401 68 : GNUNET_CONTAINER_DLL_remove (wa_head,
402 : wa_tail,
403 : wa);
404 68 : GNUNET_free (wa->label_wire_out_serial_id);
405 68 : GNUNET_free (wa->label_wire_off_out);
406 68 : GNUNET_free (wa);
407 : }
408 68 : if (NULL != ctx)
409 : {
410 68 : GNUNET_CURL_fini (ctx);
411 68 : ctx = NULL;
412 : }
413 68 : if (NULL != rctx)
414 : {
415 68 : GNUNET_CURL_gnunet_rc_destroy (rctx);
416 68 : rctx = NULL;
417 : }
418 68 : TALER_EXCHANGEDB_unload_accounts ();
419 68 : TALER_ARL_cfg = NULL;
420 68 : }
421 :
422 :
423 : /**
424 : * Detect any entries in #reserve_closures that were not yet
425 : * observed on the wire transfer side and update the progress
426 : * point accordingly.
427 : *
428 : * @param cls NULL
429 : * @param key unused key
430 : * @param value the `struct ReserveClosure` to free
431 : * @return #GNUNET_OK
432 : */
433 : static enum GNUNET_GenericReturnValue
434 1 : check_pending_rc (void *cls,
435 : const struct GNUNET_HashCode *key,
436 : void *value)
437 : {
438 1 : struct ReserveClosure *rc = value;
439 :
440 : (void) cls;
441 : (void) key;
442 1 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
443 : "Missing wire transfer for closed reserve with balance %s\n",
444 : TALER_amount2s (&rc->amount));
445 1 : if (! TALER_amount_is_zero (&rc->amount))
446 : {
447 1 : struct TALER_AUDITORDB_ClosureLags cl = {
448 1 : .problem_row_id = rc->rowid,
449 : .account = rc->receiver_account,
450 : .amount = rc->amount,
451 : .deadline = rc->execution_date.abs_time,
452 : .wtid = rc->wtid
453 : };
454 : enum GNUNET_DB_QueryStatus qs;
455 :
456 1 : qs = TALER_ARL_adb->insert_auditor_closure_lags (
457 1 : TALER_ARL_adb->cls,
458 : &cl);
459 1 : if (qs < 0)
460 : {
461 0 : global_qs = qs;
462 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
463 0 : return GNUNET_SYSERR;
464 : }
465 1 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_closure_amount_lag),
466 : &TALER_ARL_USE_AB (total_closure_amount_lag),
467 : &rc->amount);
468 : }
469 1 : return GNUNET_OK;
470 : }
471 :
472 :
473 : /**
474 : * Compute the key under which a reserve closure for a given
475 : * @a receiver_account and @a wtid would be stored.
476 : *
477 : * @param receiver_account payto://-URI of the account
478 : * @param wtid wire transfer identifier used
479 : * @param[out] key set to the key
480 : */
481 : static void
482 5 : hash_rc (const struct TALER_FullPayto receiver_account,
483 : const struct TALER_WireTransferIdentifierRawP *wtid,
484 : struct GNUNET_HashCode *key)
485 5 : {
486 : struct TALER_NormalizedPayto npto
487 5 : = TALER_payto_normalize (receiver_account);
488 5 : size_t slen = strlen (npto.normalized_payto);
489 5 : char buf[sizeof (struct TALER_WireTransferIdentifierRawP) + slen];
490 :
491 5 : GNUNET_memcpy (buf,
492 : wtid,
493 : sizeof (*wtid));
494 5 : GNUNET_memcpy (&buf[sizeof (*wtid)],
495 : npto.normalized_payto,
496 : slen);
497 5 : GNUNET_CRYPTO_hash (buf,
498 : sizeof (buf),
499 : key);
500 5 : GNUNET_free (npto.normalized_payto);
501 5 : }
502 :
503 :
504 : /**
505 : * Start the database transactions and begin the audit.
506 : *
507 : * @return transaction status code
508 : */
509 : static enum GNUNET_DB_QueryStatus
510 : begin_transaction (void);
511 :
512 :
513 : /**
514 : * Commit the transaction, checkpointing our progress in the auditor DB.
515 : *
516 : * @param qs transaction status so far
517 : */
518 : static void
519 68 : commit (enum GNUNET_DB_QueryStatus qs)
520 : {
521 68 : GNUNET_CONTAINER_multihashmap_iterate (reserve_closures,
522 : &check_pending_rc,
523 : NULL);
524 68 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
525 : "Transaction logic ended with status %d\n",
526 : qs);
527 68 : TALER_ARL_edb->rollback (TALER_ARL_edb->cls);
528 68 : qs = TALER_ARL_adb->update_balance (
529 68 : TALER_ARL_adb->cls,
530 : TALER_ARL_SET_AB (total_drained),
531 : TALER_ARL_SET_AB (total_wire_out),
532 : TALER_ARL_SET_AB (total_bad_amount_out_plus),
533 : TALER_ARL_SET_AB (total_bad_amount_out_minus),
534 : TALER_ARL_SET_AB (total_closure_amount_lag),
535 : TALER_ARL_SET_AB (wire_debit_duplicate_transfer_subject_total),
536 : TALER_ARL_SET_AB (total_wire_out),
537 : NULL);
538 68 : if (0 > qs)
539 0 : goto handle_db_error;
540 68 : qs = TALER_ARL_adb->insert_balance (
541 68 : TALER_ARL_adb->cls,
542 : TALER_ARL_SET_AB (total_drained),
543 : TALER_ARL_SET_AB (total_wire_out),
544 : TALER_ARL_SET_AB (total_bad_amount_out_plus),
545 : TALER_ARL_SET_AB (total_bad_amount_out_minus),
546 : TALER_ARL_SET_AB (total_closure_amount_lag),
547 : TALER_ARL_SET_AB (wire_debit_duplicate_transfer_subject_total),
548 : TALER_ARL_SET_AB (total_wire_out),
549 : NULL);
550 68 : if (0 > qs)
551 0 : goto handle_db_error;
552 68 : for (struct WireAccount *wa = wa_head;
553 136 : NULL != wa;
554 68 : wa = wa->next)
555 : {
556 68 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
557 : "Transaction of account %s ends at %llu/%llu\n",
558 : wa->ai->section_name,
559 : (unsigned long long) wa->last_wire_out_serial_id,
560 : (unsigned long long) wa->wire_off_out);
561 68 : qs = TALER_ARL_adb->update_auditor_progress (
562 68 : TALER_ARL_adb->cls,
563 68 : wa->label_wire_out_serial_id,
564 : wa->last_wire_out_serial_id,
565 : wa->label_wire_off_out,
566 : wa->wire_off_out,
567 : NULL);
568 68 : if (0 > qs)
569 0 : goto handle_db_error;
570 68 : qs = TALER_ARL_adb->insert_auditor_progress (
571 68 : TALER_ARL_adb->cls,
572 68 : wa->label_wire_out_serial_id,
573 : wa->last_wire_out_serial_id,
574 : wa->label_wire_off_out,
575 : wa->wire_off_out,
576 : NULL);
577 68 : if (0 > qs)
578 0 : goto handle_db_error;
579 68 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
580 : "Transaction ends at %s=%llu for account `%s'\n",
581 : wa->label_wire_out_serial_id,
582 : (unsigned long long) wa->last_wire_out_serial_id,
583 : wa->ai->section_name);
584 : }
585 68 : qs = TALER_ARL_adb->update_auditor_progress (
586 68 : TALER_ARL_adb->cls,
587 : TALER_ARL_SET_PP (wire_reserve_close_id),
588 : NULL);
589 68 : if (0 > qs)
590 0 : goto handle_db_error;
591 68 : qs = TALER_ARL_adb->insert_auditor_progress (
592 68 : TALER_ARL_adb->cls,
593 : TALER_ARL_SET_PP (wire_reserve_close_id),
594 : NULL);
595 68 : if (0 > qs)
596 0 : goto handle_db_error;
597 68 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
598 : "Concluded audit step at %llu\n",
599 : (unsigned long long) TALER_ARL_USE_PP (wire_reserve_close_id));
600 68 : qs = TALER_ARL_adb->commit (TALER_ARL_adb->cls);
601 68 : if (0 > qs)
602 0 : goto handle_db_error;
603 68 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
604 : "Transaction concluded!\n");
605 68 : if (1 == test_mode)
606 68 : GNUNET_SCHEDULER_shutdown ();
607 68 : return;
608 0 : handle_db_error:
609 0 : TALER_ARL_adb->rollback (TALER_ARL_adb->cls);
610 0 : for (unsigned int max_retries = 3; max_retries>0; max_retries--)
611 : {
612 0 : if (GNUNET_DB_STATUS_HARD_ERROR == qs)
613 0 : break;
614 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
615 : "Serialization issue, trying again\n");
616 0 : qs = begin_transaction ();
617 : }
618 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
619 : "Hard database error, terminating\n");
620 0 : GNUNET_SCHEDULER_shutdown ();
621 : }
622 :
623 :
624 : /**
625 : * Check that @a want is within #TIME_TOLERANCE of @a have.
626 : * Otherwise report an inconsistency in row @a rowid of @a table.
627 : *
628 : * @param table where is the inconsistency (if any)
629 : * @param rowid what is the row
630 : * @param want what is the expected time
631 : * @param have what is the time we got
632 : * @return true on success, false to abort
633 : */
634 : static bool
635 10 : check_time_difference (const char *table,
636 : uint64_t rowid,
637 : struct GNUNET_TIME_Timestamp want,
638 : struct GNUNET_TIME_Timestamp have)
639 : {
640 : struct GNUNET_TIME_Relative delta;
641 : char *details;
642 :
643 10 : if (GNUNET_TIME_timestamp_cmp (have, >, want))
644 9 : delta = GNUNET_TIME_absolute_get_difference (want.abs_time,
645 : have.abs_time);
646 : else
647 1 : delta = GNUNET_TIME_absolute_get_difference (have.abs_time,
648 : want.abs_time);
649 10 : if (GNUNET_TIME_relative_cmp (delta,
650 : <=,
651 : TIME_TOLERANCE))
652 9 : return true;
653 :
654 1 : GNUNET_asprintf (&details,
655 : "execution date mismatch (%s)",
656 : GNUNET_TIME_relative2s (delta,
657 : true));
658 : {
659 1 : struct TALER_AUDITORDB_RowMinorInconsistencies rmi = {
660 : .row_table = (char *) table,
661 : .problem_row = rowid,
662 : .diagnostic = details
663 : };
664 : enum GNUNET_DB_QueryStatus qs;
665 :
666 1 : qs = TALER_ARL_adb->insert_row_minor_inconsistencies (
667 1 : TALER_ARL_adb->cls,
668 : &rmi);
669 :
670 1 : if (qs < 0)
671 : {
672 0 : global_qs = qs;
673 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
674 0 : GNUNET_free (details);
675 0 : return false;
676 : }
677 : }
678 1 : GNUNET_free (details);
679 1 : return true;
680 : }
681 :
682 :
683 : /**
684 : * Closure for #check_rc_matches
685 : */
686 : struct CheckMatchContext
687 : {
688 :
689 : /**
690 : * Reserve operation looking for a match
691 : */
692 : const struct WireTransferOutInfo *roi;
693 :
694 : /**
695 : * Set to true if we found a match.
696 : */
697 : bool found;
698 : };
699 :
700 :
701 : /**
702 : * Check if any of the reserve closures match the given wire transfer.
703 : *
704 : * @param[in,out] cls a `struct CheckMatchContext`
705 : * @param key key of @a value in #reserve_closures
706 : * @param value a `struct ReserveClosure`
707 : */
708 : static enum GNUNET_GenericReturnValue
709 1 : check_rc_matches (void *cls,
710 : const struct GNUNET_HashCode *key,
711 : void *value)
712 : {
713 1 : struct CheckMatchContext *cmx = cls;
714 1 : struct ReserveClosure *rc = value;
715 :
716 1 : if ((0 == GNUNET_memcmp (&cmx->roi->details.wtid,
717 1 : &rc->wtid)) &&
718 1 : (0 == TALER_full_payto_cmp (rc->receiver_account,
719 2 : cmx->roi->details.credit_account_uri)) &&
720 1 : (0 == TALER_amount_cmp (&rc->amount,
721 1 : &cmx->roi->details.amount)))
722 : {
723 1 : if (! check_time_difference ("reserves_closures",
724 : rc->rowid,
725 : rc->execution_date,
726 1 : cmx->roi->details.execution_date))
727 : {
728 0 : free_rc (NULL,
729 : key,
730 : rc);
731 0 : return GNUNET_SYSERR;
732 : }
733 1 : cmx->found = true;
734 1 : free_rc (NULL,
735 : key,
736 : rc);
737 1 : return GNUNET_NO;
738 : }
739 0 : return GNUNET_OK;
740 : }
741 :
742 :
743 : /**
744 : * Maximum required length for make_missing_diag().
745 : */
746 : #define MAX_DIAG_LEN 128
747 :
748 : /**
749 : * Make diagnostic string for missing wire transfer.
750 : *
751 : * @param[out] diag where to write the diagnostic string
752 : * @param wtid wire transfer ID to include
753 : */
754 : static void
755 3 : make_missing_diag (char diag[MAX_DIAG_LEN],
756 : const struct TALER_WireTransferIdentifierRawP *wtid)
757 : {
758 : char *wtid_s;
759 :
760 3 : wtid_s = GNUNET_STRINGS_data_to_string_alloc (wtid,
761 : sizeof (*wtid));
762 3 : GNUNET_snprintf (diag,
763 : MAX_DIAG_LEN,
764 : "expected outgoing wire transfer %s missing",
765 : wtid_s);
766 3 : GNUNET_free (wtid_s);
767 3 : }
768 :
769 :
770 : /**
771 : * Check if an existing report on a missing wire
772 : * out operation justified the @a roi. If so,
773 : * clear the existing report.
774 : *
775 : * @param roi reserve out operation to check
776 : * @return #GNUNET_YES if @a roi was justified by a previous report,
777 : * #GNUNET_NO of @a roi was not justified by a previous missing report
778 : * #GNUNET_SYSERR on database trouble
779 : */
780 : static enum GNUNET_GenericReturnValue
781 2 : check_reported_inconsistency (struct WireTransferOutInfo *roi)
782 : {
783 : char diag[MAX_DIAG_LEN];
784 2 : struct TALER_AUDITORDB_WireOutInconsistency woi = {
785 2 : .wire_out_row_id = roi->details.serial_id,
786 : .destination_account = roi->details.credit_account_uri,
787 : .diagnostic = diag,
788 : .expected = roi->details.amount,
789 : .claimed = zero,
790 : };
791 : enum GNUNET_DB_QueryStatus qs;
792 :
793 2 : make_missing_diag (diag,
794 2 : &roi->details.wtid);
795 2 : qs = TALER_ARL_adb->delete_wire_out_inconsistency_if_matching (
796 2 : TALER_ARL_adb->cls,
797 : &woi);
798 2 : if (qs < 0)
799 : {
800 0 : global_qs = qs;
801 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
802 0 : return GNUNET_SYSERR;
803 : }
804 2 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
805 : {
806 2 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
807 : "Deletion of wire out inconsistency %llu (%s, %s, %s) failed: not reported missing!\n",
808 : (unsigned long long) roi->details.serial_id,
809 : roi->details.credit_account_uri.full_payto,
810 : diag,
811 : TALER_amount2s (&roi->details.amount));
812 2 : return GNUNET_NO;
813 : }
814 0 : TALER_ARL_amount_subtract (&TALER_ARL_USE_AB (total_bad_amount_out_minus),
815 : &TALER_ARL_USE_AB (total_bad_amount_out_minus),
816 : &roi->details.amount);
817 0 : return GNUNET_YES;
818 : }
819 :
820 :
821 : /**
822 : * Check if a profit drain operation justified the @a roi
823 : *
824 : * @param roi reserve out operation to check
825 : * @return #GNUNET_YES if @a roi was justified by a profit drain,
826 : * #GNUNET_NO of @a roi was not justified by a proft drain
827 : * #GNUNET_SYSERR on database trouble
828 : */
829 : static enum GNUNET_GenericReturnValue
830 2 : check_profit_drain (struct WireTransferOutInfo *roi)
831 : {
832 : enum GNUNET_DB_QueryStatus qs;
833 : uint64_t serial;
834 : char *account_section;
835 : struct TALER_FullPayto payto_uri;
836 : struct GNUNET_TIME_Timestamp request_timestamp;
837 : struct TALER_Amount amount;
838 : struct TALER_MasterSignatureP master_sig;
839 :
840 2 : qs = TALER_ARL_edb->get_drain_profit (
841 2 : TALER_ARL_edb->cls,
842 2 : &roi->details.wtid,
843 : &serial,
844 : &account_section,
845 : &payto_uri,
846 : &request_timestamp,
847 : &amount,
848 : &master_sig);
849 2 : switch (qs)
850 : {
851 0 : case GNUNET_DB_STATUS_HARD_ERROR:
852 0 : GNUNET_break (0);
853 0 : global_ret = EXIT_FAILURE;
854 0 : GNUNET_SCHEDULER_shutdown ();
855 0 : return GNUNET_SYSERR;
856 0 : case GNUNET_DB_STATUS_SOFT_ERROR:
857 : /* should fail on commit later ... */
858 0 : GNUNET_break (0);
859 0 : return GNUNET_SYSERR;
860 1 : case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
861 : /* not a profit drain */
862 1 : return GNUNET_NO;
863 1 : case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
864 1 : break;
865 : }
866 1 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
867 : "Profit drain of %s to %s found!\n",
868 : TALER_amount2s (&amount),
869 : payto_uri.full_payto);
870 1 : if (GNUNET_OK !=
871 1 : TALER_exchange_offline_profit_drain_verify (
872 1 : &roi->details.wtid,
873 : request_timestamp,
874 : &amount,
875 : account_section,
876 : payto_uri,
877 : &TALER_ARL_master_pub,
878 : &master_sig))
879 : {
880 0 : struct TALER_AUDITORDB_RowInconsistency ri = {
881 0 : .row_id = roi->details.serial_id,
882 : .row_table = (char *) "profit_drains",
883 : .diagnostic = (char *) "invalid signature"
884 : };
885 :
886 0 : GNUNET_break (0);
887 0 : qs = TALER_ARL_adb->insert_row_inconsistency (
888 0 : TALER_ARL_adb->cls,
889 : &ri);
890 0 : GNUNET_free (payto_uri.full_payto);
891 0 : GNUNET_free (account_section);
892 0 : if (qs < 0)
893 : {
894 0 : global_qs = qs;
895 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
896 0 : return GNUNET_SYSERR;
897 : }
898 0 : return GNUNET_NO;
899 : }
900 1 : GNUNET_free (account_section);
901 :
902 : {
903 1 : if (0 !=
904 1 : TALER_full_payto_normalize_and_cmp (payto_uri,
905 : roi->details.credit_account_uri))
906 : {
907 0 : struct TALER_AUDITORDB_WireOutInconsistency woi = {
908 : .wire_out_row_id = serial,
909 : .destination_account = roi->details.credit_account_uri,
910 : .diagnostic = (char *) "profit drain wired to invalid account",
911 : .expected = roi->details.amount,
912 : .claimed = zero,
913 : };
914 :
915 0 : qs = TALER_ARL_adb->insert_wire_out_inconsistency (
916 0 : TALER_ARL_adb->cls,
917 : &woi);
918 0 : if (qs < 0)
919 : {
920 0 : global_qs = qs;
921 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
922 0 : GNUNET_free (payto_uri.full_payto);
923 0 : return GNUNET_SYSERR;
924 : }
925 0 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_out_plus),
926 : &TALER_ARL_USE_AB (total_bad_amount_out_plus),
927 : &amount);
928 0 : GNUNET_free (payto_uri.full_payto);
929 0 : return GNUNET_YES; /* justified, kind-of */
930 : }
931 : }
932 1 : GNUNET_free (payto_uri.full_payto);
933 1 : if (0 !=
934 1 : TALER_amount_cmp (&amount,
935 1 : &roi->details.amount))
936 : {
937 0 : struct TALER_AUDITORDB_WireOutInconsistency woi = {
938 0 : .wire_out_row_id = roi->details.serial_id,
939 : .destination_account = roi->details.credit_account_uri,
940 : .diagnostic = (char *) "incorrect amount drained to correct account",
941 : .expected = roi->details.amount,
942 : .claimed = amount,
943 : };
944 :
945 0 : qs = TALER_ARL_adb->insert_wire_out_inconsistency (
946 0 : TALER_ARL_adb->cls,
947 : &woi);
948 0 : if (qs < 0)
949 : {
950 0 : global_qs = qs;
951 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
952 0 : return GNUNET_SYSERR;
953 : }
954 0 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_out_minus),
955 : &TALER_ARL_USE_AB (total_bad_amount_out_minus),
956 : &roi->details.amount);
957 0 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_out_plus),
958 : &TALER_ARL_USE_AB (total_bad_amount_out_plus),
959 : &amount);
960 0 : return GNUNET_YES; /* justified, kind-of */
961 : }
962 : /* profit drain was correct */
963 1 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_drained),
964 : &TALER_ARL_USE_AB (total_drained),
965 : &amount);
966 1 : return GNUNET_YES;
967 : }
968 :
969 :
970 : /**
971 : * Check whether the given transfer was justified because it was
972 : * actually previously reported as missing.
973 : *
974 : * @param roi the reserve out operation to check
975 : * @return #GNUNET_OK on success, #GNUNET_NO if there was no
976 : * matching lag, #GNUNET_SYSERR on database trouble
977 : */
978 : static enum GNUNET_GenericReturnValue
979 1 : check_closure_lag (const struct WireTransferOutInfo *roi)
980 : {
981 : enum GNUNET_DB_QueryStatus qs;
982 :
983 1 : qs = TALER_ARL_adb->delete_auditor_closure_lag (
984 1 : TALER_ARL_adb->cls,
985 : &roi->details.amount,
986 : &roi->details.wtid,
987 : roi->details.credit_account_uri);
988 1 : if (qs < 0)
989 : {
990 0 : global_qs = qs;
991 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
992 0 : return GNUNET_SYSERR;
993 : }
994 1 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
995 1 : return GNUNET_NO;
996 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
997 : "Cleared closure lag: found justification\n");
998 0 : TALER_ARL_amount_subtract (&TALER_ARL_USE_AB (total_closure_amount_lag),
999 : &TALER_ARL_USE_AB (total_closure_amount_lag),
1000 : &roi->details.amount);
1001 0 : return GNUNET_YES; /* found! */
1002 : }
1003 :
1004 :
1005 : /**
1006 : * Check whether the given transfer was justified by a reserve closure or
1007 : * profit drain. If not, complain that we failed to match an entry from
1008 : * #out_map. This means a wire transfer was made without proper
1009 : * justification.
1010 : *
1011 : * @param cls a `struct WireAccount`
1012 : * @param key unused key
1013 : * @param value the `struct WireTransferOutInfo` to report
1014 : * @return #GNUNET_OK on success
1015 : */
1016 : static enum GNUNET_GenericReturnValue
1017 3 : complain_out_not_found (void *cls,
1018 : const struct GNUNET_HashCode *key,
1019 : void *value)
1020 : {
1021 : // struct WireAccount *wa = cls;
1022 3 : struct WireTransferOutInfo *roi = value;
1023 : struct GNUNET_HashCode rkey;
1024 3 : struct CheckMatchContext cmx = {
1025 : .roi = roi,
1026 : .found = false
1027 : };
1028 : enum GNUNET_GenericReturnValue ret;
1029 :
1030 : (void) cls;
1031 : (void) key;
1032 3 : hash_rc (roi->details.credit_account_uri,
1033 3 : &roi->details.wtid,
1034 : &rkey);
1035 3 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1036 : "Checking for reserve closure %s benefiting %s\n",
1037 : GNUNET_h2s (&rkey),
1038 : roi->details.credit_account_uri.full_payto);
1039 3 : GNUNET_CONTAINER_multihashmap_get_multiple (reserve_closures,
1040 : &rkey,
1041 : &check_rc_matches,
1042 : &cmx);
1043 3 : if (cmx.found)
1044 1 : return GNUNET_OK;
1045 2 : ret = check_reported_inconsistency (roi);
1046 2 : if (GNUNET_NO != ret)
1047 0 : return ret;
1048 2 : ret = check_profit_drain (roi);
1049 2 : if (GNUNET_NO != ret)
1050 1 : return ret;
1051 1 : ret = check_closure_lag (roi);
1052 1 : if (GNUNET_NO != ret)
1053 0 : return ret;
1054 :
1055 : {
1056 1 : struct TALER_AUDITORDB_WireOutInconsistency woi = {
1057 : .destination_account = roi->details.credit_account_uri,
1058 : .diagnostic = (char *) "missing justification for outgoing wire transfer",
1059 1 : .wire_out_row_id = roi->details.serial_id,
1060 : .expected = zero,
1061 : .claimed = roi->details.amount
1062 : };
1063 : enum GNUNET_DB_QueryStatus qs;
1064 :
1065 1 : qs = TALER_ARL_adb->insert_wire_out_inconsistency (
1066 1 : TALER_ARL_adb->cls,
1067 : &woi);
1068 1 : if (qs < 0)
1069 : {
1070 0 : global_qs = qs;
1071 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1072 0 : return GNUNET_SYSERR;
1073 : }
1074 : }
1075 1 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_out_plus),
1076 : &TALER_ARL_USE_AB (total_bad_amount_out_plus),
1077 : &roi->details.amount);
1078 1 : return GNUNET_OK;
1079 : }
1080 :
1081 :
1082 : /**
1083 : * Function called with details about outgoing wire transfers
1084 : * as claimed by the exchange DB.
1085 : *
1086 : * @param cls a `struct WireAccount`
1087 : * @param rowid unique serial ID in wire_out table
1088 : * @param date timestamp of the transfer (roughly)
1089 : * @param wtid wire transfer subject
1090 : * @param payto_uri wire transfer details of the receiver
1091 : * @param amount amount that was wired
1092 : * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
1093 : */
1094 : static enum GNUNET_GenericReturnValue
1095 14 : wire_out_cb (
1096 : void *cls,
1097 : uint64_t rowid,
1098 : struct GNUNET_TIME_Timestamp date,
1099 : const struct TALER_WireTransferIdentifierRawP *wtid,
1100 : const struct TALER_FullPayto payto_uri,
1101 : const struct TALER_Amount *amount)
1102 : {
1103 14 : struct WireAccount *wa = cls;
1104 : struct GNUNET_HashCode key;
1105 : struct WireTransferOutInfo *roi;
1106 :
1107 14 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1108 : "Exchange wire OUT #%llu at %s of %s with WTID %s\n",
1109 : (unsigned long long) rowid,
1110 : GNUNET_TIME_timestamp2s (date),
1111 : TALER_amount2s (amount),
1112 : TALER_B2S (wtid));
1113 14 : wa->last_wire_out_serial_id = rowid + 1;
1114 14 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_wire_out),
1115 : &TALER_ARL_USE_AB (total_wire_out),
1116 : amount);
1117 14 : GNUNET_CRYPTO_hash (wtid,
1118 : sizeof (*wtid),
1119 : &key);
1120 14 : roi = GNUNET_CONTAINER_multihashmap_get (out_map,
1121 : &key);
1122 14 : if (NULL == roi)
1123 : {
1124 : /* Wire transfer was not made (yet) at all (but would have been
1125 : justified), so the entire amount is missing / still to be done. This
1126 : is moderately harmless, it might just be that the
1127 : taler-exchange-transfer tool or bank has not yet fully caught up with
1128 : the transfers it should do.
1129 : May be cleared later by check_reported_inconsistency() */
1130 : char diag[MAX_DIAG_LEN];
1131 1 : struct TALER_AUDITORDB_WireOutInconsistency woi = {
1132 : .destination_account = payto_uri,
1133 : .diagnostic = diag,
1134 : .wire_out_row_id = rowid,
1135 : .expected = *amount,
1136 : .claimed = zero,
1137 : };
1138 : enum GNUNET_DB_QueryStatus qs;
1139 :
1140 1 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1141 : "Wire out for row %llu still missing\n",
1142 : (unsigned long long) rowid);
1143 1 : make_missing_diag (diag,
1144 : wtid);
1145 1 : qs = TALER_ARL_adb->insert_wire_out_inconsistency (
1146 1 : TALER_ARL_adb->cls,
1147 : &woi);
1148 1 : if (qs < 0)
1149 : {
1150 0 : global_qs = qs;
1151 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1152 0 : return GNUNET_SYSERR;
1153 : }
1154 1 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_out_minus),
1155 : &TALER_ARL_USE_AB (total_bad_amount_out_minus),
1156 : amount);
1157 1 : return GNUNET_OK;
1158 : }
1159 :
1160 13 : if (0 != TALER_full_payto_normalize_and_cmp (payto_uri,
1161 : roi->details.credit_account_uri))
1162 : {
1163 : /* Destination bank account is wrong in actual wire transfer, so
1164 : we should count the wire transfer as entirely spurious, and
1165 : additionally consider the justified wire transfer as missing. */
1166 0 : struct TALER_AUDITORDB_WireOutInconsistency woi = {
1167 : .wire_out_row_id = rowid,
1168 : .destination_account = payto_uri,
1169 : .diagnostic = (char *) "receiver account mismatch",
1170 : .expected = *amount,
1171 : .claimed = roi->details.amount,
1172 : };
1173 : enum GNUNET_DB_QueryStatus qs;
1174 :
1175 0 : qs = TALER_ARL_adb->insert_wire_out_inconsistency (
1176 0 : TALER_ARL_adb->cls,
1177 : &woi);
1178 0 : if (qs < 0)
1179 : {
1180 0 : global_qs = qs;
1181 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1182 0 : return GNUNET_SYSERR;
1183 : }
1184 0 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_out_plus),
1185 : &TALER_ARL_USE_AB (total_bad_amount_out_plus),
1186 : &roi->details.amount);
1187 0 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_out_minus),
1188 : &TALER_ARL_USE_AB (total_bad_amount_out_minus),
1189 : amount);
1190 0 : GNUNET_assert (GNUNET_OK ==
1191 : free_roi (NULL,
1192 : &key,
1193 : roi));
1194 0 : return GNUNET_OK;
1195 : }
1196 :
1197 13 : if (0 != TALER_amount_cmp (&roi->details.amount,
1198 : amount))
1199 : {
1200 4 : struct TALER_AUDITORDB_WireOutInconsistency woi = {
1201 : .destination_account = payto_uri,
1202 : .diagnostic = (char *) "wire amount does not match",
1203 : .wire_out_row_id = rowid,
1204 : .expected = *amount,
1205 : .claimed = roi->details.amount,
1206 : };
1207 : enum GNUNET_DB_QueryStatus qs;
1208 :
1209 4 : qs = TALER_ARL_adb->insert_wire_out_inconsistency (
1210 4 : TALER_ARL_adb->cls,
1211 : &woi);
1212 4 : if (qs < 0)
1213 : {
1214 0 : global_qs = qs;
1215 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1216 0 : return GNUNET_SYSERR;
1217 : }
1218 4 : if (0 < TALER_amount_cmp (amount,
1219 4 : &roi->details.amount))
1220 : {
1221 : /* amount > roi->details.amount: wire transfer was smaller than it should have been */
1222 : struct TALER_Amount delta;
1223 :
1224 2 : TALER_ARL_amount_subtract (&delta,
1225 : amount,
1226 : &roi->details.amount);
1227 2 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_out_minus),
1228 : &TALER_ARL_USE_AB (total_bad_amount_out_minus),
1229 : &delta);
1230 : }
1231 : else
1232 : {
1233 : /* roi->details.amount < amount: wire transfer was larger than it should have been */
1234 : struct TALER_Amount delta;
1235 :
1236 2 : TALER_ARL_amount_subtract (&delta,
1237 : &roi->details.amount,
1238 : amount);
1239 2 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_out_plus),
1240 : &TALER_ARL_USE_AB (total_bad_amount_out_plus),
1241 : &delta);
1242 : }
1243 4 : GNUNET_assert (GNUNET_OK ==
1244 : free_roi (NULL,
1245 : &key,
1246 : roi));
1247 4 : return GNUNET_OK;
1248 : }
1249 :
1250 : {
1251 : enum GNUNET_GenericReturnValue ret;
1252 :
1253 9 : if (! check_time_difference ("wire_out",
1254 : rowid,
1255 : date,
1256 : roi->details.execution_date))
1257 : {
1258 : /* We had a database error, fail */
1259 0 : ret = GNUNET_SYSERR;
1260 : }
1261 : else
1262 : {
1263 9 : ret = GNUNET_OK;
1264 : }
1265 9 : GNUNET_assert (GNUNET_OK ==
1266 : free_roi (NULL,
1267 : &key,
1268 : roi));
1269 9 : return ret;
1270 : }
1271 : }
1272 :
1273 :
1274 : /**
1275 : * Main function for processing 'debit' data. We start by going over
1276 : * the DEBIT transactions this time, and then verify that all of them are
1277 : * justified by reserve closures, profit drains or regular outgoing
1278 : * wire transfers from aggregated deposits.
1279 : *
1280 : * @param[in,out] wa wire account list to process
1281 : */
1282 : static void
1283 : process_debits (struct WireAccount *wa);
1284 :
1285 :
1286 : /**
1287 : * Go over the "wire_out" table of the exchange and
1288 : * verify that all wire outs are in that table.
1289 : *
1290 : * @param[in,out] wa wire account we are processing
1291 : */
1292 : static void
1293 68 : check_exchange_wire_out (struct WireAccount *wa)
1294 : {
1295 : enum GNUNET_DB_QueryStatus qs;
1296 :
1297 68 : GNUNET_assert (NULL == wa->dhh);
1298 68 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1299 : "Analyzing exchange's wire OUT table for account `%s'\n",
1300 : wa->ai->section_name);
1301 68 : qs = TALER_ARL_edb->select_wire_out_above_serial_id_by_account (
1302 68 : TALER_ARL_edb->cls,
1303 68 : wa->ai->section_name,
1304 : wa->last_wire_out_serial_id,
1305 : &wire_out_cb,
1306 : wa);
1307 68 : if (0 > qs)
1308 : {
1309 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1310 0 : global_ret = EXIT_FAILURE;
1311 0 : GNUNET_SCHEDULER_shutdown ();
1312 0 : return;
1313 : }
1314 68 : GNUNET_CONTAINER_multihashmap_iterate (out_map,
1315 : &complain_out_not_found,
1316 : wa);
1317 : /* clean up */
1318 68 : GNUNET_CONTAINER_multihashmap_iterate (out_map,
1319 : &free_roi,
1320 : NULL);
1321 68 : process_debits (wa->next);
1322 : }
1323 :
1324 :
1325 : /**
1326 : * This function is called for all transactions that
1327 : * are debited from the exchange's account (outgoing
1328 : * transactions).
1329 : *
1330 : * @param cls `struct WireAccount` with current wire account to process
1331 : * @param dhr HTTP response details
1332 : */
1333 : static void
1334 : history_debit_cb (
1335 : void *cls,
1336 : const struct TALER_BANK_DebitHistoryResponse *dhr);
1337 :
1338 :
1339 : /**
1340 : * Task scheduled to begin long-polling on the
1341 : * bank transfer.
1342 : *
1343 : * @param cls a `struct WireAccount *`
1344 : */
1345 : static void
1346 0 : dh_long_poll (void *cls)
1347 : {
1348 0 : struct WireAccount *wa = cls;
1349 :
1350 0 : wa->dhh_task = NULL;
1351 : wa->dhh_next
1352 0 : = GNUNET_TIME_relative_to_absolute (MIN_LONGPOLL_DELAY);
1353 0 : GNUNET_assert (NULL == wa->dhh);
1354 0 : wa->dhh = TALER_BANK_debit_history (
1355 : ctx,
1356 0 : wa->ai->auth,
1357 : wa->wire_off_out,
1358 : MAX_PER_TRANSACTION,
1359 : MAX_LONGPOLL_DELAY,
1360 : &history_debit_cb,
1361 : wa);
1362 0 : if (NULL == wa->dhh)
1363 : {
1364 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1365 : "Failed to start long-polling for bank transaction history for `%s'\n",
1366 : wa->ai->section_name);
1367 0 : global_ret = EXIT_FAILURE;
1368 0 : GNUNET_SCHEDULER_shutdown ();
1369 0 : return;
1370 : }
1371 : }
1372 :
1373 :
1374 : static void
1375 68 : history_debit_cb (
1376 : void *cls,
1377 : const struct TALER_BANK_DebitHistoryResponse *dhr)
1378 : {
1379 68 : struct WireAccount *wa = cls;
1380 : struct WireTransferOutInfo *roi;
1381 : size_t slen;
1382 :
1383 68 : wa->dhh = NULL;
1384 68 : if ( (MHD_HTTP_OK == dhr->http_status) &&
1385 14 : (0 != dhr->details.ok.details_length) )
1386 : {
1387 : /* As we got results, we go again *immediately* */
1388 14 : wa->dhh_next = GNUNET_TIME_UNIT_ZERO_ABS;
1389 : }
1390 68 : GNUNET_assert (NULL == wa->dhh_task);
1391 : wa->dhh_task
1392 68 : = GNUNET_SCHEDULER_add_at (wa->dhh_next,
1393 : &dh_long_poll,
1394 : wa);
1395 68 : switch (dhr->http_status)
1396 : {
1397 14 : case MHD_HTTP_OK:
1398 30 : for (unsigned int i = 0; i < dhr->details.ok.details_length; i++)
1399 : {
1400 16 : const struct TALER_BANK_DebitDetails *dd
1401 16 : = &dhr->details.ok.details[i];
1402 :
1403 16 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1404 : "Analyzing bank DEBIT #%llu at %s of %s with WTID %s\n",
1405 : (unsigned long long) dd->serial_id,
1406 : GNUNET_TIME_timestamp2s (dd->execution_date),
1407 : TALER_amount2s (&dd->amount),
1408 : TALER_B2S (&dd->wtid));
1409 16 : wa->wire_off_out = dd->serial_id + 1;
1410 16 : slen = strlen (dd->credit_account_uri.full_payto) + 1;
1411 16 : roi = GNUNET_malloc (sizeof (struct WireTransferOutInfo)
1412 : + slen);
1413 16 : GNUNET_CRYPTO_hash (&dd->wtid,
1414 : sizeof (dd->wtid),
1415 : &roi->subject_hash);
1416 16 : roi->details = *dd;
1417 : roi->details.credit_account_uri.full_payto
1418 16 : = (char *) &roi[1];
1419 16 : GNUNET_memcpy (&roi[1],
1420 : dd->credit_account_uri.full_payto,
1421 : slen);
1422 16 : if (GNUNET_OK !=
1423 16 : GNUNET_CONTAINER_multihashmap_put (out_map,
1424 16 : &roi->subject_hash,
1425 : roi,
1426 : GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
1427 : {
1428 0 : struct TALER_AUDITORDB_WireFormatInconsistency wfi = {
1429 : .amount = dd->amount,
1430 0 : .wire_offset = dd->serial_id,
1431 : .diagnostic = (char *) "duplicate outgoing wire transfer subject"
1432 : };
1433 : enum GNUNET_DB_QueryStatus qs;
1434 :
1435 0 : qs = TALER_ARL_adb->insert_wire_format_inconsistency (
1436 0 : TALER_ARL_adb->cls,
1437 : &wfi);
1438 0 : if (qs < 0)
1439 : {
1440 0 : global_qs = qs;
1441 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1442 0 : commit (qs);
1443 0 : return;
1444 : }
1445 0 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (
1446 : wire_debit_duplicate_transfer_subject_total),
1447 : &TALER_ARL_USE_AB (
1448 : wire_debit_duplicate_transfer_subject_total),
1449 : &dd->amount);
1450 : }
1451 : }
1452 14 : check_exchange_wire_out (wa);
1453 14 : return;
1454 54 : case MHD_HTTP_NO_CONTENT:
1455 54 : check_exchange_wire_out (wa);
1456 54 : return;
1457 0 : case MHD_HTTP_NOT_FOUND:
1458 0 : if (ignore_account_404)
1459 : {
1460 0 : check_exchange_wire_out (wa);
1461 0 : return;
1462 : }
1463 0 : break;
1464 0 : default:
1465 0 : break;
1466 : }
1467 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1468 : "Error fetching debit history of account %s: %u/%u!\n",
1469 : wa->ai->section_name,
1470 : dhr->http_status,
1471 : (unsigned int) dhr->ec);
1472 0 : commit (GNUNET_DB_STATUS_HARD_ERROR);
1473 0 : global_ret = EXIT_FAILURE;
1474 0 : GNUNET_SCHEDULER_shutdown ();
1475 0 : return;
1476 : }
1477 :
1478 :
1479 : static void
1480 136 : process_debits (struct WireAccount *wa)
1481 : {
1482 : /* skip accounts where DEBIT is not enabled */
1483 136 : while ( (NULL != wa) &&
1484 68 : (! wa->ai->debit_enabled) )
1485 0 : wa = wa->next;
1486 136 : if (NULL == wa)
1487 : {
1488 : /* end of iteration */
1489 68 : commit (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
1490 68 : return;
1491 : }
1492 68 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1493 : "Checking bank DEBIT records of account `%s'\n",
1494 : wa->ai->section_name);
1495 68 : if ( (NULL == wa->dhh) &&
1496 68 : (NULL == wa->dhh_task) )
1497 : {
1498 136 : wa->dhh = TALER_BANK_debit_history (
1499 : ctx,
1500 68 : wa->ai->auth,
1501 : wa->wire_off_out,
1502 : MAX_PER_TRANSACTION,
1503 68 : GNUNET_TIME_UNIT_ZERO,
1504 : &history_debit_cb,
1505 : wa);
1506 68 : if (NULL == wa->dhh)
1507 : {
1508 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1509 : "Failed to obtain bank transaction history for `%s'\n",
1510 : wa->ai->section_name);
1511 0 : commit (GNUNET_DB_STATUS_HARD_ERROR);
1512 0 : global_ret = EXIT_FAILURE;
1513 0 : GNUNET_SCHEDULER_shutdown ();
1514 0 : return;
1515 : }
1516 : }
1517 : }
1518 :
1519 :
1520 : /**
1521 : * Function called about reserve closing operations the aggregator triggered.
1522 : *
1523 : * @param cls closure; NULL
1524 : * @param rowid row identifier used to uniquely identify the reserve closing operation
1525 : * @param execution_date when did we execute the close operation
1526 : * @param amount_with_fee how much did we debit the reserve
1527 : * @param closing_fee how much did we charge for closing the reserve
1528 : * @param reserve_pub public key of the reserve
1529 : * @param receiver_account where did we send the funds, in payto://-format
1530 : * @param wtid identifier used for the wire transfer
1531 : * @param close_request_row which close request triggered the operation?
1532 : * 0 if it was a timeout (not used)
1533 : * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
1534 : */
1535 : static enum GNUNET_GenericReturnValue
1536 2 : reserve_closed_cb (
1537 : void *cls,
1538 : uint64_t rowid,
1539 : struct GNUNET_TIME_Timestamp execution_date,
1540 : const struct TALER_Amount *amount_with_fee,
1541 : const struct TALER_Amount *closing_fee,
1542 : const struct TALER_ReservePublicKeyP *reserve_pub,
1543 : const struct TALER_FullPayto receiver_account,
1544 : const struct TALER_WireTransferIdentifierRawP *wtid,
1545 : uint64_t close_request_row)
1546 : {
1547 : struct ReserveClosure *rc;
1548 : struct GNUNET_HashCode key;
1549 :
1550 : (void) cls;
1551 : (void) close_request_row;
1552 2 : GNUNET_assert (TALER_ARL_USE_PP (wire_reserve_close_id) <= rowid);
1553 2 : TALER_ARL_USE_PP (wire_reserve_close_id) = rowid + 1;
1554 2 : rc = GNUNET_new (struct ReserveClosure);
1555 2 : if (TALER_ARL_SR_INVALID_NEGATIVE ==
1556 2 : TALER_ARL_amount_subtract_neg (&rc->amount,
1557 : amount_with_fee,
1558 : closing_fee))
1559 : {
1560 0 : struct TALER_AUDITORDB_RowInconsistency ri = {
1561 : .row_id = rowid,
1562 : .row_table
1563 : = (char *) "reserves_closures",
1564 : .diagnostic
1565 : = (char *) "closing fee above reserve balance (and closed anyway)"
1566 : };
1567 : enum GNUNET_DB_QueryStatus qs;
1568 :
1569 0 : qs = TALER_ARL_adb->insert_row_inconsistency (
1570 0 : TALER_ARL_adb->cls,
1571 : &ri);
1572 0 : if (qs < 0)
1573 : {
1574 0 : global_qs = qs;
1575 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1576 0 : return GNUNET_OK;
1577 : }
1578 0 : GNUNET_free (rc);
1579 0 : return GNUNET_OK;
1580 : }
1581 : rc->receiver_account.full_payto
1582 2 : = GNUNET_strdup (receiver_account.full_payto);
1583 2 : rc->wtid = *wtid;
1584 2 : rc->execution_date = execution_date;
1585 2 : rc->rowid = rowid;
1586 2 : hash_rc (rc->receiver_account,
1587 : wtid,
1588 : &key);
1589 2 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1590 : "Discovered reserve closure %llu (%s) over %s benefiting %s\n",
1591 : (unsigned long long) rowid,
1592 : GNUNET_h2s (&key),
1593 : TALER_amount2s (amount_with_fee),
1594 : receiver_account.full_payto);
1595 2 : (void) GNUNET_CONTAINER_multihashmap_put (
1596 : reserve_closures,
1597 : &key,
1598 : rc,
1599 : GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
1600 2 : return GNUNET_OK;
1601 : }
1602 :
1603 :
1604 : /**
1605 : * Start the database transactions and begin the audit.
1606 : *
1607 : * @return transaction status code
1608 : */
1609 : static enum GNUNET_DB_QueryStatus
1610 68 : begin_transaction (void)
1611 : {
1612 : enum GNUNET_DB_QueryStatus qs;
1613 :
1614 68 : if (GNUNET_SYSERR ==
1615 68 : TALER_ARL_edb->preflight (TALER_ARL_edb->cls))
1616 : {
1617 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1618 : "Failed to initialize exchange database connection.\n");
1619 0 : return GNUNET_DB_STATUS_HARD_ERROR;
1620 : }
1621 68 : if (GNUNET_SYSERR ==
1622 68 : TALER_ARL_adb->preflight (TALER_ARL_adb->cls))
1623 : {
1624 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1625 : "Failed to initialize auditor database session.\n");
1626 0 : return GNUNET_DB_STATUS_HARD_ERROR;
1627 : }
1628 68 : global_qs = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
1629 68 : if (GNUNET_OK !=
1630 68 : TALER_ARL_adb->start (TALER_ARL_adb->cls))
1631 : {
1632 0 : GNUNET_break (0);
1633 0 : return GNUNET_DB_STATUS_HARD_ERROR;
1634 : }
1635 68 : if (GNUNET_OK !=
1636 68 : TALER_ARL_edb->start_read_only (TALER_ARL_edb->cls,
1637 : "wire debit auditor"))
1638 : {
1639 0 : GNUNET_break (0);
1640 0 : return GNUNET_DB_STATUS_HARD_ERROR;
1641 : }
1642 68 : qs = TALER_ARL_adb->get_balance (
1643 68 : TALER_ARL_adb->cls,
1644 : TALER_ARL_GET_AB (total_drained),
1645 : TALER_ARL_GET_AB (total_wire_out),
1646 : TALER_ARL_GET_AB (total_bad_amount_out_plus),
1647 : TALER_ARL_GET_AB (total_bad_amount_out_minus),
1648 : TALER_ARL_GET_AB (total_closure_amount_lag),
1649 : TALER_ARL_GET_AB (wire_debit_duplicate_transfer_subject_total),
1650 : TALER_ARL_GET_AB (total_wire_out),
1651 : NULL);
1652 68 : switch (qs)
1653 : {
1654 0 : case GNUNET_DB_STATUS_HARD_ERROR:
1655 0 : GNUNET_break (0);
1656 0 : return qs;
1657 0 : case GNUNET_DB_STATUS_SOFT_ERROR:
1658 0 : GNUNET_break (0);
1659 0 : return qs;
1660 0 : case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
1661 : case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
1662 0 : break;
1663 : }
1664 68 : for (struct WireAccount *wa = wa_head;
1665 136 : NULL != wa;
1666 68 : wa = wa->next)
1667 : {
1668 68 : GNUNET_asprintf (&wa->label_wire_out_serial_id,
1669 : "wire-%s-%s",
1670 68 : wa->ai->section_name,
1671 : "wire_out_serial_id");
1672 68 : GNUNET_asprintf (&wa->label_wire_off_out,
1673 : "wire-%s-%s",
1674 68 : wa->ai->section_name,
1675 : "wire_off_out");
1676 68 : qs = TALER_ARL_adb->get_auditor_progress (
1677 68 : TALER_ARL_adb->cls,
1678 68 : wa->label_wire_out_serial_id,
1679 : &wa->last_wire_out_serial_id,
1680 : wa->label_wire_off_out,
1681 : &wa->wire_off_out,
1682 : NULL);
1683 68 : if (0 > qs)
1684 : {
1685 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1686 0 : return qs;
1687 : }
1688 68 : GNUNET_assert (2 == qs);
1689 68 : wa->start_wire_out_serial_id = wa->last_wire_out_serial_id;
1690 68 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1691 : "Resuming account %s debit audit at %llu/%llu\n",
1692 : wa->ai->section_name,
1693 : (unsigned long long) wa->last_wire_out_serial_id,
1694 : (unsigned long long) wa->wire_off_out);
1695 : }
1696 68 : qs = TALER_ARL_adb->get_auditor_progress (
1697 68 : TALER_ARL_adb->cls,
1698 : TALER_ARL_GET_PP (wire_reserve_close_id),
1699 : NULL);
1700 68 : if (0 > qs)
1701 : {
1702 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1703 0 : return qs;
1704 : }
1705 68 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
1706 : {
1707 0 : GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
1708 : "First analysis of with wire auditor, starting audit from scratch\n");
1709 : }
1710 : else
1711 : {
1712 68 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1713 : "Resuming wire debit audit at %llu\n",
1714 : (unsigned long long) TALER_ARL_USE_PP (wire_reserve_close_id));
1715 : }
1716 68 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1717 : "Iterating over reserve closures from %llu\n",
1718 : (unsigned long long) TALER_ARL_USE_PP (wire_reserve_close_id));
1719 68 : qs = TALER_ARL_edb->select_reserve_closed_above_serial_id (
1720 68 : TALER_ARL_edb->cls,
1721 : TALER_ARL_USE_PP (wire_reserve_close_id),
1722 : &reserve_closed_cb,
1723 : NULL);
1724 68 : if (0 > qs)
1725 : {
1726 0 : GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
1727 0 : return GNUNET_DB_STATUS_HARD_ERROR;
1728 : }
1729 68 : process_debits (wa_head);
1730 68 : return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
1731 : }
1732 :
1733 :
1734 : /**
1735 : * Function called with information about a wire account. Adds the
1736 : * account to our list for processing (if it is enabled and we can
1737 : * load the plugin).
1738 : *
1739 : * @param cls closure, NULL
1740 : * @param ai account information
1741 : */
1742 : static void
1743 68 : process_account_cb (void *cls,
1744 : const struct TALER_EXCHANGEDB_AccountInfo *ai)
1745 : {
1746 : struct WireAccount *wa;
1747 :
1748 : (void) cls;
1749 68 : if ( (! ai->debit_enabled) &&
1750 0 : (! ai->credit_enabled) )
1751 0 : return; /* not an active exchange account */
1752 68 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1753 : "Found exchange account `%s'\n",
1754 : ai->section_name);
1755 68 : wa = GNUNET_new (struct WireAccount);
1756 68 : wa->ai = ai;
1757 68 : GNUNET_CONTAINER_DLL_insert (wa_head,
1758 : wa_tail,
1759 : wa);
1760 : }
1761 :
1762 :
1763 : /**
1764 : * Function called on events received from Postgres.
1765 : *
1766 : * @param cls closure, NULL
1767 : * @param extra additional event data provided
1768 : * @param extra_size number of bytes in @a extra
1769 : */
1770 : static void
1771 0 : db_notify (void *cls,
1772 : const void *extra,
1773 : size_t extra_size)
1774 : {
1775 : (void) cls;
1776 : (void) extra;
1777 : (void) extra_size;
1778 :
1779 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1780 : "Received notification to wake wire helper\n");
1781 : /* If there are accounts we are still processing, abort
1782 : the HTTP requests so we can start afresh. */
1783 0 : for (struct WireAccount *wa = wa_head;
1784 0 : NULL != wa;
1785 0 : wa = wa->next)
1786 : {
1787 0 : if (NULL != wa->dhh)
1788 : {
1789 0 : TALER_BANK_debit_history_cancel (wa->dhh);
1790 0 : wa->dhh = NULL;
1791 : }
1792 0 : check_exchange_wire_out (wa);
1793 : }
1794 :
1795 0 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
1796 0 : begin_transaction ())
1797 : {
1798 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1799 : "Audit failed\n");
1800 0 : GNUNET_break (0);
1801 0 : global_ret = EXIT_FAILURE;
1802 0 : GNUNET_SCHEDULER_shutdown ();
1803 0 : return;
1804 : }
1805 : }
1806 :
1807 :
1808 : /**
1809 : * Main function that will be run.
1810 : *
1811 : * @param cls closure
1812 : * @param args remaining command-line arguments
1813 : * @param cfgfile name of the configuration file used (for saving, can be NULL!)
1814 : * @param c configuration
1815 : */
1816 : static void
1817 68 : run (void *cls,
1818 : char *const *args,
1819 : const char *cfgfile,
1820 : const struct GNUNET_CONFIGURATION_Handle *c)
1821 : {
1822 : (void) cls;
1823 : (void) args;
1824 : (void) cfgfile;
1825 68 : cfg = c;
1826 68 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1827 : "Launching wire debit auditor\n");
1828 68 : if (GNUNET_OK !=
1829 68 : TALER_ARL_init (c))
1830 : {
1831 0 : global_ret = EXIT_FAILURE;
1832 0 : return;
1833 : }
1834 :
1835 : reserve_closures
1836 68 : = GNUNET_CONTAINER_multihashmap_create (1024,
1837 : GNUNET_NO);
1838 68 : GNUNET_assert (GNUNET_OK ==
1839 : TALER_amount_set_zero (TALER_ARL_currency,
1840 : &zero));
1841 68 : GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
1842 : NULL);
1843 68 : ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
1844 : &rctx);
1845 68 : rctx = GNUNET_CURL_gnunet_rc_create (ctx);
1846 68 : if (NULL == ctx)
1847 : {
1848 0 : GNUNET_break (0);
1849 0 : global_ret = EXIT_FAILURE;
1850 0 : return;
1851 : }
1852 68 : reserve_closures = GNUNET_CONTAINER_multihashmap_create (1024,
1853 : GNUNET_NO);
1854 68 : out_map = GNUNET_CONTAINER_multihashmap_create (1024,
1855 : true);
1856 68 : if (GNUNET_OK !=
1857 68 : TALER_EXCHANGEDB_load_accounts (TALER_ARL_cfg,
1858 : TALER_EXCHANGEDB_ALO_DEBIT
1859 : | TALER_EXCHANGEDB_ALO_CREDIT
1860 : | TALER_EXCHANGEDB_ALO_AUTHDATA))
1861 : {
1862 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1863 : "No bank accounts configured\n");
1864 0 : global_ret = EXIT_NOTCONFIGURED;
1865 0 : GNUNET_SCHEDULER_shutdown ();
1866 0 : return;
1867 : }
1868 68 : TALER_EXCHANGEDB_find_accounts (&process_account_cb,
1869 : NULL);
1870 :
1871 68 : if (0 == test_mode)
1872 : {
1873 0 : struct GNUNET_DB_EventHeaderP es = {
1874 0 : .size = htons (sizeof (es)),
1875 0 : .type = htons (TALER_DBEVENT_EXCHANGE_AUDITOR_WAKE_HELPER_WIRE)
1876 : };
1877 :
1878 0 : eh = TALER_ARL_adb->event_listen (TALER_ARL_adb->cls,
1879 : &es,
1880 0 : GNUNET_TIME_UNIT_FOREVER_REL,
1881 : &db_notify,
1882 : NULL);
1883 0 : GNUNET_assert (NULL != eh);
1884 : }
1885 68 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
1886 68 : begin_transaction ())
1887 : {
1888 0 : GNUNET_break (0);
1889 0 : global_ret = EXIT_FAILURE;
1890 0 : GNUNET_SCHEDULER_shutdown ();
1891 0 : return;
1892 : }
1893 : }
1894 :
1895 :
1896 : /**
1897 : * The main function of the wire auditing tool. Checks that
1898 : * the exchange's records of wire transfers match that of
1899 : * the wire gateway.
1900 : *
1901 : * @param argc number of arguments from the command line
1902 : * @param argv command line arguments
1903 : * @return 0 ok, 1 on error
1904 : */
1905 : int
1906 68 : main (int argc,
1907 : char *const *argv)
1908 : {
1909 68 : const struct GNUNET_GETOPT_CommandLineOption options[] = {
1910 68 : GNUNET_GETOPT_option_flag ('i',
1911 : "internal",
1912 : "perform checks only applicable for exchange-internal audits",
1913 : &internal_checks),
1914 68 : GNUNET_GETOPT_option_flag ('I',
1915 : "ignore-not-found",
1916 : "continue, even if the bank account of the exchange was not found",
1917 : &ignore_account_404),
1918 68 : GNUNET_GETOPT_option_flag ('t',
1919 : "test",
1920 : "run in test mode and exit when idle",
1921 : &test_mode),
1922 68 : GNUNET_GETOPT_option_timetravel ('T',
1923 : "timetravel"),
1924 : GNUNET_GETOPT_OPTION_END
1925 : };
1926 : enum GNUNET_GenericReturnValue ret;
1927 :
1928 68 : ret = GNUNET_PROGRAM_run (
1929 : TALER_AUDITOR_project_data (),
1930 : argc,
1931 : argv,
1932 : "taler-helper-auditor-wire-debit",
1933 : gettext_noop (
1934 : "Audit exchange database for consistency with the bank's outgoing wire transfers"),
1935 : options,
1936 : &run,
1937 : NULL);
1938 68 : if (GNUNET_SYSERR == ret)
1939 0 : return EXIT_INVALIDARGUMENT;
1940 68 : if (GNUNET_NO == ret)
1941 0 : return EXIT_SUCCESS;
1942 68 : return global_ret;
1943 : }
1944 :
1945 :
1946 : /* end of taler-helper-auditor-wire-debit.c */
|