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