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-credit.c
18 : * @brief audits that wire transfers match those from an exchange database.
19 : * @author Christian Grothoff
20 : *
21 : * This auditor verifies that 'reserves_in' actually matches
22 : * the incoming wire transfers from the bank.
23 : */
24 : #include "platform.h"
25 : #include <gnunet/gnunet_util_lib.h>
26 : #include <gnunet/gnunet_curl_lib.h>
27 : #include "auditordb_lib.h"
28 : #include "exchangedb_lib.h"
29 : #include "taler/taler_json_lib.h"
30 : #include "taler/taler_bank_service.h"
31 : #include "taler/taler_signatures.h"
32 : #include "report-lib.h"
33 : #include "taler/taler_dbevents.h"
34 : #include \
35 : "exchange-database/iterate_reserves_in_above_serial_id_by_account.h"
36 : #include "auditor-database/delete_reserve_in_inconsistency.h"
37 : #include "auditor-database/event_listen.h"
38 : #include "auditor-database/get_auditor_progress.h"
39 : #include "auditor-database/get_balance.h"
40 : #include "auditor-database/insert_auditor_progress.h"
41 : #include "auditor-database/insert_balance.h"
42 : #include "auditor-database/delete_kycauth_in_inconsistency.h"
43 : #include "auditor-database/get_kycauth_in_inconsistency.h"
44 : #include "auditor-database/insert_kycauth_in_inconsistency.h"
45 : #include "auditor-database/insert_misattribution_in_inconsistency.h"
46 : #include "auditor-database/insert_reserve_in_inconsistency.h"
47 : #include "auditor-database/insert_row_inconsistency.h"
48 : #include "auditor-database/insert_row_minor_inconsistencies.h"
49 : #include "auditor-database/preflight.h"
50 : #include "auditor-database/get_reserve_in_inconsistency.h"
51 : #include "auditor-database/start.h"
52 : #include "exchange-database/preflight.h"
53 : #include "exchange-database/rollback.h"
54 : #include \
55 : "exchange-database/iterate_kycauth_in_above_serial_id_by_account.h"
56 : #include \
57 : "exchange-database/iterate_reserves_in_above_serial_id_by_account.h"
58 : #include "exchange-database/start_read_only.h"
59 :
60 : /**
61 : * How much time do we allow the aggregator to lag behind? If
62 : * wire transfers should have been made more than #GRACE_PERIOD
63 : * before, we issue warnings.
64 : */
65 : #define GRACE_PERIOD GNUNET_TIME_UNIT_HOURS
66 :
67 : /**
68 : * Maximum number of wire transfers we process per
69 : * (database) transaction.
70 : */
71 : #define MAX_PER_TRANSACTION 1024
72 :
73 : /**
74 : * How much do we allow the bank and the exchange to disagree about
75 : * timestamps? Should be sufficiently large to avoid bogus reports from deltas
76 : * created by imperfect clock synchronization and network delay.
77 : */
78 : #define TIME_TOLERANCE GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, \
79 : 15)
80 :
81 :
82 : /**
83 : * Run in test mode. Exit when idle instead of
84 : * going to sleep and waiting for more work.
85 : */
86 : static int test_mode;
87 :
88 : /**
89 : * Information we keep for each supported account.
90 : */
91 : struct WireAccount
92 : {
93 : /**
94 : * Accounts are kept in a DLL.
95 : */
96 : struct WireAccount *next;
97 :
98 : /**
99 : * Plugins are kept in a DLL.
100 : */
101 : struct WireAccount *prev;
102 :
103 : /**
104 : * Account details.
105 : */
106 : const struct TALER_EXCHANGEDB_AccountInfo *ai;
107 :
108 : /**
109 : * Active wire request for the transaction history.
110 : */
111 : struct TALER_BANK_CreditHistoryHandle *chh;
112 :
113 : /**
114 : * Progress point for this account.
115 : */
116 : uint64_t last_reserve_in_serial_id;
117 :
118 : /**
119 : * Initial progress point for this account.
120 : */
121 : uint64_t start_reserve_in_serial_id;
122 :
123 : /**
124 : * Progress point for this account in the kycauths_in table.
125 : */
126 : uint64_t last_kycauth_in_serial_id;
127 :
128 : /**
129 : * Where we are in the inbound transaction history.
130 : */
131 : uint64_t wire_off_in;
132 :
133 : /**
134 : * Label under which we store our pp's reserve_in_serial_id.
135 : */
136 : char *label_reserve_in_serial_id;
137 :
138 : /**
139 : * Label under which we store our pp's kycauth_in_serial_id.
140 : */
141 : char *label_kycauth_in_serial_id;
142 :
143 : /**
144 : * Label under which we store our wire_off_in.
145 : */
146 : char *label_wire_off_in;
147 :
148 : };
149 :
150 :
151 : /**
152 : * Return value from main().
153 : */
154 : static int global_ret;
155 :
156 : /**
157 : * State of the current database transaction with
158 : * the auditor DB.
159 : */
160 : static enum GNUNET_DB_QueryStatus global_qs;
161 :
162 : /**
163 : * Map with information about incoming wire transfers.
164 : * Maps hashes of the wire offsets to `struct ReserveInInfo`s.
165 : */
166 : static struct GNUNET_CONTAINER_MultiHashMap *in_map;
167 :
168 : /**
169 : * Map with information about incoming KYCAUTH wire transfers.
170 : * Maps hashes of the wire offsets to `struct KycauthInInfo`s.
171 : */
172 : static struct GNUNET_CONTAINER_MultiHashMap *kycauth_map;
173 :
174 : /**
175 : * Head of list of wire accounts we still need to look at.
176 : */
177 : static struct WireAccount *wa_head;
178 :
179 : /**
180 : * Tail of list of wire accounts we still need to look at.
181 : */
182 : static struct WireAccount *wa_tail;
183 :
184 : /**
185 : * Amount that is considered "tiny"
186 : */
187 : static struct TALER_Amount tiny_amount;
188 :
189 : /**
190 : * Total amount that was transferred too much to the exchange.
191 : */
192 : static TALER_ARL_DEF_AB (total_bad_amount_in_plus);
193 :
194 : /**
195 : * Total amount that was transferred too little to the exchange.
196 : */
197 : static TALER_ARL_DEF_AB (total_bad_amount_in_minus);
198 :
199 : /**
200 : * Total amount where the exchange has the wrong sender account
201 : * for incoming funds and may thus wire funds to the wrong
202 : * destination when closing the reserve.
203 : */
204 : static TALER_ARL_DEF_AB (total_misattribution_in);
205 :
206 : /**
207 : * Total amount credited to exchange accounts, as claimed by the exchange.
208 : * Includes #TAC_total_kycauth_revenue, so that this is the credit side of the
209 : * balance the exchange's bank account is expected to have.
210 : */
211 : static TALER_ARL_DEF_AB (total_wire_in);
212 :
213 : /**
214 : * Total amount credited to exchange accounts via KYCAUTH, as reported by
215 : * the bank.
216 : */
217 : static TALER_ARL_DEF_AB (total_kycauth_in);
218 :
219 : /**
220 : * Total amount credited to exchange accounts via KYCAUTH, as booked by the
221 : * exchange in its `kycauths_in` table. KYCAUTH funds are not credited to
222 : * any reserve and are never paid back, so they are revenue of the exchange
223 : * operator, like wire fees and closing fees, and may be drained together
224 : * with those. Counted towards #TAC_total_wire_in for that reason.
225 : */
226 : static TALER_ARL_DEF_AB (total_kycauth_revenue);
227 :
228 : /**
229 : * Total wire credit fees charged to the exchange account.
230 : */
231 : static TALER_ARL_DEF_AB (total_wire_credit_fees);
232 :
233 : /**
234 : * Amount of zero in our currency.
235 : */
236 : static struct TALER_Amount zero;
237 :
238 : /**
239 : * Handle to the context for interacting with the bank.
240 : */
241 : static struct GNUNET_CURL_Context *ctx;
242 :
243 : /**
244 : * Scheduler context for running the @e ctx.
245 : */
246 : static struct GNUNET_CURL_RescheduleContext *rc;
247 :
248 : /**
249 : * Should we run checks that only work for exchange-internal audits?
250 : */
251 : static int internal_checks;
252 :
253 : /**
254 : * Should we ignore if the bank does not know our bank
255 : * account?
256 : */
257 : static int ignore_account_404;
258 :
259 : /**
260 : * Database event handler to wake us up again.
261 : */
262 : static struct GNUNET_DB_EventHandler *eh;
263 :
264 : /**
265 : * The auditors's configuration.
266 : */
267 : static const struct GNUNET_CONFIGURATION_Handle *cfg;
268 :
269 : /* ***************************** Shutdown **************************** */
270 :
271 : /**
272 : * Entry in map with wire information we expect to obtain from the
273 : * bank later.
274 : */
275 : struct ReserveInInfo
276 : {
277 :
278 : /**
279 : * Hash of expected row offset.
280 : */
281 : struct GNUNET_HashCode row_off_hash;
282 :
283 : /**
284 : * Expected details about the wire transfer.
285 : * The member "account_url" is to be allocated
286 : * at the end of this struct!
287 : */
288 : struct TALER_BANK_CreditDetails credit_details;
289 :
290 : /**
291 : * RowID in reserves_in table.
292 : */
293 : uint64_t rowid;
294 :
295 : };
296 :
297 :
298 : /**
299 : * Entry in #kycauth_map with the KYCAUTH transfer the exchange claims to
300 : * have received, waiting to be matched against the bank's credit history.
301 : */
302 : struct KycauthInInfo
303 : {
304 :
305 : /**
306 : * Hash of expected row offset.
307 : */
308 : struct GNUNET_HashCode row_off_hash;
309 :
310 : /**
311 : * RowID in the kycauths_in table.
312 : */
313 : uint64_t rowid;
314 :
315 : /**
316 : * Public key the transfer associated with the debited account.
317 : */
318 : union TALER_AccountPublicKeyP account_pub;
319 :
320 : /**
321 : * Amount the exchange says it received.
322 : */
323 : struct TALER_Amount credit;
324 :
325 : /**
326 : * When the exchange says it received the funds.
327 : */
328 : struct GNUNET_TIME_Timestamp execution_date;
329 :
330 : /**
331 : * payto://-URL of the debited account, allocated at the end of this
332 : * struct. NULL if the exchange has no `wire_targets` row for the
333 : * account, which it only creates the first time it sees one.
334 : */
335 : struct TALER_FullPayto sender_account_details;
336 :
337 : };
338 :
339 :
340 : /**
341 : * Free entry in #in_map.
342 : *
343 : * @param cls NULL
344 : * @param key unused key
345 : * @param value the `struct ReserveInInfo` to free
346 : * @return #GNUNET_OK
347 : */
348 : static enum GNUNET_GenericReturnValue
349 16 : free_rii (void *cls,
350 : const struct GNUNET_HashCode *key,
351 : void *value)
352 : {
353 16 : struct ReserveInInfo *rii = value;
354 :
355 : (void) cls;
356 16 : GNUNET_assert (GNUNET_YES ==
357 : GNUNET_CONTAINER_multihashmap_remove (in_map,
358 : key,
359 : rii));
360 16 : GNUNET_free (rii);
361 16 : return GNUNET_OK;
362 : }
363 :
364 :
365 : /**
366 : * Free entry in #kycauth_map.
367 : *
368 : * @param cls NULL
369 : * @param key unused key
370 : * @param value the `struct KycauthInInfo` to free
371 : * @return #GNUNET_OK
372 : */
373 : static enum GNUNET_GenericReturnValue
374 0 : free_kii (void *cls,
375 : const struct GNUNET_HashCode *key,
376 : void *value)
377 : {
378 0 : struct KycauthInInfo *kii = value;
379 :
380 : (void) cls;
381 0 : GNUNET_assert (GNUNET_YES ==
382 : GNUNET_CONTAINER_multihashmap_remove (kycauth_map,
383 : key,
384 : kii));
385 0 : GNUNET_free (kii);
386 0 : return GNUNET_OK;
387 : }
388 :
389 :
390 : /**
391 : * Task run on shutdown.
392 : *
393 : * @param cls NULL
394 : */
395 : static void
396 6 : do_shutdown (void *cls)
397 : {
398 : struct WireAccount *wa;
399 :
400 : (void) cls;
401 6 : if (NULL != eh)
402 : {
403 0 : TALER_AUDITORDB_event_listen_cancel (eh);
404 0 : eh = NULL;
405 : }
406 6 : TALER_ARL_done ();
407 6 : if (NULL != in_map)
408 : {
409 0 : GNUNET_CONTAINER_multihashmap_iterate (in_map,
410 : &free_rii,
411 : NULL);
412 0 : GNUNET_CONTAINER_multihashmap_destroy (in_map);
413 0 : in_map = NULL;
414 : }
415 6 : if (NULL != kycauth_map)
416 : {
417 0 : GNUNET_CONTAINER_multihashmap_iterate (kycauth_map,
418 : &free_kii,
419 : NULL);
420 0 : GNUNET_CONTAINER_multihashmap_destroy (kycauth_map);
421 0 : kycauth_map = NULL;
422 : }
423 12 : while (NULL != (wa = wa_head))
424 : {
425 6 : if (NULL != wa->chh)
426 : {
427 0 : TALER_BANK_credit_history_cancel (wa->chh);
428 0 : wa->chh = NULL;
429 : }
430 6 : GNUNET_CONTAINER_DLL_remove (wa_head,
431 : wa_tail,
432 : wa);
433 6 : GNUNET_free (wa->label_reserve_in_serial_id);
434 6 : GNUNET_free (wa->label_kycauth_in_serial_id);
435 6 : GNUNET_free (wa->label_wire_off_in);
436 6 : GNUNET_free (wa);
437 : }
438 6 : if (NULL != ctx)
439 : {
440 6 : GNUNET_CURL_fini (ctx);
441 6 : ctx = NULL;
442 : }
443 6 : if (NULL != rc)
444 : {
445 6 : GNUNET_CURL_gnunet_rc_destroy (rc);
446 6 : rc = NULL;
447 : }
448 6 : TALER_EXCHANGEDB_unload_accounts ();
449 6 : TALER_ARL_cfg = NULL;
450 6 : }
451 :
452 :
453 : /**
454 : * Start the database transactions and begin the audit.
455 : *
456 : * @return transaction status code
457 : */
458 : static enum GNUNET_DB_QueryStatus
459 : begin_transaction (void);
460 :
461 :
462 : /**
463 : * Rollback the current transaction, reset our state and try
464 : * again (we had a serialization error).
465 : */
466 : static void
467 0 : rollback_and_reset (void)
468 : {
469 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
470 : "Serialization issue, trying again\n");
471 0 : TALER_AUDITORDB_rollback (TALER_ARL_adb);
472 0 : for (unsigned int max_retries = 3; max_retries>0; max_retries--)
473 : {
474 : enum GNUNET_DB_QueryStatus qs;
475 :
476 0 : if (NULL != in_map)
477 : {
478 0 : GNUNET_CONTAINER_multihashmap_iterate (in_map,
479 : &free_rii,
480 : NULL);
481 0 : GNUNET_CONTAINER_multihashmap_destroy (in_map);
482 0 : in_map = NULL;
483 : }
484 0 : if (NULL != kycauth_map)
485 : {
486 0 : GNUNET_CONTAINER_multihashmap_iterate (kycauth_map,
487 : &free_kii,
488 : NULL);
489 0 : GNUNET_CONTAINER_multihashmap_destroy (kycauth_map);
490 0 : kycauth_map = NULL;
491 : }
492 0 : qs = begin_transaction ();
493 0 : if (GNUNET_DB_STATUS_HARD_ERROR == qs)
494 0 : break;
495 : }
496 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
497 : "Hard database error, terminating\n");
498 0 : GNUNET_SCHEDULER_shutdown ();
499 0 : }
500 :
501 :
502 : /**
503 : * Commit the transaction, checkpointing our progress in the auditor DB.
504 : *
505 : * @param qs transaction status so far
506 : */
507 : static void
508 6 : commit (enum GNUNET_DB_QueryStatus qs)
509 : {
510 6 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
511 : "Transaction logic ended with status %d\n",
512 : qs);
513 6 : TALER_EXCHANGEDB_rollback (TALER_ARL_edb);
514 6 : if (qs < 0)
515 0 : goto handle_db_error;
516 6 : qs = TALER_AUDITORDB_insert_balance (
517 : TALER_ARL_adb,
518 : TALER_ARL_SET_AB (total_wire_in),
519 : TALER_ARL_SET_AB (total_kycauth_in),
520 : TALER_ARL_SET_AB (total_kycauth_revenue),
521 : TALER_ARL_SET_AB (total_wire_credit_fees),
522 : TALER_ARL_SET_AB (total_bad_amount_in_plus),
523 : TALER_ARL_SET_AB (total_bad_amount_in_minus),
524 : TALER_ARL_SET_AB (total_misattribution_in),
525 : NULL);
526 6 : if (0 > qs)
527 0 : goto handle_db_error;
528 6 : for (struct WireAccount *wa = wa_head;
529 12 : NULL != wa;
530 6 : wa = wa->next)
531 : {
532 6 : qs = TALER_AUDITORDB_insert_auditor_progress (
533 : TALER_ARL_adb,
534 6 : wa->label_reserve_in_serial_id,
535 : wa->last_reserve_in_serial_id,
536 : wa->label_kycauth_in_serial_id,
537 : wa->last_kycauth_in_serial_id,
538 : wa->label_wire_off_in,
539 : wa->wire_off_in,
540 : NULL);
541 6 : if (0 > qs)
542 0 : goto handle_db_error;
543 6 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
544 : "Transaction ends at %s=%llu for account `%s'\n",
545 : wa->label_reserve_in_serial_id,
546 : (unsigned long long) wa->last_reserve_in_serial_id,
547 : wa->ai->section_name);
548 : }
549 6 : qs = TALER_AUDITORDB_commit (TALER_ARL_adb);
550 6 : if (0 > qs)
551 : {
552 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
553 0 : goto handle_db_error;
554 : }
555 6 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
556 : "Transaction concluded!\n");
557 6 : if (1 == test_mode)
558 6 : GNUNET_SCHEDULER_shutdown ();
559 6 : return;
560 0 : handle_db_error:
561 0 : rollback_and_reset ();
562 : }
563 :
564 :
565 : /**
566 : * Conclude the credit history check by logging entries that
567 : * were not found and freeing resources. Then move on to
568 : * processing debits.
569 : */
570 : static void
571 6 : conclude_credit_history (void)
572 : {
573 6 : if (NULL != in_map)
574 : {
575 6 : GNUNET_assert (0 ==
576 : GNUNET_CONTAINER_multihashmap_size (in_map));
577 6 : GNUNET_CONTAINER_multihashmap_destroy (in_map);
578 6 : in_map = NULL;
579 : }
580 6 : if (NULL != kycauth_map)
581 : {
582 6 : GNUNET_assert (0 ==
583 : GNUNET_CONTAINER_multihashmap_size (kycauth_map));
584 6 : GNUNET_CONTAINER_multihashmap_destroy (kycauth_map);
585 6 : kycauth_map = NULL;
586 : }
587 6 : commit (global_qs);
588 6 : }
589 :
590 :
591 : /**
592 : * Check if the given wire transfers are equivalent.
593 : *
594 : * @param credit amount that was received
595 : * @param credit2 2nd amount that was received
596 : * @param reserve_pub public key of the reserve (also the WTID)
597 : * @param reserve_pub2 2nd public key of the reserve (also the WTID)
598 : * @param sender_account_details payto://-URL of the sender's bank account
599 : * @param sender_account_details2 2nd payto://-URL of the sender's bank account
600 : * @param execution_date when did we receive the funds
601 : * @param execution_date2 2nd when did we receive the funds
602 : * @return #GNUNET_YES if so,
603 : * #GNUNET_NO if not
604 : * #GNUNET_SYSERR on internal error
605 : */
606 : static enum GNUNET_GenericReturnValue
607 0 : check_equality (const struct TALER_Amount *credit,
608 : const struct TALER_Amount *credit2,
609 : const struct TALER_ReservePublicKeyP *reserve_pub,
610 : const struct TALER_ReservePublicKeyP *reserve_pub2,
611 : const struct TALER_FullPayto sender_account_details,
612 : const struct TALER_FullPayto sender_account_details2,
613 : struct GNUNET_TIME_Timestamp execution_date,
614 : struct GNUNET_TIME_Timestamp execution_date2)
615 : {
616 0 : if (0 != TALER_amount_cmp (credit,
617 : credit2))
618 0 : return GNUNET_NO;
619 0 : if (0 != GNUNET_memcmp (reserve_pub,
620 : reserve_pub2))
621 0 : return GNUNET_NO;
622 : {
623 : struct TALER_NormalizedPayto np;
624 : struct TALER_NormalizedPayto np2;
625 : bool fail;
626 :
627 0 : np = TALER_payto_normalize (sender_account_details);
628 0 : np2 = TALER_payto_normalize (sender_account_details2);
629 0 : fail = (0 != TALER_normalized_payto_cmp (np,
630 : np2));
631 0 : GNUNET_free (np.normalized_payto);
632 0 : GNUNET_free (np2.normalized_payto);
633 0 : if (fail)
634 0 : return GNUNET_NO;
635 : }
636 0 : if (GNUNET_TIME_timestamp_cmp (execution_date,
637 : !=,
638 : execution_date2))
639 0 : return GNUNET_NO;
640 0 : return GNUNET_YES;
641 : }
642 :
643 :
644 : /**
645 : * Check if two payto://-URLs designate the same bank account.
646 : *
647 : * @param a first account, may be NULL
648 : * @param b second account, may be NULL
649 : * @return true if they match, or if either side is unknown
650 : */
651 : static bool
652 0 : same_account (const struct TALER_FullPayto a,
653 : const struct TALER_FullPayto b)
654 : {
655 : struct TALER_NormalizedPayto na;
656 : struct TALER_NormalizedPayto nb;
657 : bool ret;
658 :
659 : /* The exchange only creates the wire_targets row for an account the
660 : first time it sees one, so it legitimately may not know the debited
661 : account of a KYCAUTH transfer. Do not turn that into a finding. */
662 0 : if ( (NULL == a.full_payto) ||
663 0 : (NULL == b.full_payto) )
664 0 : return true;
665 0 : na = TALER_payto_normalize (a);
666 0 : nb = TALER_payto_normalize (b);
667 0 : ret = (0 == TALER_normalized_payto_cmp (na,
668 : nb));
669 0 : GNUNET_free (na.normalized_payto);
670 0 : GNUNET_free (nb.normalized_payto);
671 0 : return ret;
672 : }
673 :
674 :
675 : /**
676 : * Check if the two views of a KYCAUTH wire transfer agree in every
677 : * respect.
678 : *
679 : * @param credit amount of the first view
680 : * @param credit2 amount of the second view
681 : * @param account_pub account key of the first view
682 : * @param account_pub2 account key of the second view
683 : * @param sender_account_details debited account of the first view
684 : * @param sender_account_details2 debited account of the second view
685 : * @param execution_date execution time of the first view
686 : * @param execution_date2 execution time of the second view
687 : * @return true if the two agree
688 : */
689 : static bool
690 0 : check_kycauth_equality (
691 : const struct TALER_Amount *credit,
692 : const struct TALER_Amount *credit2,
693 : const union TALER_AccountPublicKeyP *account_pub,
694 : const union TALER_AccountPublicKeyP *account_pub2,
695 : const struct TALER_FullPayto sender_account_details,
696 : const struct TALER_FullPayto sender_account_details2,
697 : struct GNUNET_TIME_Timestamp execution_date,
698 : struct GNUNET_TIME_Timestamp execution_date2)
699 : {
700 0 : return (0 == TALER_amount_cmp (credit,
701 0 : credit2)) &&
702 0 : (0 == GNUNET_memcmp (account_pub,
703 0 : account_pub2)) &&
704 0 : same_account (sender_account_details,
705 0 : sender_account_details2) &&
706 0 : GNUNET_TIME_timestamp_cmp (execution_date,
707 : ==,
708 : execution_date2);
709 : }
710 :
711 :
712 : /**
713 : * Function called with details about incoming wire transfers that
714 : * established an account key for KYC authentication, as claimed by the
715 : * exchange DB.
716 : *
717 : * Such a transfer is not credited to a reserve and is never paid back, so
718 : * the money is simply revenue of the exchange operator. We book it as
719 : * such: it is part of the balance the exchange's bank account is expected
720 : * to have, and the exchange may drain it together with its fee income.
721 : *
722 : * The row is then remembered in #kycauth_map, so that the walk over the
723 : * bank's credit history that follows can confirm the transfer actually
724 : * happened; whatever is left in that map afterwards is money the exchange
725 : * claims to have received and the bank knows nothing about.
726 : *
727 : * @param cls a `struct WireAccount` we are processing
728 : * @param rowid unique serial ID for the entry in our DB
729 : * @param account_pub public key the transfer associated with the account
730 : * @param credit amount that was received
731 : * @param sender_account_details payto://-URL of the sender's bank account,
732 : * NULL if the exchange has no `wire_targets` row for it
733 : * @param wire_reference unique identifier for the wire transfer
734 : * @param execution_date when did we receive the funds
735 : * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
736 : */
737 : static enum GNUNET_GenericReturnValue
738 0 : kycauth_in_cb (void *cls,
739 : uint64_t rowid,
740 : const union TALER_AccountPublicKeyP *account_pub,
741 : const struct TALER_Amount *credit,
742 : const struct TALER_FullPayto sender_account_details,
743 : uint64_t wire_reference,
744 : struct GNUNET_TIME_Timestamp execution_date)
745 : {
746 0 : struct WireAccount *wa = cls;
747 : struct KycauthInInfo *kii;
748 : size_t slen;
749 :
750 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
751 : "Analyzing exchange KYCAUTH IN (%llu) at %s of %s\n",
752 : (unsigned long long) rowid,
753 : GNUNET_TIME_timestamp2s (execution_date),
754 : TALER_amount2s (credit));
755 : /* Advance the cursor up front: every path below has fully accounted for
756 : this row, and reading it a second time would count the revenue twice. */
757 0 : wa->last_kycauth_in_serial_id = rowid + 1;
758 0 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_kycauth_revenue),
759 : &TALER_ARL_USE_AB (total_kycauth_revenue),
760 : credit);
761 0 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_wire_in),
762 : &TALER_ARL_USE_AB (total_wire_in),
763 : credit);
764 : {
765 : enum GNUNET_DB_QueryStatus qs;
766 : struct TALER_AUDITORDB_KycauthInInconsistency dc;
767 :
768 0 : qs = TALER_AUDITORDB_get_kycauth_in_inconsistency (
769 : TALER_ARL_adb,
770 : wire_reference,
771 : &dc);
772 0 : switch (qs)
773 : {
774 0 : case GNUNET_DB_STATUS_HARD_ERROR:
775 : case GNUNET_DB_STATUS_SOFT_ERROR:
776 0 : global_qs = qs;
777 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
778 0 : return GNUNET_SYSERR;
779 0 : case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
780 0 : break;
781 0 : case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
782 : {
783 0 : bool healed = false;
784 :
785 0 : if (TALER_amount_is_zero (&dc.amount_exchange_expected))
786 : {
787 : /* An earlier run saw this transfer at the bank while the
788 : exchange had not booked it yet. If the two now agree, that
789 : finding was a transient and we retract it. */
790 0 : healed = check_kycauth_equality (&dc.amount_wired,
791 : credit,
792 : &dc.account_pub,
793 : account_pub,
794 : dc.account,
795 : sender_account_details,
796 : GNUNET_TIME_absolute_to_timestamp (
797 : dc.timestamp),
798 : execution_date);
799 : }
800 0 : GNUNET_free (dc.account.full_payto);
801 0 : GNUNET_free (dc.diagnostic);
802 0 : if (healed)
803 : {
804 0 : qs = TALER_AUDITORDB_delete_kycauth_in_inconsistency (
805 : TALER_ARL_adb,
806 : dc.serial_id);
807 0 : if (qs < 0)
808 : {
809 0 : global_qs = qs;
810 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
811 0 : return GNUNET_SYSERR;
812 : }
813 : /* We are retracting the finding, so the excess credit we booked
814 : when we made it has to go as well -- otherwise a transfer the
815 : exchange merely booked late inflates this balance forever. */
816 0 : TALER_ARL_amount_subtract (
817 : &TALER_ARL_USE_AB (total_bad_amount_in_plus),
818 : &TALER_ARL_USE_AB (total_bad_amount_in_plus),
819 : &dc.amount_wired);
820 : /* The bank side of this transfer was accounted for when the
821 : finding was made, so do not expect it again. */
822 0 : return GNUNET_OK;
823 : }
824 0 : break;
825 : }
826 : }
827 : }
828 0 : slen = (NULL == sender_account_details.full_payto)
829 : ? 0
830 0 : : strlen (sender_account_details.full_payto) + 1;
831 0 : kii = GNUNET_malloc (sizeof (struct KycauthInInfo) + slen);
832 0 : kii->rowid = rowid;
833 0 : kii->account_pub = *account_pub;
834 0 : kii->credit = *credit;
835 0 : kii->execution_date = execution_date;
836 0 : if (0 != slen)
837 : {
838 0 : kii->sender_account_details.full_payto = (char *) &kii[1];
839 0 : GNUNET_memcpy (&kii[1],
840 : sender_account_details.full_payto,
841 : slen);
842 : }
843 0 : GNUNET_CRYPTO_hash (&wire_reference,
844 : sizeof (uint64_t),
845 : &kii->row_off_hash);
846 0 : if (GNUNET_OK !=
847 0 : GNUNET_CONTAINER_multihashmap_put (
848 : kycauth_map,
849 0 : &kii->row_off_hash,
850 : kii,
851 : GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
852 : {
853 0 : struct TALER_AUDITORDB_RowInconsistency ri = {
854 : .problem_row_id = rowid,
855 : .row_table = (char *) "kycauths_in",
856 : .diagnostic = (char *) "duplicate wire offset"
857 : };
858 : enum GNUNET_DB_QueryStatus qs;
859 :
860 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
861 : "Duplicate wire offset\n");
862 0 : qs = TALER_AUDITORDB_insert_row_inconsistency (
863 : TALER_ARL_adb,
864 : &ri);
865 0 : GNUNET_free (kii);
866 0 : if (qs < 0)
867 : {
868 0 : global_qs = qs;
869 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
870 0 : return GNUNET_SYSERR;
871 : }
872 : }
873 0 : return GNUNET_OK;
874 : }
875 :
876 :
877 : /**
878 : * Function called with details about incoming wire transfers
879 : * as claimed by the exchange DB.
880 : *
881 : * @param cls a `struct WireAccount` we are processing
882 : * @param rowid unique serial ID for the entry in our DB
883 : * @param reserve_pub public key of the reserve (also the WTID)
884 : * @param credit amount that was received
885 : * @param sender_account_details payto://-URL of the sender's bank account
886 : * @param wire_reference unique identifier for the wire transfer
887 : * @param execution_date when did we receive the funds
888 : * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
889 : */
890 : static enum GNUNET_GenericReturnValue
891 16 : reserve_in_cb (void *cls,
892 : uint64_t rowid,
893 : const struct TALER_ReservePublicKeyP *reserve_pub,
894 : const struct TALER_Amount *credit,
895 : const struct TALER_FullPayto sender_account_details,
896 : uint64_t wire_reference,
897 : struct GNUNET_TIME_Timestamp execution_date)
898 : {
899 16 : struct WireAccount *wa = cls;
900 : struct ReserveInInfo *rii;
901 : size_t slen;
902 :
903 16 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
904 : "Analyzing exchange wire IN (%llu) at %s of %s with reserve_pub %s\n",
905 : (unsigned long long) rowid,
906 : GNUNET_TIME_timestamp2s (execution_date),
907 : TALER_amount2s (credit),
908 : TALER_B2S (reserve_pub));
909 : /* Advance the cursor up front: every path below has fully accounted for
910 : this row, and reading it a second time would count the credit twice. */
911 16 : wa->last_reserve_in_serial_id = rowid + 1;
912 16 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_wire_in),
913 : &TALER_ARL_USE_AB (total_wire_in),
914 : credit);
915 : {
916 : enum GNUNET_DB_QueryStatus qs;
917 : struct TALER_AUDITORDB_ReserveInInconsistency dc;
918 :
919 16 : qs = TALER_AUDITORDB_get_reserve_in_inconsistency (
920 : TALER_ARL_adb,
921 : wire_reference,
922 : &dc);
923 16 : switch (qs)
924 : {
925 0 : case GNUNET_DB_STATUS_HARD_ERROR:
926 : case GNUNET_DB_STATUS_SOFT_ERROR:
927 0 : global_qs = qs;
928 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
929 0 : return GNUNET_SYSERR;
930 16 : case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
931 16 : break;
932 0 : case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
933 : {
934 0 : enum GNUNET_GenericReturnValue ret = GNUNET_NO;
935 :
936 0 : if (TALER_amount_is_zero (&dc.amount_exchange_expected))
937 : {
938 : /* database entry indicates unmatched transaction */
939 0 : ret = check_equality (&dc.amount_wired,
940 : credit,
941 : &dc.reserve_pub,
942 : reserve_pub,
943 : dc.account,
944 : sender_account_details,
945 : GNUNET_TIME_absolute_to_timestamp (dc.timestamp)
946 : ,
947 : execution_date);
948 : }
949 0 : GNUNET_free (dc.account.full_payto);
950 0 : GNUNET_free (dc.diagnostic);
951 0 : if (GNUNET_SYSERR == ret)
952 0 : return GNUNET_SYSERR;
953 0 : if (GNUNET_YES == ret)
954 : {
955 0 : qs = TALER_AUDITORDB_delete_reserve_in_inconsistency (
956 : TALER_ARL_adb,
957 : dc.serial_id);
958 0 : if (qs < 0)
959 : {
960 0 : global_qs = qs;
961 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
962 0 : return GNUNET_SYSERR;
963 : }
964 : /* We are retracting the finding, so the excess credit we booked
965 : when we made it has to go as well -- otherwise a transfer the
966 : exchange merely booked late inflates this balance forever. */
967 0 : TALER_ARL_amount_subtract (
968 : &TALER_ARL_USE_AB (total_bad_amount_in_plus),
969 : &TALER_ARL_USE_AB (total_bad_amount_in_plus),
970 : &dc.amount_wired);
971 0 : return GNUNET_OK;
972 : }
973 0 : break;
974 : }
975 : }
976 : }
977 16 : slen = strlen (sender_account_details.full_payto) + 1;
978 16 : rii = GNUNET_malloc (sizeof (struct ReserveInInfo) + slen);
979 16 : rii->rowid = rowid;
980 16 : rii->credit_details.type = TALER_BANK_CT_RESERVE;
981 16 : rii->credit_details.amount = *credit;
982 16 : rii->credit_details.execution_date = execution_date;
983 16 : rii->credit_details.details.reserve.reserve_pub = *reserve_pub;
984 16 : rii->credit_details.debit_account_uri.full_payto = (char *) &rii[1];
985 16 : GNUNET_memcpy (&rii[1],
986 : sender_account_details.full_payto,
987 : slen);
988 16 : GNUNET_CRYPTO_hash (&wire_reference,
989 : sizeof (uint64_t),
990 : &rii->row_off_hash);
991 16 : if (GNUNET_OK !=
992 16 : GNUNET_CONTAINER_multihashmap_put (in_map,
993 16 : &rii->row_off_hash,
994 : rii,
995 : GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
996 : {
997 0 : struct TALER_AUDITORDB_RowInconsistency ri = {
998 : .problem_row_id = rowid,
999 : .row_table = (char *) "reserves_in",
1000 : .diagnostic = (char *) "duplicate wire offset"
1001 : };
1002 : enum GNUNET_DB_QueryStatus qs;
1003 :
1004 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1005 : "Duplicate wire offset\n");
1006 0 : qs = TALER_AUDITORDB_insert_row_inconsistency (
1007 : TALER_ARL_adb,
1008 : &ri);
1009 0 : GNUNET_free (rii);
1010 0 : if (qs < 0)
1011 : {
1012 0 : global_qs = qs;
1013 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1014 0 : return GNUNET_SYSERR;
1015 : }
1016 0 : return GNUNET_OK;
1017 : }
1018 16 : return GNUNET_OK;
1019 : }
1020 :
1021 :
1022 : /**
1023 : * Complain that we failed to match an entry from #in_map.
1024 : *
1025 : * @param cls a `struct WireAccount`
1026 : * @param key unused key
1027 : * @param value the `struct ReserveInInfo` to free
1028 : * @return #GNUNET_OK
1029 : */
1030 : static enum GNUNET_GenericReturnValue
1031 0 : complain_in_not_found (void *cls,
1032 : const struct GNUNET_HashCode *key,
1033 : void *value)
1034 : {
1035 0 : struct WireAccount *wa = cls;
1036 0 : struct ReserveInInfo *rii = value;
1037 : enum GNUNET_DB_QueryStatus qs;
1038 0 : struct TALER_AUDITORDB_ReserveInInconsistency riiDb = {
1039 0 : .bank_row_id = rii->rowid,
1040 : .diagnostic = (char *)
1041 : "incoming wire transfer claimed by exchange not found",
1042 0 : .account = wa->ai->payto_uri,
1043 : .amount_exchange_expected = rii->credit_details.amount,
1044 : .amount_wired = zero,
1045 : .reserve_pub = rii->credit_details.details.reserve.reserve_pub,
1046 : .timestamp = rii->credit_details.execution_date.abs_time
1047 : };
1048 :
1049 : (void) key;
1050 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1051 : "Incoming wire transfer #%llu claimed by exchange not found\n",
1052 : (unsigned long long) rii->rowid);
1053 0 : GNUNET_assert (TALER_BANK_CT_RESERVE ==
1054 : rii->credit_details.type);
1055 0 : qs = TALER_AUDITORDB_insert_reserve_in_inconsistency (
1056 : TALER_ARL_adb,
1057 : &riiDb);
1058 0 : if (qs < 0)
1059 : {
1060 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1061 0 : global_qs = qs;
1062 0 : return GNUNET_SYSERR;
1063 : }
1064 0 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_in_minus),
1065 : &TALER_ARL_USE_AB (total_bad_amount_in_minus),
1066 : &rii->credit_details.amount);
1067 0 : return GNUNET_OK;
1068 : }
1069 :
1070 :
1071 : /**
1072 : * Complain that we failed to match an entry from #kycauth_map: the
1073 : * exchange booked a KYCAUTH transfer the bank never made. Beyond the
1074 : * missing money, this means the exchange let an account authenticate
1075 : * without anyone ever having proven that they control it.
1076 : *
1077 : * @param cls a `struct WireAccount`
1078 : * @param key unused key
1079 : * @param value the `struct KycauthInInfo` we could not match
1080 : * @return #GNUNET_OK
1081 : */
1082 : static enum GNUNET_GenericReturnValue
1083 0 : complain_kycauth_in_not_found (void *cls,
1084 : const struct GNUNET_HashCode *key,
1085 : void *value)
1086 : {
1087 0 : struct WireAccount *wa = cls;
1088 0 : struct KycauthInInfo *kii = value;
1089 : enum GNUNET_DB_QueryStatus qs;
1090 0 : struct TALER_AUDITORDB_KycauthInInconsistency kiiDb = {
1091 0 : .bank_row_id = kii->rowid,
1092 : .diagnostic = (char *)
1093 : "KYCAUTH wire transfer claimed by exchange not found",
1094 0 : .account = (NULL == kii->sender_account_details.full_payto)
1095 0 : ? wa->ai->payto_uri
1096 0 : : kii->sender_account_details,
1097 : .amount_exchange_expected = kii->credit,
1098 : .amount_wired = zero,
1099 : .account_pub = kii->account_pub,
1100 : .timestamp = kii->execution_date.abs_time
1101 : };
1102 :
1103 : (void) key;
1104 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1105 : "KYCAUTH wire transfer #%llu claimed by exchange not found\n",
1106 : (unsigned long long) kii->rowid);
1107 0 : qs = TALER_AUDITORDB_insert_kycauth_in_inconsistency (
1108 : TALER_ARL_adb,
1109 : &kiiDb);
1110 0 : if (qs < 0)
1111 : {
1112 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1113 0 : global_qs = qs;
1114 0 : return GNUNET_SYSERR;
1115 : }
1116 0 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_in_minus),
1117 : &TALER_ARL_USE_AB (total_bad_amount_in_minus),
1118 : &kii->credit);
1119 0 : return GNUNET_OK;
1120 : }
1121 :
1122 :
1123 : /**
1124 : * Start processing the next wire account.
1125 : * Shuts down if we are done.
1126 : *
1127 : * @param cls `struct WireAccount` with a wire account list to process
1128 : */
1129 : static void
1130 : process_credits (void *cls);
1131 :
1132 :
1133 : /**
1134 : * We got all of the incoming transactions for @a wa,
1135 : * finish processing the account.
1136 : *
1137 : * @param[in,out] wa wire account to process
1138 : */
1139 : static void
1140 6 : conclude_account (struct WireAccount *wa)
1141 : {
1142 6 : GNUNET_assert (NULL == wa->chh);
1143 6 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1144 : "Reconciling CREDIT processing of account `%s'\n",
1145 : wa->ai->section_name);
1146 6 : if (NULL != in_map)
1147 : {
1148 6 : GNUNET_CONTAINER_multihashmap_iterate (in_map,
1149 : &complain_in_not_found,
1150 : wa);
1151 : /* clean up before 2nd phase */
1152 6 : GNUNET_CONTAINER_multihashmap_iterate (in_map,
1153 : &free_rii,
1154 : NULL);
1155 6 : if (global_qs < 0)
1156 : {
1157 0 : commit (global_qs);
1158 0 : return;
1159 : }
1160 : }
1161 6 : if (NULL != kycauth_map)
1162 : {
1163 6 : GNUNET_CONTAINER_multihashmap_iterate (kycauth_map,
1164 : &complain_kycauth_in_not_found,
1165 : wa);
1166 : /* clean up before 2nd phase */
1167 6 : GNUNET_CONTAINER_multihashmap_iterate (kycauth_map,
1168 : &free_kii,
1169 : NULL);
1170 6 : if (global_qs < 0)
1171 : {
1172 0 : commit (global_qs);
1173 0 : return;
1174 : }
1175 : }
1176 6 : process_credits (wa->next);
1177 : }
1178 :
1179 :
1180 : /**
1181 : * Analyze a KYCAUTH credit the bank reports against what the exchange
1182 : * booked in its `kycauths_in` table (collected in #kycauth_map).
1183 : *
1184 : * @param[in,out] wa account that received the transfer
1185 : * @param credit_details transfer details as reported by the bank
1186 : * @return true on success, false to stop loop at this point
1187 : */
1188 : static bool
1189 0 : analyze_kycauth_credit (
1190 : struct WireAccount *wa,
1191 : const struct TALER_BANK_CreditDetails *credit_details)
1192 : {
1193 : struct KycauthInInfo *kii;
1194 : struct GNUNET_HashCode key;
1195 :
1196 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1197 : "Analyzing bank KYCAUTH CREDIT #%llu at %s of %s\n",
1198 : (unsigned long long) credit_details->serial_id,
1199 : GNUNET_TIME_timestamp2s (credit_details->execution_date),
1200 : TALER_amount2s (&credit_details->amount));
1201 0 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_kycauth_in),
1202 : &TALER_ARL_USE_AB (total_kycauth_in),
1203 : &credit_details->amount);
1204 : /* the bank charges its credit fee on this transfer just like on
1205 : any other, so it must be part of the total either way */
1206 0 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_wire_credit_fees),
1207 : &TALER_ARL_USE_AB (total_wire_credit_fees),
1208 : &credit_details->credit_fee);
1209 0 : GNUNET_CRYPTO_hash (&credit_details->serial_id,
1210 : sizeof (credit_details->serial_id),
1211 : &key);
1212 0 : kii = GNUNET_CONTAINER_multihashmap_get (kycauth_map,
1213 : &key);
1214 0 : if (NULL == kii)
1215 : {
1216 0 : struct TALER_AUDITORDB_KycauthInInconsistency dc = {
1217 0 : .bank_row_id = credit_details->serial_id,
1218 : .amount_exchange_expected = zero,
1219 : .amount_wired = credit_details->amount,
1220 : .account_pub = credit_details->details.kycauth.account_pub,
1221 : .timestamp = credit_details->execution_date.abs_time,
1222 : .account = credit_details->debit_account_uri,
1223 : .diagnostic = (char *) "unknown to exchange"
1224 : };
1225 : enum GNUNET_DB_QueryStatus qs;
1226 :
1227 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1228 : "Failed to find KYCAUTH transfer at `%s' in exchange database.\n",
1229 : GNUNET_TIME_timestamp2s (credit_details->execution_date));
1230 0 : qs = TALER_AUDITORDB_insert_kycauth_in_inconsistency (TALER_ARL_adb,
1231 : &dc);
1232 0 : if (qs <= 0)
1233 : {
1234 0 : global_qs = qs;
1235 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1236 0 : return false;
1237 : }
1238 0 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_in_plus),
1239 : &TALER_ARL_USE_AB (total_bad_amount_in_plus),
1240 : &credit_details->amount);
1241 : /* We have fully processed this credit: the finding is on file, and
1242 : kycauth_in_cb() retracts it if the exchange books the transfer
1243 : later. Looking at it again would only duplicate the finding. */
1244 0 : wa->wire_off_in = credit_details->serial_id;
1245 0 : return true;
1246 : }
1247 :
1248 : /* Update offset */
1249 0 : wa->wire_off_in = credit_details->serial_id;
1250 0 : if (0 != TALER_amount_cmp (&kii->credit,
1251 : &credit_details->amount))
1252 : {
1253 0 : struct TALER_AUDITORDB_KycauthInInconsistency dc = {
1254 0 : .bank_row_id = credit_details->serial_id,
1255 : .amount_exchange_expected = kii->credit,
1256 : .amount_wired = credit_details->amount,
1257 : .account_pub = kii->account_pub,
1258 : .timestamp = kii->execution_date.abs_time,
1259 0 : .account = wa->ai->payto_uri,
1260 : .diagnostic = (char *) "wire amount does not match"
1261 : };
1262 : enum GNUNET_DB_QueryStatus qs;
1263 :
1264 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1265 : "KYCAUTH transfer amount differs\n");
1266 0 : qs = TALER_AUDITORDB_insert_kycauth_in_inconsistency (
1267 : TALER_ARL_adb,
1268 : &dc);
1269 0 : if (qs <= 0)
1270 : {
1271 0 : global_qs = qs;
1272 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1273 0 : return false;
1274 : }
1275 0 : if (0 < TALER_amount_cmp (&credit_details->amount,
1276 0 : &kii->credit))
1277 : {
1278 : struct TALER_Amount delta;
1279 :
1280 0 : TALER_ARL_amount_subtract (&delta,
1281 : &credit_details->amount,
1282 : &kii->credit);
1283 0 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_in_plus),
1284 : &TALER_ARL_USE_AB (total_bad_amount_in_plus),
1285 : &delta);
1286 : }
1287 : else
1288 : {
1289 : struct TALER_Amount delta;
1290 :
1291 0 : TALER_ARL_amount_subtract (&delta,
1292 : &kii->credit,
1293 : &credit_details->amount);
1294 0 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_in_minus),
1295 : &TALER_ARL_USE_AB (total_bad_amount_in_minus),
1296 : &delta);
1297 : }
1298 : }
1299 0 : if (0 != GNUNET_memcmp (&kii->account_pub,
1300 : &credit_details->details.kycauth.account_pub))
1301 : {
1302 : /* No money is missing, but the exchange associated a different key
1303 : with the account than the one the sender asked for: the wrong party
1304 : can now authenticate as the owner of that bank account. */
1305 0 : struct TALER_AUDITORDB_KycauthInInconsistency dc = {
1306 0 : .bank_row_id = credit_details->serial_id,
1307 : .amount_exchange_expected = kii->credit,
1308 : .amount_wired = credit_details->amount,
1309 : .account_pub = kii->account_pub,
1310 : .timestamp = kii->execution_date.abs_time,
1311 0 : .account = wa->ai->payto_uri,
1312 : .diagnostic = (char *) "account public key does not match"
1313 : };
1314 : enum GNUNET_DB_QueryStatus qs;
1315 :
1316 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1317 : "KYCAUTH account public key differs\n");
1318 0 : qs = TALER_AUDITORDB_insert_kycauth_in_inconsistency (
1319 : TALER_ARL_adb,
1320 : &dc);
1321 0 : if (qs <= 0)
1322 : {
1323 0 : global_qs = qs;
1324 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1325 0 : return false;
1326 : }
1327 : }
1328 0 : if (! same_account (kii->sender_account_details,
1329 : credit_details->debit_account_uri))
1330 : {
1331 : /* Again no money is missing: the exchange authorised the wrong bank
1332 : account. There is no reserve to close towards it either, so this
1333 : does not feed into #total_misattribution_in. */
1334 0 : struct TALER_AUDITORDB_KycauthInInconsistency dc = {
1335 0 : .bank_row_id = credit_details->serial_id,
1336 : .amount_exchange_expected = kii->credit,
1337 : .amount_wired = credit_details->amount,
1338 : .account_pub = kii->account_pub,
1339 : .timestamp = kii->execution_date.abs_time,
1340 : .account = credit_details->debit_account_uri,
1341 : .diagnostic = (char *) "debited account does not match"
1342 : };
1343 : enum GNUNET_DB_QueryStatus qs;
1344 :
1345 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1346 : "Origin bank account of KYCAUTH transfer differs\n");
1347 0 : qs = TALER_AUDITORDB_insert_kycauth_in_inconsistency (
1348 : TALER_ARL_adb,
1349 : &dc);
1350 0 : if (qs <= 0)
1351 : {
1352 0 : global_qs = qs;
1353 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1354 0 : return false;
1355 : }
1356 : }
1357 0 : if (GNUNET_TIME_timestamp_cmp (credit_details->execution_date,
1358 : !=,
1359 : kii->execution_date))
1360 : {
1361 0 : struct TALER_AUDITORDB_RowMinorInconsistencies rmi = {
1362 0 : .problem_row = kii->rowid,
1363 : .diagnostic = (char *) "execution date mismatch",
1364 : .row_table = (char *) "kycauths_in"
1365 : };
1366 : enum GNUNET_DB_QueryStatus qs;
1367 :
1368 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1369 : "Execution date of KYCAUTH transfer differs\n");
1370 0 : qs = TALER_AUDITORDB_insert_row_minor_inconsistencies (
1371 : TALER_ARL_adb,
1372 : &rmi);
1373 0 : if (qs < 0)
1374 : {
1375 0 : global_qs = qs;
1376 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1377 0 : return false;
1378 : }
1379 : }
1380 0 : GNUNET_assert (GNUNET_OK ==
1381 : free_kii (NULL,
1382 : &key,
1383 : kii));
1384 0 : return true;
1385 : }
1386 :
1387 :
1388 : /**
1389 : * Analyze credit transaction @a details into @a wa.
1390 : *
1391 : * @param[in,out] wa account that received the transfer
1392 : * @param credit_details transfer details
1393 : * @return true on success, false to stop loop at this point
1394 : */
1395 : static bool
1396 16 : analyze_credit (
1397 : struct WireAccount *wa,
1398 : const struct TALER_BANK_CreditDetails *credit_details)
1399 : {
1400 : struct ReserveInInfo *rii;
1401 : struct GNUNET_HashCode key;
1402 :
1403 16 : switch (credit_details->type)
1404 : {
1405 16 : case TALER_BANK_CT_RESERVE:
1406 16 : break;
1407 0 : case TALER_BANK_CT_KYCAUTH:
1408 0 : return analyze_kycauth_credit (wa,
1409 : credit_details);
1410 0 : case TALER_BANK_CT_WAD:
1411 0 : GNUNET_break (0); /* FIXME: Wad not yet supported */
1412 0 : return false;
1413 : }
1414 16 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1415 : "Analyzing bank CREDIT #%llu at %s of %s with Reserve-pub %s\n",
1416 : (unsigned long long) credit_details->serial_id,
1417 : GNUNET_TIME_timestamp2s (credit_details->execution_date),
1418 : TALER_amount2s (&credit_details->amount),
1419 : TALER_B2S (&credit_details->details.reserve.reserve_pub));
1420 16 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_wire_credit_fees),
1421 : &TALER_ARL_USE_AB (total_wire_credit_fees),
1422 : &credit_details->credit_fee);
1423 16 : GNUNET_CRYPTO_hash (&credit_details->serial_id,
1424 : sizeof (credit_details->serial_id),
1425 : &key);
1426 16 : rii = GNUNET_CONTAINER_multihashmap_get (in_map,
1427 : &key);
1428 16 : if (NULL == rii)
1429 : {
1430 0 : struct TALER_AUDITORDB_ReserveInInconsistency dc = {
1431 0 : .bank_row_id = credit_details->serial_id,
1432 : .amount_exchange_expected = zero,
1433 : .amount_wired = credit_details->amount,
1434 : .reserve_pub = credit_details->details.reserve.reserve_pub,
1435 : .timestamp = credit_details->execution_date.abs_time,
1436 : .account = credit_details->debit_account_uri,
1437 : .diagnostic = (char *) "unknown to exchange"
1438 : };
1439 : enum GNUNET_DB_QueryStatus qs;
1440 :
1441 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1442 : "Failed to find wire transfer at `%s' in exchange database.\n",
1443 : GNUNET_TIME_timestamp2s (credit_details->execution_date));
1444 0 : qs = TALER_AUDITORDB_insert_reserve_in_inconsistency (TALER_ARL_adb,
1445 : &dc);
1446 0 : if (qs <= 0)
1447 : {
1448 0 : global_qs = qs;
1449 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1450 0 : return false;
1451 : }
1452 0 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_in_plus),
1453 : &TALER_ARL_USE_AB (total_bad_amount_in_plus),
1454 : &credit_details->amount);
1455 : /* We have fully processed this credit: the finding is on file, and
1456 : reserve_in_cb() retracts it if the exchange books the transfer
1457 : later. Looking at it again would only duplicate the finding. */
1458 0 : wa->wire_off_in = credit_details->serial_id;
1459 0 : return true;
1460 : }
1461 :
1462 : /* Update offset */
1463 16 : wa->wire_off_in = credit_details->serial_id;
1464 : /* compare records with expected data */
1465 16 : if (0 != GNUNET_memcmp (&credit_details->details.reserve.reserve_pub,
1466 : &rii->credit_details.details.reserve.reserve_pub))
1467 : {
1468 0 : struct TALER_AUDITORDB_ReserveInInconsistency riiDb = {
1469 0 : .bank_row_id = credit_details->serial_id,
1470 : .amount_exchange_expected = rii->credit_details.amount,
1471 : .amount_wired = zero,
1472 : .reserve_pub = rii->credit_details.details.reserve.reserve_pub,
1473 : .timestamp = rii->credit_details.execution_date.abs_time,
1474 0 : .account = wa->ai->payto_uri,
1475 : .diagnostic = (char *) "wire subject does not match"
1476 : };
1477 : enum GNUNET_DB_QueryStatus qs;
1478 :
1479 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1480 : "Reserve public key differs\n");
1481 0 : qs = TALER_AUDITORDB_insert_reserve_in_inconsistency (
1482 : TALER_ARL_adb,
1483 : &riiDb);
1484 0 : if (qs <= 0)
1485 : {
1486 0 : global_qs = qs;
1487 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1488 0 : return false;
1489 : }
1490 0 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_in_minus),
1491 : &TALER_ARL_USE_AB (total_bad_amount_in_minus),
1492 : &rii->credit_details.amount);
1493 0 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_in_plus),
1494 : &TALER_ARL_USE_AB (total_bad_amount_in_plus),
1495 : &credit_details->amount);
1496 0 : GNUNET_assert (GNUNET_OK ==
1497 : free_rii (NULL,
1498 : &key,
1499 : rii));
1500 0 : return true;
1501 : }
1502 16 : if (0 != TALER_amount_cmp (&rii->credit_details.amount,
1503 : &credit_details->amount))
1504 : {
1505 0 : struct TALER_AUDITORDB_ReserveInInconsistency riiDb = {
1506 : .diagnostic = (char *) "wire amount does not match",
1507 0 : .account = wa->ai->payto_uri,
1508 0 : .bank_row_id = credit_details->serial_id,
1509 : .amount_exchange_expected = rii->credit_details.amount,
1510 : .amount_wired = credit_details->amount,
1511 : .reserve_pub = rii->credit_details.details.reserve.reserve_pub,
1512 : .timestamp = rii->credit_details.execution_date.abs_time
1513 : };
1514 : enum GNUNET_DB_QueryStatus qs;
1515 :
1516 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1517 : "Wire transfer amount differs\n");
1518 0 : qs = TALER_AUDITORDB_insert_reserve_in_inconsistency (
1519 : TALER_ARL_adb,
1520 : &riiDb);
1521 0 : GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != qs);
1522 0 : if (qs < 0)
1523 : {
1524 0 : global_qs = qs;
1525 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1526 0 : return false;
1527 : }
1528 0 : if (0 < TALER_amount_cmp (&credit_details->amount,
1529 0 : &rii->credit_details.amount))
1530 : {
1531 : /* details->amount > rii->details.amount: wire transfer was larger than it should have been */
1532 : struct TALER_Amount delta;
1533 :
1534 0 : TALER_ARL_amount_subtract (&delta,
1535 : &credit_details->amount,
1536 : &rii->credit_details.amount);
1537 0 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_in_plus),
1538 : &TALER_ARL_USE_AB (total_bad_amount_in_plus),
1539 : &delta);
1540 : }
1541 : else
1542 : {
1543 : /* rii->details.amount < details->amount: wire transfer was smaller than it should have been */
1544 : struct TALER_Amount delta;
1545 :
1546 0 : TALER_ARL_amount_subtract (&delta,
1547 : &rii->credit_details.amount,
1548 : &credit_details->amount);
1549 0 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_in_minus),
1550 : &TALER_ARL_USE_AB (total_bad_amount_in_minus),
1551 : &delta);
1552 : }
1553 : }
1554 :
1555 : {
1556 : struct TALER_NormalizedPayto np;
1557 : struct TALER_NormalizedPayto np2;
1558 :
1559 16 : np = TALER_payto_normalize (credit_details->debit_account_uri);
1560 16 : np2 = TALER_payto_normalize (rii->credit_details.debit_account_uri);
1561 16 : if (0 != TALER_normalized_payto_cmp (np,
1562 : np2))
1563 : {
1564 0 : struct TALER_AUDITORDB_MisattributionInInconsistency mii = {
1565 : .reserve_pub = rii->credit_details.details.reserve.reserve_pub,
1566 : .amount = rii->credit_details.amount,
1567 0 : .bank_row = credit_details->serial_id
1568 : };
1569 : enum GNUNET_DB_QueryStatus qs;
1570 :
1571 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1572 : "Origin bank account differs\n");
1573 0 : qs = TALER_AUDITORDB_insert_misattribution_in_inconsistency (
1574 : TALER_ARL_adb,
1575 : &mii);
1576 0 : GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != qs);
1577 0 : if (qs < 0)
1578 : {
1579 0 : global_qs = qs;
1580 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1581 0 : GNUNET_free (np.normalized_payto);
1582 0 : GNUNET_free (np2.normalized_payto);
1583 0 : return false;
1584 : }
1585 0 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_misattribution_in),
1586 : &TALER_ARL_USE_AB (total_misattribution_in),
1587 : &rii->credit_details.amount);
1588 : }
1589 16 : GNUNET_free (np.normalized_payto);
1590 16 : GNUNET_free (np2.normalized_payto);
1591 : }
1592 16 : if (GNUNET_TIME_timestamp_cmp (credit_details->execution_date,
1593 : !=,
1594 : rii->credit_details.execution_date))
1595 : {
1596 0 : struct TALER_AUDITORDB_RowMinorInconsistencies rmi = {
1597 0 : .problem_row = rii->rowid,
1598 : .diagnostic = (char *) "execution date mismatch",
1599 : .row_table = (char *) "reserves_in"
1600 : };
1601 : enum GNUNET_DB_QueryStatus qs;
1602 :
1603 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1604 : "Execution date differs\n");
1605 0 : qs = TALER_AUDITORDB_insert_row_minor_inconsistencies (
1606 : TALER_ARL_adb,
1607 : &rmi);
1608 :
1609 0 : if (qs < 0)
1610 : {
1611 0 : global_qs = qs;
1612 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1613 0 : return false;
1614 : }
1615 : }
1616 16 : GNUNET_assert (GNUNET_OK ==
1617 : free_rii (NULL,
1618 : &key,
1619 : rii));
1620 16 : return true;
1621 : }
1622 :
1623 :
1624 : /**
1625 : * This function is called for all transactions that
1626 : * are credited to the exchange's account (incoming
1627 : * transactions).
1628 : *
1629 : * @param cls `struct WireAccount` we are processing
1630 : * @param chr HTTP response returned by the bank
1631 : */
1632 : static void
1633 6 : history_credit_cb (void *cls,
1634 : const struct TALER_BANK_CreditHistoryResponse *chr)
1635 : {
1636 6 : struct WireAccount *wa = cls;
1637 :
1638 6 : wa->chh = NULL;
1639 6 : switch (chr->http_status)
1640 : {
1641 4 : case MHD_HTTP_OK:
1642 20 : for (unsigned int i = 0; i < chr->details.ok.details_length; i++)
1643 : {
1644 16 : const struct TALER_BANK_CreditDetails *cd
1645 16 : = &chr->details.ok.details[i];
1646 :
1647 16 : if (! analyze_credit (wa,
1648 : cd))
1649 : {
1650 0 : switch (global_qs)
1651 : {
1652 0 : case GNUNET_DB_STATUS_SOFT_ERROR:
1653 0 : rollback_and_reset ();
1654 0 : return;
1655 0 : case GNUNET_DB_STATUS_HARD_ERROR:
1656 0 : GNUNET_SCHEDULER_shutdown ();
1657 0 : return;
1658 0 : case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
1659 : /* perfectly fine */
1660 0 : break;
1661 0 : case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
1662 : /* perfectly fine */
1663 0 : break;
1664 : }
1665 0 : break;
1666 : }
1667 : }
1668 4 : conclude_account (wa);
1669 4 : return;
1670 0 : case MHD_HTTP_NO_CONTENT:
1671 0 : conclude_account (wa);
1672 0 : return;
1673 2 : case MHD_HTTP_NOT_FOUND:
1674 2 : if (ignore_account_404)
1675 : {
1676 2 : conclude_account (wa);
1677 2 : return;
1678 : }
1679 0 : break;
1680 0 : default:
1681 0 : break;
1682 : }
1683 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1684 : "Error fetching credit history of account %s: %u (%s)\n",
1685 : wa->ai->section_name,
1686 : chr->http_status,
1687 : TALER_ErrorCode_get_hint (chr->ec));
1688 0 : commit (GNUNET_DB_STATUS_HARD_ERROR);
1689 0 : global_ret = EXIT_FAILURE;
1690 0 : GNUNET_SCHEDULER_shutdown ();
1691 : }
1692 :
1693 :
1694 : /* ***************************** Setup logic ************************ */
1695 :
1696 :
1697 : /**
1698 : * Start processing the next wire account.
1699 : * Shuts down if we are done.
1700 : *
1701 : * @param cls `struct WireAccount` with a wire account list to process
1702 : */
1703 : static void
1704 12 : process_credits (void *cls)
1705 : {
1706 12 : struct WireAccount *wa = cls;
1707 : enum GNUNET_DB_QueryStatus qs;
1708 :
1709 : /* skip accounts where CREDIT is not enabled */
1710 12 : while ( (NULL != wa) &&
1711 6 : (GNUNET_NO == wa->ai->credit_enabled) )
1712 0 : wa = wa->next;
1713 12 : if (NULL == wa)
1714 : {
1715 : /* done with all accounts, conclude check */
1716 6 : conclude_credit_history ();
1717 6 : return;
1718 : }
1719 6 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1720 : "Analyzing exchange's wire IN table for account `%s'\n",
1721 : wa->ai->section_name);
1722 6 : qs = TALER_EXCHANGEDB_iterate_reserves_in_above_serial_id_by_account (
1723 : TALER_ARL_edb,
1724 6 : wa->ai->section_name,
1725 : wa->last_reserve_in_serial_id,
1726 : &reserve_in_cb,
1727 : wa);
1728 6 : if (0 > qs)
1729 : {
1730 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1731 0 : global_ret = EXIT_FAILURE;
1732 0 : GNUNET_SCHEDULER_shutdown ();
1733 0 : return;
1734 : }
1735 :
1736 6 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1737 : "Analyzing exchange's KYCAUTH IN table for account `%s'\n",
1738 : wa->ai->section_name);
1739 6 : qs = TALER_EXCHANGEDB_iterate_kycauth_in_above_serial_id_by_account (
1740 : TALER_ARL_edb,
1741 6 : wa->ai->section_name,
1742 : wa->last_kycauth_in_serial_id,
1743 : &kycauth_in_cb,
1744 : wa);
1745 6 : if (0 > qs)
1746 : {
1747 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1748 0 : global_ret = EXIT_FAILURE;
1749 0 : GNUNET_SCHEDULER_shutdown ();
1750 0 : return;
1751 : }
1752 :
1753 6 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1754 : "Starting bank CREDIT history of account `%s'\n",
1755 : wa->ai->section_name);
1756 6 : GNUNET_assert (NULL == wa->chh);
1757 12 : wa->chh = TALER_BANK_credit_history (ctx,
1758 6 : wa->ai->auth,
1759 : wa->wire_off_in,
1760 : MAX_PER_TRANSACTION,
1761 6 : GNUNET_TIME_UNIT_ZERO,
1762 : &history_credit_cb,
1763 : wa);
1764 6 : if (NULL == wa->chh)
1765 : {
1766 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1767 : "Failed to obtain bank transaction history\n");
1768 0 : commit (GNUNET_DB_STATUS_HARD_ERROR);
1769 0 : global_ret = EXIT_FAILURE;
1770 0 : GNUNET_SCHEDULER_shutdown ();
1771 0 : return;
1772 : }
1773 : }
1774 :
1775 :
1776 : /**
1777 : * Begin audit of CREDITs to the exchange.
1778 : */
1779 : static void
1780 6 : begin_credit_audit (void)
1781 : {
1782 6 : GNUNET_assert (NULL == in_map);
1783 6 : in_map = GNUNET_CONTAINER_multihashmap_create (1024,
1784 : GNUNET_YES);
1785 6 : GNUNET_assert (NULL == kycauth_map);
1786 6 : kycauth_map = GNUNET_CONTAINER_multihashmap_create (1024,
1787 : GNUNET_YES);
1788 : /* now go over all bank accounts and check delta with in_map */
1789 6 : process_credits (wa_head);
1790 6 : }
1791 :
1792 :
1793 : static enum GNUNET_DB_QueryStatus
1794 6 : begin_transaction (void)
1795 : {
1796 : enum GNUNET_DB_QueryStatus qs;
1797 :
1798 6 : if (GNUNET_SYSERR ==
1799 6 : TALER_EXCHANGEDB_preflight (TALER_ARL_edb))
1800 : {
1801 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1802 : "Failed to initialize exchange database connection.\n");
1803 0 : return GNUNET_DB_STATUS_HARD_ERROR;
1804 : }
1805 6 : if (GNUNET_SYSERR ==
1806 6 : TALER_AUDITORDB_preflight (TALER_ARL_adb))
1807 : {
1808 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1809 : "Failed to initialize auditor database session.\n");
1810 0 : return GNUNET_DB_STATUS_HARD_ERROR;
1811 : }
1812 6 : global_qs = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
1813 6 : if (GNUNET_OK !=
1814 6 : TALER_AUDITORDB_start (TALER_ARL_adb,
1815 : "auditor-wire-credit"))
1816 : {
1817 0 : GNUNET_break (0);
1818 0 : return GNUNET_DB_STATUS_HARD_ERROR;
1819 : }
1820 6 : if (GNUNET_OK !=
1821 6 : TALER_EXCHANGEDB_start_read_only (TALER_ARL_edb,
1822 : "wire credit auditor"))
1823 : {
1824 0 : GNUNET_break (0);
1825 0 : return GNUNET_DB_STATUS_HARD_ERROR;
1826 : }
1827 6 : qs = TALER_AUDITORDB_get_balance (
1828 : TALER_ARL_adb,
1829 : TALER_ARL_GET_AB (total_wire_in),
1830 : TALER_ARL_GET_AB (total_kycauth_in),
1831 : TALER_ARL_GET_AB (total_kycauth_revenue),
1832 : TALER_ARL_GET_AB (total_wire_credit_fees),
1833 : TALER_ARL_GET_AB (total_bad_amount_in_plus),
1834 : TALER_ARL_GET_AB (total_bad_amount_in_minus),
1835 : TALER_ARL_GET_AB (total_misattribution_in),
1836 : NULL);
1837 6 : switch (qs)
1838 : {
1839 0 : case GNUNET_DB_STATUS_HARD_ERROR:
1840 0 : GNUNET_break (0);
1841 0 : return qs;
1842 0 : case GNUNET_DB_STATUS_SOFT_ERROR:
1843 0 : GNUNET_break (0);
1844 0 : return qs;
1845 0 : case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
1846 : case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
1847 0 : break;
1848 : }
1849 6 : for (struct WireAccount *wa = wa_head;
1850 12 : NULL != wa;
1851 6 : wa = wa->next)
1852 : {
1853 : /* we are called again by rollback_and_reset() after a serialization
1854 : failure, so the labels may already have been computed */
1855 6 : GNUNET_free (wa->label_reserve_in_serial_id);
1856 6 : GNUNET_free (wa->label_kycauth_in_serial_id);
1857 6 : GNUNET_free (wa->label_wire_off_in);
1858 6 : GNUNET_asprintf (&wa->label_reserve_in_serial_id,
1859 : "wire-%s-%s",
1860 6 : wa->ai->section_name,
1861 : "reserve_in_serial_id");
1862 6 : GNUNET_asprintf (&wa->label_kycauth_in_serial_id,
1863 : "wire-%s-%s",
1864 6 : wa->ai->section_name,
1865 : "kycauth_in_serial_id");
1866 6 : GNUNET_asprintf (&wa->label_wire_off_in,
1867 : "wire-%s-%s",
1868 6 : wa->ai->section_name,
1869 : "wire_off_in");
1870 6 : qs = TALER_AUDITORDB_get_auditor_progress (
1871 : TALER_ARL_adb,
1872 6 : wa->label_reserve_in_serial_id,
1873 : &wa->last_reserve_in_serial_id,
1874 : wa->label_kycauth_in_serial_id,
1875 : &wa->last_kycauth_in_serial_id,
1876 : wa->label_wire_off_in,
1877 : &wa->wire_off_in,
1878 : NULL);
1879 6 : if (0 > qs)
1880 : {
1881 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1882 0 : return qs;
1883 : }
1884 6 : wa->start_reserve_in_serial_id = wa->last_reserve_in_serial_id;
1885 6 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1886 : "Starting from reserve_in at %s=%llu for account `%s'\n",
1887 : wa->label_reserve_in_serial_id,
1888 : (unsigned long long) wa->start_reserve_in_serial_id,
1889 : wa->ai->section_name);
1890 : }
1891 :
1892 6 : begin_credit_audit ();
1893 6 : return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
1894 : }
1895 :
1896 :
1897 : /**
1898 : * Function called with information about a wire account. Adds the
1899 : * account to our list for processing (if it is enabled and we can
1900 : * load the plugin).
1901 : *
1902 : * @param cls closure, NULL
1903 : * @param ai account information
1904 : */
1905 : static void
1906 6 : process_account_cb (void *cls,
1907 : const struct TALER_EXCHANGEDB_AccountInfo *ai)
1908 : {
1909 : struct WireAccount *wa;
1910 :
1911 : (void) cls;
1912 6 : if ((! ai->debit_enabled) &&
1913 0 : (! ai->credit_enabled))
1914 0 : return; /* not an active exchange account */
1915 6 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1916 : "Found exchange account `%s'\n",
1917 : ai->section_name);
1918 6 : wa = GNUNET_new (struct WireAccount);
1919 6 : wa->ai = ai;
1920 6 : GNUNET_CONTAINER_DLL_insert (wa_head,
1921 : wa_tail,
1922 : wa);
1923 : }
1924 :
1925 :
1926 : /**
1927 : * Function called on events received from Postgres.
1928 : *
1929 : * @param cls closure, NULL
1930 : * @param extra additional event data provided
1931 : * @param extra_size number of bytes in @a extra
1932 : */
1933 : static void
1934 0 : db_notify (void *cls,
1935 : const void *extra,
1936 : size_t extra_size)
1937 : {
1938 : (void) cls;
1939 : (void) extra;
1940 : (void) extra_size;
1941 :
1942 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1943 : "Received notification to wake wire helper\n");
1944 : /* If there are accounts we are still processing, abort
1945 : the HTTP requests so we can start afresh. */
1946 0 : for (struct WireAccount *wa = wa_head;
1947 0 : NULL != wa;
1948 0 : wa = wa->next)
1949 : {
1950 0 : if (NULL != wa->chh)
1951 : {
1952 0 : TALER_BANK_credit_history_cancel (wa->chh);
1953 0 : wa->chh = NULL;
1954 : }
1955 0 : conclude_account (wa);
1956 : }
1957 :
1958 0 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
1959 0 : begin_transaction ())
1960 : {
1961 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1962 : "Audit failed\n");
1963 0 : GNUNET_break (0);
1964 0 : global_ret = EXIT_FAILURE;
1965 0 : GNUNET_SCHEDULER_shutdown ();
1966 : }
1967 0 : }
1968 :
1969 :
1970 : /**
1971 : * Main function that will be run.
1972 : *
1973 : * @param cls closure
1974 : * @param args remaining command-line arguments
1975 : * @param cfgfile name of the configuration file used (for saving, can be NULL!)
1976 : * @param c configuration
1977 : */
1978 : static void
1979 6 : run (void *cls,
1980 : char *const *args,
1981 : const char *cfgfile,
1982 : const struct GNUNET_CONFIGURATION_Handle *c)
1983 : {
1984 : (void) cls;
1985 : (void) args;
1986 : (void) cfgfile;
1987 6 : cfg = c;
1988 6 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1989 : "Launching wire-credit auditor\n");
1990 6 : if (EXIT_SUCCESS !=
1991 6 : (global_ret = TALER_ARL_init (c)))
1992 : {
1993 0 : return;
1994 : }
1995 6 : if (GNUNET_OK !=
1996 6 : TALER_config_get_amount (TALER_ARL_cfg,
1997 : "auditor",
1998 : "TINY_AMOUNT",
1999 : &tiny_amount))
2000 : {
2001 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
2002 : "auditor",
2003 : "TINY_AMOUNT");
2004 0 : global_ret = EXIT_NOTCONFIGURED;
2005 0 : return;
2006 : }
2007 6 : GNUNET_assert (GNUNET_OK ==
2008 : TALER_amount_set_zero (TALER_ARL_currency,
2009 : &zero));
2010 6 : GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
2011 : NULL);
2012 6 : ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
2013 : &rc);
2014 6 : rc = GNUNET_CURL_gnunet_rc_create (ctx);
2015 6 : if (NULL == ctx)
2016 : {
2017 0 : GNUNET_break (0);
2018 0 : global_ret = EXIT_FAILURE;
2019 0 : return;
2020 : }
2021 6 : if (GNUNET_OK !=
2022 6 : TALER_EXCHANGEDB_load_accounts (TALER_ARL_cfg,
2023 : TALER_EXCHANGEDB_ALO_CREDIT
2024 : | TALER_EXCHANGEDB_ALO_AUTHDATA))
2025 : {
2026 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2027 : "No bank accounts configured\n");
2028 0 : global_ret = EXIT_NOTCONFIGURED;
2029 0 : GNUNET_SCHEDULER_shutdown ();
2030 0 : return;
2031 : }
2032 6 : TALER_EXCHANGEDB_find_accounts (&process_account_cb,
2033 : NULL);
2034 :
2035 6 : if (0 == test_mode)
2036 : {
2037 0 : struct GNUNET_DB_EventHeaderP es = {
2038 0 : .size = htons (sizeof (es)),
2039 0 : .type = htons (TALER_DBEVENT_EXCHANGE_AUDITOR_WAKE_HELPER_WIRE)
2040 : };
2041 :
2042 0 : eh = TALER_AUDITORDB_event_listen (TALER_ARL_adb,
2043 : &es,
2044 0 : GNUNET_TIME_UNIT_FOREVER_REL,
2045 : &db_notify,
2046 : NULL);
2047 0 : GNUNET_assert (NULL != eh);
2048 : }
2049 6 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
2050 6 : begin_transaction ())
2051 : {
2052 0 : GNUNET_break (0);
2053 0 : global_ret = EXIT_FAILURE;
2054 0 : GNUNET_SCHEDULER_shutdown ();
2055 0 : return;
2056 : }
2057 : }
2058 :
2059 :
2060 : /**
2061 : * The main function of the wire auditing tool. Checks that
2062 : * the exchange's records of wire transfers match that of
2063 : * the wire gateway.
2064 : *
2065 : * @param argc number of arguments from the command line
2066 : * @param argv command line arguments
2067 : * @return 0 ok, 1 on error
2068 : */
2069 : int
2070 6 : main (int argc,
2071 : char *const *argv)
2072 : {
2073 6 : const struct GNUNET_GETOPT_CommandLineOption options[] = {
2074 6 : GNUNET_GETOPT_option_flag ('i',
2075 : "internal",
2076 : "perform checks only applicable for exchange-internal audits",
2077 : &internal_checks),
2078 6 : GNUNET_GETOPT_option_flag ('I',
2079 : "ignore-not-found",
2080 : "continue, even if the bank account of the exchange was not found",
2081 : &ignore_account_404),
2082 6 : GNUNET_GETOPT_option_flag ('t',
2083 : "test",
2084 : "run in test mode and exit when idle",
2085 : &test_mode),
2086 6 : GNUNET_GETOPT_option_timetravel ('T',
2087 : "timetravel"),
2088 : GNUNET_GETOPT_OPTION_END
2089 : };
2090 : enum GNUNET_GenericReturnValue ret;
2091 :
2092 6 : ret = GNUNET_PROGRAM_run (
2093 : TALER_AUDITOR_project_data (),
2094 : argc,
2095 : argv,
2096 : "taler-helper-auditor-wire-credit",
2097 : gettext_noop (
2098 : "Audit exchange database for consistency with the bank's wire transfers"),
2099 : options,
2100 : &run,
2101 : NULL);
2102 6 : if (GNUNET_SYSERR == ret)
2103 0 : return EXIT_NOTCONFIGURED;
2104 6 : if (GNUNET_NO == ret)
2105 0 : return EXIT_SUCCESS;
2106 6 : return global_ret;
2107 : }
2108 :
2109 :
2110 : /* end of taler-helper-auditor-wire-credit.c */
|