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 "taler_auditordb_plugin.h"
28 : #include "taler_exchangedb_lib.h"
29 : #include "taler_json_lib.h"
30 : #include "taler_bank_service.h"
31 : #include "taler_signatures.h"
32 : #include "report-lib.h"
33 : #include "taler_dbevents.h"
34 :
35 :
36 : /**
37 : * How much time do we allow the aggregator to lag behind? If
38 : * wire transfers should have been made more than #GRACE_PERIOD
39 : * before, we issue warnings.
40 : */
41 : #define GRACE_PERIOD GNUNET_TIME_UNIT_HOURS
42 :
43 : /**
44 : * Maximum number of wire transfers we process per
45 : * (database) transaction.
46 : */
47 : #define MAX_PER_TRANSACTION 1024
48 :
49 : /**
50 : * How much do we allow the bank and the exchange to disagree about
51 : * timestamps? Should be sufficiently large to avoid bogus reports from deltas
52 : * created by imperfect clock synchronization and network delay.
53 : */
54 : #define TIME_TOLERANCE GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, \
55 : 15)
56 :
57 :
58 : /**
59 : * Run in test mode. Exit when idle instead of
60 : * going to sleep and waiting for more work.
61 : */
62 : static int test_mode;
63 :
64 : /**
65 : * Information we keep for each supported account.
66 : */
67 : struct WireAccount
68 : {
69 : /**
70 : * Accounts are kept in a DLL.
71 : */
72 : struct WireAccount *next;
73 :
74 : /**
75 : * Plugins are kept in a DLL.
76 : */
77 : struct WireAccount *prev;
78 :
79 : /**
80 : * Account details.
81 : */
82 : const struct TALER_EXCHANGEDB_AccountInfo *ai;
83 :
84 : /**
85 : * Active wire request for the transaction history.
86 : */
87 : struct TALER_BANK_CreditHistoryHandle *chh;
88 :
89 : /**
90 : * Progress point for this account.
91 : */
92 : uint64_t last_reserve_in_serial_id;
93 :
94 : /**
95 : * Initial progress point for this account.
96 : */
97 : uint64_t start_reserve_in_serial_id;
98 :
99 : /**
100 : * Where we are in the inbound transaction history.
101 : */
102 : uint64_t wire_off_in;
103 :
104 : /**
105 : * Label under which we store our pp's reserve_in_serial_id.
106 : */
107 : char *label_reserve_in_serial_id;
108 :
109 : /**
110 : * Label under which we store our wire_off_in.
111 : */
112 : char *label_wire_off_in;
113 :
114 : };
115 :
116 :
117 : /**
118 : * Return value from main().
119 : */
120 : static int global_ret;
121 :
122 : /**
123 : * State of the current database transaction with
124 : * the auditor DB.
125 : */
126 : static enum GNUNET_DB_QueryStatus global_qs;
127 :
128 : /**
129 : * Map with information about incoming wire transfers.
130 : * Maps hashes of the wire offsets to `struct ReserveInInfo`s.
131 : */
132 : static struct GNUNET_CONTAINER_MultiHashMap *in_map;
133 :
134 : /**
135 : * Head of list of wire accounts we still need to look at.
136 : */
137 : static struct WireAccount *wa_head;
138 :
139 : /**
140 : * Tail of list of wire accounts we still need to look at.
141 : */
142 : static struct WireAccount *wa_tail;
143 :
144 : /**
145 : * Amount that is considered "tiny"
146 : */
147 : static struct TALER_Amount tiny_amount;
148 :
149 : /**
150 : * Total amount that was transferred too much to the exchange.
151 : */
152 : static TALER_ARL_DEF_AB (total_bad_amount_in_plus);
153 :
154 : /**
155 : * Total amount that was transferred too little to the exchange.
156 : */
157 : static TALER_ARL_DEF_AB (total_bad_amount_in_minus);
158 :
159 : /**
160 : * Total amount where the exchange has the wrong sender account
161 : * for incoming funds and may thus wire funds to the wrong
162 : * destination when closing the reserve.
163 : */
164 : static TALER_ARL_DEF_AB (total_misattribution_in);
165 :
166 : /**
167 : * Total amount credited to exchange accounts.
168 : */
169 : static TALER_ARL_DEF_AB (total_wire_in);
170 :
171 : /**
172 : * Total amount credited to exchange accounts via KYCAUTH
173 : */
174 : static TALER_ARL_DEF_AB (total_kycauth_in);
175 :
176 : /**
177 : * Total wire credit fees charged to the exchange account.
178 : */
179 : static TALER_ARL_DEF_AB (total_wire_credit_fees);
180 :
181 : /**
182 : * Amount of zero in our currency.
183 : */
184 : static struct TALER_Amount zero;
185 :
186 : /**
187 : * Handle to the context for interacting with the bank.
188 : */
189 : static struct GNUNET_CURL_Context *ctx;
190 :
191 : /**
192 : * Scheduler context for running the @e ctx.
193 : */
194 : static struct GNUNET_CURL_RescheduleContext *rc;
195 :
196 : /**
197 : * Should we run checks that only work for exchange-internal audits?
198 : */
199 : static int internal_checks;
200 :
201 : /**
202 : * Should we ignore if the bank does not know our bank
203 : * account?
204 : */
205 : static int ignore_account_404;
206 :
207 : /**
208 : * Database event handler to wake us up again.
209 : */
210 : static struct GNUNET_DB_EventHandler *eh;
211 :
212 : /**
213 : * The auditors's configuration.
214 : */
215 : static const struct GNUNET_CONFIGURATION_Handle *cfg;
216 :
217 : /* ***************************** Shutdown **************************** */
218 :
219 : /**
220 : * Entry in map with wire information we expect to obtain from the
221 : * bank later.
222 : */
223 : struct ReserveInInfo
224 : {
225 :
226 : /**
227 : * Hash of expected row offset.
228 : */
229 : struct GNUNET_HashCode row_off_hash;
230 :
231 : /**
232 : * Expected details about the wire transfer.
233 : * The member "account_url" is to be allocated
234 : * at the end of this struct!
235 : */
236 : struct TALER_BANK_CreditDetails credit_details;
237 :
238 : /**
239 : * RowID in reserves_in table.
240 : */
241 : uint64_t rowid;
242 :
243 : };
244 :
245 :
246 : /**
247 : * Free entry in #in_map.
248 : *
249 : * @param cls NULL
250 : * @param key unused key
251 : * @param value the `struct ReserveInInfo` to free
252 : * @return #GNUNET_OK
253 : */
254 : static enum GNUNET_GenericReturnValue
255 68 : free_rii (void *cls,
256 : const struct GNUNET_HashCode *key,
257 : void *value)
258 : {
259 68 : struct ReserveInInfo *rii = value;
260 :
261 : (void) cls;
262 68 : GNUNET_assert (GNUNET_YES ==
263 : GNUNET_CONTAINER_multihashmap_remove (in_map,
264 : key,
265 : rii));
266 68 : GNUNET_free (rii);
267 68 : return GNUNET_OK;
268 : }
269 :
270 :
271 : /**
272 : * Task run on shutdown.
273 : *
274 : * @param cls NULL
275 : */
276 : static void
277 68 : do_shutdown (void *cls)
278 : {
279 : struct WireAccount *wa;
280 :
281 : (void) cls;
282 68 : if (NULL != eh)
283 : {
284 0 : TALER_ARL_adb->event_listen_cancel (eh);
285 0 : eh = NULL;
286 : }
287 68 : TALER_ARL_done ();
288 68 : if (NULL != in_map)
289 : {
290 0 : GNUNET_CONTAINER_multihashmap_iterate (in_map,
291 : &free_rii,
292 : NULL);
293 0 : GNUNET_CONTAINER_multihashmap_destroy (in_map);
294 0 : in_map = NULL;
295 : }
296 136 : while (NULL != (wa = wa_head))
297 : {
298 68 : if (NULL != wa->chh)
299 : {
300 0 : TALER_BANK_credit_history_cancel (wa->chh);
301 0 : wa->chh = NULL;
302 : }
303 68 : GNUNET_CONTAINER_DLL_remove (wa_head,
304 : wa_tail,
305 : wa);
306 68 : GNUNET_free (wa->label_reserve_in_serial_id);
307 68 : GNUNET_free (wa->label_wire_off_in);
308 68 : GNUNET_free (wa);
309 : }
310 68 : if (NULL != ctx)
311 : {
312 68 : GNUNET_CURL_fini (ctx);
313 68 : ctx = NULL;
314 : }
315 68 : if (NULL != rc)
316 : {
317 68 : GNUNET_CURL_gnunet_rc_destroy (rc);
318 68 : rc = NULL;
319 : }
320 68 : TALER_EXCHANGEDB_unload_accounts ();
321 68 : TALER_ARL_cfg = NULL;
322 68 : }
323 :
324 :
325 : /**
326 : * Start the database transactions and begin the audit.
327 : *
328 : * @return transaction status code
329 : */
330 : static enum GNUNET_DB_QueryStatus
331 : begin_transaction (void);
332 :
333 :
334 : /**
335 : * Rollback the current transaction, reset our state and try
336 : * again (we had a serialization error).
337 : */
338 : static void
339 0 : rollback_and_reset (void)
340 : {
341 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
342 : "Serialization issue, trying again\n");
343 0 : TALER_ARL_adb->rollback (TALER_ARL_adb->cls);
344 0 : for (unsigned int max_retries = 3; max_retries>0; max_retries--)
345 : {
346 : enum GNUNET_DB_QueryStatus qs;
347 :
348 0 : if (NULL != in_map)
349 : {
350 0 : GNUNET_CONTAINER_multihashmap_iterate (in_map,
351 : &free_rii,
352 : NULL);
353 0 : GNUNET_CONTAINER_multihashmap_destroy (in_map);
354 0 : in_map = NULL;
355 : }
356 0 : qs = begin_transaction ();
357 0 : if (GNUNET_DB_STATUS_HARD_ERROR == qs)
358 0 : break;
359 : }
360 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
361 : "Hard database error, terminating\n");
362 0 : GNUNET_SCHEDULER_shutdown ();
363 0 : }
364 :
365 :
366 : /**
367 : * Commit the transaction, checkpointing our progress in the auditor DB.
368 : *
369 : * @param qs transaction status so far
370 : */
371 : static void
372 68 : commit (enum GNUNET_DB_QueryStatus qs)
373 : {
374 68 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
375 : "Transaction logic ended with status %d\n",
376 : qs);
377 68 : TALER_ARL_edb->rollback (TALER_ARL_edb->cls);
378 68 : if (qs < 0)
379 0 : goto handle_db_error;
380 68 : qs = TALER_ARL_adb->update_balance (
381 68 : TALER_ARL_adb->cls,
382 : TALER_ARL_SET_AB (total_wire_in),
383 : TALER_ARL_SET_AB (total_kycauth_in),
384 : TALER_ARL_SET_AB (total_wire_credit_fees),
385 : TALER_ARL_SET_AB (total_bad_amount_in_plus),
386 : TALER_ARL_SET_AB (total_bad_amount_in_minus),
387 : TALER_ARL_SET_AB (total_misattribution_in),
388 : NULL);
389 68 : if (0 > qs)
390 0 : goto handle_db_error;
391 68 : qs = TALER_ARL_adb->insert_balance (
392 68 : TALER_ARL_adb->cls,
393 : TALER_ARL_SET_AB (total_wire_in),
394 : TALER_ARL_SET_AB (total_kycauth_in),
395 : TALER_ARL_SET_AB (total_wire_credit_fees),
396 : TALER_ARL_SET_AB (total_bad_amount_in_plus),
397 : TALER_ARL_SET_AB (total_bad_amount_in_minus),
398 : TALER_ARL_SET_AB (total_misattribution_in),
399 : NULL);
400 68 : if (0 > qs)
401 0 : goto handle_db_error;
402 68 : for (struct WireAccount *wa = wa_head;
403 136 : NULL != wa;
404 68 : wa = wa->next)
405 : {
406 68 : qs = TALER_ARL_adb->update_auditor_progress (
407 68 : TALER_ARL_adb->cls,
408 68 : wa->label_reserve_in_serial_id,
409 : wa->last_reserve_in_serial_id,
410 : wa->label_wire_off_in,
411 : wa->wire_off_in,
412 : NULL);
413 68 : if (0 > qs)
414 0 : goto handle_db_error;
415 68 : qs = TALER_ARL_adb->insert_auditor_progress (
416 68 : TALER_ARL_adb->cls,
417 68 : wa->label_reserve_in_serial_id,
418 : wa->last_reserve_in_serial_id,
419 : wa->label_wire_off_in,
420 : wa->wire_off_in,
421 : NULL);
422 68 : if (0 > qs)
423 0 : goto handle_db_error;
424 68 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
425 : "Transaction ends at %s=%llu for account `%s'\n",
426 : wa->label_reserve_in_serial_id,
427 : (unsigned long long) wa->last_reserve_in_serial_id,
428 : wa->ai->section_name);
429 : }
430 68 : qs = TALER_ARL_adb->commit (TALER_ARL_adb->cls);
431 68 : if (0 > qs)
432 : {
433 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
434 0 : goto handle_db_error;
435 : }
436 68 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
437 : "Transaction concluded!\n");
438 68 : if (1 == test_mode)
439 68 : GNUNET_SCHEDULER_shutdown ();
440 68 : return;
441 0 : handle_db_error:
442 0 : rollback_and_reset ();
443 : }
444 :
445 :
446 : /**
447 : * Conclude the credit history check by logging entries that
448 : * were not found and freeing resources. Then move on to
449 : * processing debits.
450 : */
451 : static void
452 68 : conclude_credit_history (void)
453 : {
454 68 : if (NULL != in_map)
455 : {
456 68 : GNUNET_assert (0 ==
457 : GNUNET_CONTAINER_multihashmap_size (in_map));
458 68 : GNUNET_CONTAINER_multihashmap_destroy (in_map);
459 68 : in_map = NULL;
460 : }
461 68 : commit (global_qs);
462 68 : }
463 :
464 :
465 : /**
466 : * Check if the given wire transfers are equivalent.
467 : *
468 : * @param credit amount that was received
469 : * @param credit2 2nd amount that was received
470 : * @param reserve_pub public key of the reserve (also the WTID)
471 : * @param reserve_pub2 2nd public key of the reserve (also the WTID)
472 : * @param sender_account_details payto://-URL of the sender's bank account
473 : * @param sender_account_details2 2nd payto://-URL of the sender's bank account
474 : * @param execution_date when did we receive the funds
475 : * @param execution_date2 2nd when did we receive the funds
476 : * @return #GNUNET_YES if so,
477 : * #GNUNET_NO if not
478 : * #GNUNET_SYSERR on internal error
479 : */
480 : static enum GNUNET_GenericReturnValue
481 0 : check_equality (const struct TALER_Amount *credit,
482 : const struct TALER_Amount *credit2,
483 : const struct TALER_ReservePublicKeyP *reserve_pub,
484 : const struct TALER_ReservePublicKeyP *reserve_pub2,
485 : const struct TALER_FullPayto sender_account_details,
486 : const struct TALER_FullPayto sender_account_details2,
487 : struct GNUNET_TIME_Timestamp execution_date,
488 : struct GNUNET_TIME_Timestamp execution_date2)
489 : {
490 0 : if (0 != TALER_amount_cmp (credit,
491 : credit2))
492 0 : return GNUNET_NO;
493 0 : if (0 != GNUNET_memcmp (reserve_pub,
494 : reserve_pub2))
495 0 : return GNUNET_NO;
496 : {
497 : struct TALER_NormalizedPayto np;
498 : struct TALER_NormalizedPayto np2;
499 : bool fail;
500 :
501 0 : np = TALER_payto_normalize (sender_account_details);
502 0 : np2 = TALER_payto_normalize (sender_account_details2);
503 0 : fail = (0 != TALER_normalized_payto_cmp (np,
504 : np2));
505 0 : GNUNET_free (np.normalized_payto);
506 0 : GNUNET_free (np2.normalized_payto);
507 0 : if (fail)
508 0 : return GNUNET_NO;
509 : }
510 0 : if (GNUNET_TIME_timestamp_cmp (execution_date,
511 : !=,
512 : execution_date2))
513 0 : return GNUNET_NO;
514 0 : return GNUNET_YES;
515 : }
516 :
517 :
518 : /**
519 : * Function called with details about incoming wire transfers
520 : * as claimed by the exchange DB.
521 : *
522 : * @param cls a `struct WireAccount` we are processing
523 : * @param rowid unique serial ID for the entry in our DB
524 : * @param reserve_pub public key of the reserve (also the WTID)
525 : * @param credit amount that was received
526 : * @param sender_account_details payto://-URL of the sender's bank account
527 : * @param wire_reference unique identifier for the wire transfer
528 : * @param execution_date when did we receive the funds
529 : * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
530 : */
531 : static enum GNUNET_GenericReturnValue
532 68 : reserve_in_cb (void *cls,
533 : uint64_t rowid,
534 : const struct TALER_ReservePublicKeyP *reserve_pub,
535 : const struct TALER_Amount *credit,
536 : const struct TALER_FullPayto sender_account_details,
537 : uint64_t wire_reference,
538 : struct GNUNET_TIME_Timestamp execution_date)
539 : {
540 68 : struct WireAccount *wa = cls;
541 : struct ReserveInInfo *rii;
542 : size_t slen;
543 :
544 68 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
545 : "Analyzing exchange wire IN (%llu) at %s of %s with reserve_pub %s\n",
546 : (unsigned long long) rowid,
547 : GNUNET_TIME_timestamp2s (execution_date),
548 : TALER_amount2s (credit),
549 : TALER_B2S (reserve_pub));
550 68 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_wire_in),
551 : &TALER_ARL_USE_AB (total_wire_in),
552 : credit);
553 : {
554 : enum GNUNET_DB_QueryStatus qs;
555 : struct TALER_AUDITORDB_ReserveInInconsistency dc;
556 :
557 68 : qs = TALER_ARL_adb->select_reserve_in_inconsistency (
558 68 : TALER_ARL_adb->cls,
559 : wire_reference,
560 : &dc);
561 68 : switch (qs)
562 : {
563 0 : case GNUNET_DB_STATUS_HARD_ERROR:
564 : case GNUNET_DB_STATUS_SOFT_ERROR:
565 0 : global_qs = qs;
566 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
567 0 : return GNUNET_SYSERR;
568 68 : case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
569 68 : break;
570 0 : case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
571 0 : if (TALER_amount_is_zero (&dc.amount_exchange_expected))
572 : {
573 : /* database entry indicates unmatched transaction */
574 : enum GNUNET_GenericReturnValue ret;
575 :
576 0 : ret = check_equality (&dc.amount_wired,
577 : credit,
578 : &dc.reserve_pub,
579 : reserve_pub,
580 : dc.account,
581 : sender_account_details,
582 : GNUNET_TIME_absolute_to_timestamp (dc.timestamp),
583 : execution_date);
584 0 : if (GNUNET_SYSERR == ret)
585 0 : return GNUNET_SYSERR;
586 0 : if (GNUNET_YES == ret)
587 : {
588 0 : qs = TALER_ARL_adb->delete_reserve_in_inconsistency (
589 0 : TALER_ARL_adb->cls,
590 : dc.serial_id);
591 0 : if (qs < 0)
592 : {
593 0 : global_qs = qs;
594 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
595 0 : return GNUNET_SYSERR;
596 : }
597 0 : return GNUNET_OK;
598 : }
599 : }
600 0 : break;
601 : }
602 : }
603 68 : slen = strlen (sender_account_details.full_payto) + 1;
604 68 : rii = GNUNET_malloc (sizeof (struct ReserveInInfo) + slen);
605 68 : rii->rowid = rowid;
606 68 : rii->credit_details.type = TALER_BANK_CT_RESERVE;
607 68 : rii->credit_details.amount = *credit;
608 68 : rii->credit_details.execution_date = execution_date;
609 68 : rii->credit_details.details.reserve.reserve_pub = *reserve_pub;
610 68 : rii->credit_details.debit_account_uri.full_payto = (char *) &rii[1];
611 68 : GNUNET_memcpy (&rii[1],
612 : sender_account_details.full_payto,
613 : slen);
614 68 : GNUNET_CRYPTO_hash (&wire_reference,
615 : sizeof (uint64_t),
616 : &rii->row_off_hash);
617 68 : if (GNUNET_OK !=
618 68 : GNUNET_CONTAINER_multihashmap_put (in_map,
619 68 : &rii->row_off_hash,
620 : rii,
621 : GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
622 : {
623 0 : struct TALER_AUDITORDB_RowInconsistency ri = {
624 : .row_id = rowid,
625 : .row_table = (char *) "reserves_in",
626 : .diagnostic = (char *) "duplicate wire offset"
627 : };
628 : enum GNUNET_DB_QueryStatus qs;
629 :
630 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
631 : "Duplicate wire offset\n");
632 0 : qs = TALER_ARL_adb->insert_row_inconsistency (
633 0 : TALER_ARL_adb->cls,
634 : &ri);
635 0 : GNUNET_free (rii);
636 0 : if (qs < 0)
637 : {
638 0 : global_qs = qs;
639 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
640 0 : return GNUNET_SYSERR;
641 : }
642 0 : return GNUNET_OK;
643 : }
644 68 : wa->last_reserve_in_serial_id = rowid + 1;
645 68 : return GNUNET_OK;
646 : }
647 :
648 :
649 : /**
650 : * Complain that we failed to match an entry from #in_map.
651 : *
652 : * @param cls a `struct WireAccount`
653 : * @param key unused key
654 : * @param value the `struct ReserveInInfo` to free
655 : * @return #GNUNET_OK
656 : */
657 : static enum GNUNET_GenericReturnValue
658 0 : complain_in_not_found (void *cls,
659 : const struct GNUNET_HashCode *key,
660 : void *value)
661 : {
662 0 : struct WireAccount *wa = cls;
663 0 : struct ReserveInInfo *rii = value;
664 : enum GNUNET_DB_QueryStatus qs;
665 0 : struct TALER_AUDITORDB_ReserveInInconsistency riiDb = {
666 0 : .bank_row_id = rii->rowid,
667 : .diagnostic = (char *)
668 : "incoming wire transfer claimed by exchange not found",
669 0 : .account = wa->ai->payto_uri,
670 : .amount_exchange_expected = rii->credit_details.amount,
671 : .amount_wired = zero,
672 : .reserve_pub = rii->credit_details.details.reserve.reserve_pub,
673 : .timestamp = rii->credit_details.execution_date.abs_time
674 : };
675 :
676 : (void) key;
677 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
678 : "Incoming wire transfer #%llu claimed by exchange not found\n",
679 : (unsigned long long) rii->rowid);
680 0 : GNUNET_assert (TALER_BANK_CT_RESERVE ==
681 : rii->credit_details.type);
682 0 : qs = TALER_ARL_adb->insert_reserve_in_inconsistency (
683 0 : TALER_ARL_adb->cls,
684 : &riiDb);
685 0 : if (qs < 0)
686 : {
687 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
688 0 : global_qs = qs;
689 0 : return GNUNET_SYSERR;
690 : }
691 0 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_in_minus),
692 : &TALER_ARL_USE_AB (total_bad_amount_in_minus),
693 : &rii->credit_details.amount);
694 0 : return GNUNET_OK;
695 : }
696 :
697 :
698 : /**
699 : * Start processing the next wire account.
700 : * Shuts down if we are done.
701 : *
702 : * @param cls `struct WireAccount` with a wire account list to process
703 : */
704 : static void
705 : process_credits (void *cls);
706 :
707 :
708 : /**
709 : * We got all of the incoming transactions for @a wa,
710 : * finish processing the account.
711 : *
712 : * @param[in,out] wa wire account to process
713 : */
714 : static void
715 68 : conclude_account (struct WireAccount *wa)
716 : {
717 68 : GNUNET_assert (NULL == wa->chh);
718 68 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
719 : "Reconciling CREDIT processing of account `%s'\n",
720 : wa->ai->section_name);
721 68 : if (NULL != in_map)
722 : {
723 68 : GNUNET_CONTAINER_multihashmap_iterate (in_map,
724 : &complain_in_not_found,
725 : wa);
726 : /* clean up before 2nd phase */
727 68 : GNUNET_CONTAINER_multihashmap_iterate (in_map,
728 : &free_rii,
729 : NULL);
730 68 : if (global_qs < 0)
731 : {
732 0 : commit (global_qs);
733 0 : return;
734 : }
735 : }
736 68 : process_credits (wa->next);
737 : }
738 :
739 :
740 : /**
741 : * Analyze credit transaction @a details into @a wa.
742 : *
743 : * @param[in,out] wa account that received the transfer
744 : * @param credit_details transfer details
745 : * @return true on success, false to stop loop at this point
746 : */
747 : static bool
748 68 : analyze_credit (
749 : struct WireAccount *wa,
750 : const struct TALER_BANK_CreditDetails *credit_details)
751 : {
752 : struct ReserveInInfo *rii;
753 : struct GNUNET_HashCode key;
754 :
755 68 : switch (credit_details->type)
756 : {
757 68 : case TALER_BANK_CT_RESERVE:
758 68 : break;
759 0 : case TALER_BANK_CT_KYCAUTH:
760 0 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_kycauth_in),
761 : &TALER_ARL_USE_AB (total_kycauth_in),
762 : &credit_details->amount);
763 0 : return true;
764 0 : case TALER_BANK_CT_WAD:
765 0 : GNUNET_break (0); /* FIXME: Wad not yet supported */
766 0 : return false;
767 : }
768 68 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
769 : "Analyzing bank CREDIT #%llu at %s of %s with Reserve-pub %s\n",
770 : (unsigned long long) credit_details->serial_id,
771 : GNUNET_TIME_timestamp2s (credit_details->execution_date),
772 : TALER_amount2s (&credit_details->amount),
773 : TALER_B2S (&credit_details->details.reserve.reserve_pub));
774 68 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_wire_credit_fees),
775 : &TALER_ARL_USE_AB (total_wire_credit_fees),
776 : &credit_details->credit_fee);
777 68 : GNUNET_CRYPTO_hash (&credit_details->serial_id,
778 : sizeof (credit_details->serial_id),
779 : &key);
780 68 : rii = GNUNET_CONTAINER_multihashmap_get (in_map,
781 : &key);
782 68 : if (NULL == rii)
783 : {
784 0 : struct TALER_AUDITORDB_ReserveInInconsistency dc = {
785 0 : .bank_row_id = credit_details->serial_id,
786 : .amount_exchange_expected = zero,
787 : .amount_wired = credit_details->amount,
788 : .reserve_pub = credit_details->details.reserve.reserve_pub,
789 : .timestamp = credit_details->execution_date.abs_time,
790 : .account = credit_details->debit_account_uri,
791 : .diagnostic = (char *) "unknown to exchange"
792 : };
793 : enum GNUNET_DB_QueryStatus qs;
794 :
795 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
796 : "Failed to find wire transfer at `%s' in exchange database.\n",
797 : GNUNET_TIME_timestamp2s (credit_details->execution_date));
798 0 : qs = TALER_ARL_adb->insert_reserve_in_inconsistency (TALER_ARL_adb->cls,
799 : &dc);
800 0 : if (qs <= 0)
801 : {
802 0 : global_qs = qs;
803 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
804 0 : return false;
805 : }
806 0 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_in_plus),
807 : &TALER_ARL_USE_AB (total_bad_amount_in_plus),
808 : &credit_details->amount);
809 0 : return true;
810 : }
811 :
812 : /* Update offset */
813 68 : wa->wire_off_in = credit_details->serial_id;
814 : /* compare records with expected data */
815 68 : if (0 != GNUNET_memcmp (&credit_details->details.reserve.reserve_pub,
816 : &rii->credit_details.details.reserve.reserve_pub))
817 : {
818 1 : struct TALER_AUDITORDB_ReserveInInconsistency riiDb = {
819 1 : .bank_row_id = credit_details->serial_id,
820 : .amount_exchange_expected = rii->credit_details.amount,
821 : .amount_wired = zero,
822 : .reserve_pub = rii->credit_details.details.reserve.reserve_pub,
823 : .timestamp = rii->credit_details.execution_date.abs_time,
824 1 : .account = wa->ai->payto_uri,
825 : .diagnostic = (char *) "wire subject does not match"
826 : };
827 : enum GNUNET_DB_QueryStatus qs;
828 :
829 1 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
830 : "Reserve public key differs\n");
831 1 : qs = TALER_ARL_adb->insert_reserve_in_inconsistency (
832 1 : TALER_ARL_adb->cls,
833 : &riiDb);
834 1 : if (qs <= 0)
835 : {
836 0 : global_qs = qs;
837 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
838 0 : return false;
839 : }
840 1 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_in_minus),
841 : &TALER_ARL_USE_AB (total_bad_amount_in_minus),
842 : &rii->credit_details.amount);
843 1 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_in_plus),
844 : &TALER_ARL_USE_AB (total_bad_amount_in_plus),
845 : &credit_details->amount);
846 1 : GNUNET_assert (GNUNET_OK ==
847 : free_rii (NULL,
848 : &key,
849 : rii));
850 1 : return true;
851 : }
852 67 : if (0 != TALER_amount_cmp (&rii->credit_details.amount,
853 : &credit_details->amount))
854 : {
855 5 : struct TALER_AUDITORDB_ReserveInInconsistency riiDb = {
856 : .diagnostic = (char *) "wire amount does not match",
857 5 : .account = wa->ai->payto_uri,
858 5 : .bank_row_id = credit_details->serial_id,
859 : .amount_exchange_expected = rii->credit_details.amount,
860 : .amount_wired = credit_details->amount,
861 : .reserve_pub = rii->credit_details.details.reserve.reserve_pub,
862 : .timestamp = rii->credit_details.execution_date.abs_time
863 : };
864 : enum GNUNET_DB_QueryStatus qs;
865 :
866 5 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
867 : "Wire transfer amount differs\n");
868 5 : qs = TALER_ARL_adb->insert_reserve_in_inconsistency (
869 5 : TALER_ARL_adb->cls,
870 : &riiDb);
871 5 : if (qs <= 0)
872 : {
873 0 : global_qs = qs;
874 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
875 0 : return false;
876 : }
877 5 : if (0 < TALER_amount_cmp (&credit_details->amount,
878 5 : &rii->credit_details.amount))
879 : {
880 : /* details->amount > rii->details.amount: wire transfer was larger than it should have been */
881 : struct TALER_Amount delta;
882 :
883 1 : TALER_ARL_amount_subtract (&delta,
884 : &credit_details->amount,
885 : &rii->credit_details.amount);
886 1 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_in_plus),
887 : &TALER_ARL_USE_AB (total_bad_amount_in_plus),
888 : &delta);
889 : }
890 : else
891 : {
892 : /* rii->details.amount < details->amount: wire transfer was smaller than it should have been */
893 : struct TALER_Amount delta;
894 :
895 4 : TALER_ARL_amount_subtract (&delta,
896 : &rii->credit_details.amount,
897 : &credit_details->amount);
898 4 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_in_minus),
899 : &TALER_ARL_USE_AB (total_bad_amount_in_minus),
900 : &delta);
901 : }
902 : }
903 :
904 : {
905 : struct TALER_NormalizedPayto np;
906 : struct TALER_NormalizedPayto np2;
907 :
908 67 : np = TALER_payto_normalize (credit_details->debit_account_uri);
909 67 : np2 = TALER_payto_normalize (rii->credit_details.debit_account_uri);
910 67 : if (0 != TALER_normalized_payto_cmp (np,
911 : np2))
912 : {
913 1 : struct TALER_AUDITORDB_MisattributionInInconsistency mii = {
914 : .reserve_pub = rii->credit_details.details.reserve.reserve_pub,
915 : .amount = rii->credit_details.amount,
916 1 : .bank_row = credit_details->serial_id
917 : };
918 : enum GNUNET_DB_QueryStatus qs;
919 :
920 1 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
921 : "Origin bank account differs\n");
922 1 : qs = TALER_ARL_adb->insert_misattribution_in_inconsistency (
923 1 : TALER_ARL_adb->cls,
924 : &mii);
925 1 : if (qs <= 0)
926 : {
927 0 : global_qs = qs;
928 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
929 0 : GNUNET_free (np.normalized_payto);
930 0 : GNUNET_free (np2.normalized_payto);
931 0 : return false;
932 : }
933 1 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_misattribution_in),
934 : &TALER_ARL_USE_AB (total_misattribution_in),
935 : &rii->credit_details.amount);
936 : }
937 67 : GNUNET_free (np.normalized_payto);
938 67 : GNUNET_free (np2.normalized_payto);
939 : }
940 67 : if (GNUNET_TIME_timestamp_cmp (credit_details->execution_date,
941 : !=,
942 : rii->credit_details.execution_date))
943 : {
944 4 : struct TALER_AUDITORDB_RowMinorInconsistencies rmi = {
945 4 : .problem_row = rii->rowid,
946 : .diagnostic = (char *) "execution date mismatch",
947 : .row_table = (char *) "reserves_in"
948 : };
949 : enum GNUNET_DB_QueryStatus qs;
950 :
951 4 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
952 : "Execution date differs\n");
953 4 : qs = TALER_ARL_adb->insert_row_minor_inconsistencies (
954 4 : TALER_ARL_adb->cls,
955 : &rmi);
956 :
957 4 : if (qs < 0)
958 : {
959 0 : global_qs = qs;
960 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
961 0 : return false;
962 : }
963 : }
964 67 : GNUNET_assert (GNUNET_OK ==
965 : free_rii (NULL,
966 : &key,
967 : rii));
968 67 : return true;
969 : }
970 :
971 :
972 : /**
973 : * This function is called for all transactions that
974 : * are credited to the exchange's account (incoming
975 : * transactions).
976 : *
977 : * @param cls `struct WireAccount` we are processing
978 : * @param chr HTTP response returned by the bank
979 : */
980 : static void
981 68 : history_credit_cb (void *cls,
982 : const struct TALER_BANK_CreditHistoryResponse *chr)
983 : {
984 68 : struct WireAccount *wa = cls;
985 :
986 68 : wa->chh = NULL;
987 68 : switch (chr->http_status)
988 : {
989 34 : case MHD_HTTP_OK:
990 102 : for (unsigned int i = 0; i < chr->details.ok.details_length; i++)
991 : {
992 68 : const struct TALER_BANK_CreditDetails *cd
993 68 : = &chr->details.ok.details[i];
994 :
995 68 : if (! analyze_credit (wa,
996 : cd))
997 : {
998 0 : switch (global_qs)
999 : {
1000 0 : case GNUNET_DB_STATUS_SOFT_ERROR:
1001 0 : rollback_and_reset ();
1002 0 : return;
1003 0 : case GNUNET_DB_STATUS_HARD_ERROR:
1004 0 : GNUNET_SCHEDULER_shutdown ();
1005 0 : return;
1006 0 : case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
1007 : /* perfectly fine */
1008 0 : break;
1009 0 : case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
1010 : /* perfectly fine */
1011 0 : break;
1012 : }
1013 0 : break;
1014 : }
1015 : }
1016 34 : conclude_account (wa);
1017 34 : return;
1018 34 : case MHD_HTTP_NO_CONTENT:
1019 34 : conclude_account (wa);
1020 34 : return;
1021 0 : case MHD_HTTP_NOT_FOUND:
1022 0 : if (ignore_account_404)
1023 : {
1024 0 : conclude_account (wa);
1025 0 : return;
1026 : }
1027 0 : break;
1028 0 : default:
1029 0 : break;
1030 : }
1031 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1032 : "Error fetching credit history of account %s: %u (%s)\n",
1033 : wa->ai->section_name,
1034 : chr->http_status,
1035 : TALER_ErrorCode_get_hint (chr->ec));
1036 0 : commit (GNUNET_DB_STATUS_HARD_ERROR);
1037 0 : global_ret = EXIT_FAILURE;
1038 0 : GNUNET_SCHEDULER_shutdown ();
1039 : }
1040 :
1041 :
1042 : /* ***************************** Setup logic ************************ */
1043 :
1044 :
1045 : /**
1046 : * Start processing the next wire account.
1047 : * Shuts down if we are done.
1048 : *
1049 : * @param cls `struct WireAccount` with a wire account list to process
1050 : */
1051 : static void
1052 136 : process_credits (void *cls)
1053 : {
1054 136 : struct WireAccount *wa = cls;
1055 : enum GNUNET_DB_QueryStatus qs;
1056 :
1057 : /* skip accounts where CREDIT is not enabled */
1058 136 : while ( (NULL != wa) &&
1059 68 : (GNUNET_NO == wa->ai->credit_enabled) )
1060 0 : wa = wa->next;
1061 136 : if (NULL == wa)
1062 : {
1063 : /* done with all accounts, conclude check */
1064 68 : conclude_credit_history ();
1065 68 : return;
1066 : }
1067 68 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1068 : "Analyzing exchange's wire IN table for account `%s'\n",
1069 : wa->ai->section_name);
1070 68 : qs = TALER_ARL_edb->select_reserves_in_above_serial_id_by_account (
1071 68 : TALER_ARL_edb->cls,
1072 68 : wa->ai->section_name,
1073 : wa->last_reserve_in_serial_id,
1074 : &reserve_in_cb,
1075 : wa);
1076 68 : if (0 > qs)
1077 : {
1078 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1079 0 : global_ret = EXIT_FAILURE;
1080 0 : GNUNET_SCHEDULER_shutdown ();
1081 0 : return;
1082 : }
1083 :
1084 68 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1085 : "Starting bank CREDIT history of account `%s'\n",
1086 : wa->ai->section_name);
1087 68 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1088 : "user `%s'\n",
1089 : wa->ai->auth->details.basic.username);
1090 68 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1091 : "pass `%s'\n",
1092 : wa->ai->auth->details.basic.password);
1093 68 : GNUNET_assert (NULL == wa->chh);
1094 136 : wa->chh = TALER_BANK_credit_history (ctx,
1095 68 : wa->ai->auth,
1096 : wa->wire_off_in,
1097 : MAX_PER_TRANSACTION,
1098 68 : GNUNET_TIME_UNIT_ZERO,
1099 : &history_credit_cb,
1100 : wa);
1101 68 : if (NULL == wa->chh)
1102 : {
1103 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1104 : "Failed to obtain bank transaction history\n");
1105 0 : commit (GNUNET_DB_STATUS_HARD_ERROR);
1106 0 : global_ret = EXIT_FAILURE;
1107 0 : GNUNET_SCHEDULER_shutdown ();
1108 0 : return;
1109 : }
1110 : }
1111 :
1112 :
1113 : /**
1114 : * Begin audit of CREDITs to the exchange.
1115 : */
1116 : static void
1117 68 : begin_credit_audit (void)
1118 : {
1119 68 : GNUNET_assert (NULL == in_map);
1120 68 : in_map = GNUNET_CONTAINER_multihashmap_create (1024,
1121 : GNUNET_YES);
1122 : /* now go over all bank accounts and check delta with in_map */
1123 68 : process_credits (wa_head);
1124 68 : }
1125 :
1126 :
1127 : static enum GNUNET_DB_QueryStatus
1128 68 : begin_transaction (void)
1129 : {
1130 : enum GNUNET_DB_QueryStatus qs;
1131 :
1132 68 : if (GNUNET_SYSERR ==
1133 68 : TALER_ARL_edb->preflight (TALER_ARL_edb->cls))
1134 : {
1135 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1136 : "Failed to initialize exchange database connection.\n");
1137 0 : return GNUNET_DB_STATUS_HARD_ERROR;
1138 : }
1139 68 : if (GNUNET_SYSERR ==
1140 68 : TALER_ARL_adb->preflight (TALER_ARL_adb->cls))
1141 : {
1142 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1143 : "Failed to initialize auditor database session.\n");
1144 0 : return GNUNET_DB_STATUS_HARD_ERROR;
1145 : }
1146 68 : global_qs = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
1147 68 : if (GNUNET_OK !=
1148 68 : TALER_ARL_adb->start (TALER_ARL_adb->cls))
1149 : {
1150 0 : GNUNET_break (0);
1151 0 : return GNUNET_DB_STATUS_HARD_ERROR;
1152 : }
1153 68 : if (GNUNET_OK !=
1154 68 : TALER_ARL_edb->start_read_only (TALER_ARL_edb->cls,
1155 : "wire credit auditor"))
1156 : {
1157 0 : GNUNET_break (0);
1158 0 : return GNUNET_DB_STATUS_HARD_ERROR;
1159 : }
1160 68 : qs = TALER_ARL_adb->get_balance (
1161 68 : TALER_ARL_adb->cls,
1162 : TALER_ARL_GET_AB (total_wire_in),
1163 : TALER_ARL_GET_AB (total_kycauth_in),
1164 : TALER_ARL_GET_AB (total_wire_credit_fees),
1165 : TALER_ARL_GET_AB (total_bad_amount_in_plus),
1166 : TALER_ARL_GET_AB (total_bad_amount_in_minus),
1167 : TALER_ARL_GET_AB (total_misattribution_in),
1168 : NULL);
1169 68 : switch (qs)
1170 : {
1171 0 : case GNUNET_DB_STATUS_HARD_ERROR:
1172 0 : GNUNET_break (0);
1173 0 : return qs;
1174 0 : case GNUNET_DB_STATUS_SOFT_ERROR:
1175 0 : GNUNET_break (0);
1176 0 : return qs;
1177 0 : case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
1178 : case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
1179 0 : break;
1180 : }
1181 68 : for (struct WireAccount *wa = wa_head;
1182 136 : NULL != wa;
1183 68 : wa = wa->next)
1184 : {
1185 68 : GNUNET_asprintf (&wa->label_reserve_in_serial_id,
1186 : "wire-%s-%s",
1187 68 : wa->ai->section_name,
1188 : "reserve_in_serial_id");
1189 68 : GNUNET_asprintf (&wa->label_wire_off_in,
1190 : "wire-%s-%s",
1191 68 : wa->ai->section_name,
1192 : "wire_off_in");
1193 68 : qs = TALER_ARL_adb->get_auditor_progress (
1194 68 : TALER_ARL_adb->cls,
1195 68 : wa->label_reserve_in_serial_id,
1196 : &wa->last_reserve_in_serial_id,
1197 : wa->label_wire_off_in,
1198 : &wa->wire_off_in,
1199 : NULL);
1200 68 : if (0 > qs)
1201 : {
1202 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1203 0 : return qs;
1204 : }
1205 68 : wa->start_reserve_in_serial_id = wa->last_reserve_in_serial_id;
1206 68 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1207 : "Starting from reserve_in at %s=%llu for account `%s'\n",
1208 : wa->label_reserve_in_serial_id,
1209 : (unsigned long long) wa->start_reserve_in_serial_id,
1210 : wa->ai->section_name);
1211 : }
1212 :
1213 68 : begin_credit_audit ();
1214 68 : return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
1215 : }
1216 :
1217 :
1218 : /**
1219 : * Function called with information about a wire account. Adds the
1220 : * account to our list for processing (if it is enabled and we can
1221 : * load the plugin).
1222 : *
1223 : * @param cls closure, NULL
1224 : * @param ai account information
1225 : */
1226 : static void
1227 68 : process_account_cb (void *cls,
1228 : const struct TALER_EXCHANGEDB_AccountInfo *ai)
1229 : {
1230 : struct WireAccount *wa;
1231 :
1232 : (void) cls;
1233 68 : if ((! ai->debit_enabled) &&
1234 0 : (! ai->credit_enabled))
1235 0 : return; /* not an active exchange account */
1236 68 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1237 : "Found exchange account `%s'\n",
1238 : ai->section_name);
1239 68 : wa = GNUNET_new (struct WireAccount);
1240 68 : wa->ai = ai;
1241 68 : GNUNET_CONTAINER_DLL_insert (wa_head,
1242 : wa_tail,
1243 : wa);
1244 : }
1245 :
1246 :
1247 : /**
1248 : * Function called on events received from Postgres.
1249 : *
1250 : * @param cls closure, NULL
1251 : * @param extra additional event data provided
1252 : * @param extra_size number of bytes in @a extra
1253 : */
1254 : static void
1255 0 : db_notify (void *cls,
1256 : const void *extra,
1257 : size_t extra_size)
1258 : {
1259 : (void) cls;
1260 : (void) extra;
1261 : (void) extra_size;
1262 :
1263 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1264 : "Received notification to wake wire helper\n");
1265 : /* If there are accounts we are still processing, abort
1266 : the HTTP requests so we can start afresh. */
1267 0 : for (struct WireAccount *wa = wa_head;
1268 0 : NULL != wa;
1269 0 : wa = wa->next)
1270 : {
1271 0 : if (NULL != wa->chh)
1272 : {
1273 0 : TALER_BANK_credit_history_cancel (wa->chh);
1274 0 : wa->chh = NULL;
1275 : }
1276 0 : conclude_account (wa);
1277 : }
1278 :
1279 0 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
1280 0 : begin_transaction ())
1281 : {
1282 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1283 : "Audit failed\n");
1284 0 : GNUNET_break (0);
1285 0 : global_ret = EXIT_FAILURE;
1286 0 : GNUNET_SCHEDULER_shutdown ();
1287 : }
1288 0 : }
1289 :
1290 :
1291 : /**
1292 : * Main function that will be run.
1293 : *
1294 : * @param cls closure
1295 : * @param args remaining command-line arguments
1296 : * @param cfgfile name of the configuration file used (for saving, can be NULL!)
1297 : * @param c configuration
1298 : */
1299 : static void
1300 68 : run (void *cls,
1301 : char *const *args,
1302 : const char *cfgfile,
1303 : const struct GNUNET_CONFIGURATION_Handle *c)
1304 : {
1305 : (void) cls;
1306 : (void) args;
1307 : (void) cfgfile;
1308 68 : cfg = c;
1309 68 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1310 : "Launching wire-credit auditor\n");
1311 68 : if (GNUNET_OK !=
1312 68 : TALER_ARL_init (c))
1313 : {
1314 0 : global_ret = EXIT_FAILURE;
1315 0 : return;
1316 : }
1317 68 : if (GNUNET_OK !=
1318 68 : TALER_config_get_amount (TALER_ARL_cfg,
1319 : "auditor",
1320 : "TINY_AMOUNT",
1321 : &tiny_amount))
1322 : {
1323 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1324 : "auditor",
1325 : "TINY_AMOUNT");
1326 0 : global_ret = EXIT_NOTCONFIGURED;
1327 0 : return;
1328 : }
1329 68 : GNUNET_assert (GNUNET_OK ==
1330 : TALER_amount_set_zero (TALER_ARL_currency,
1331 : &zero));
1332 68 : GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
1333 : NULL);
1334 68 : ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
1335 : &rc);
1336 68 : rc = GNUNET_CURL_gnunet_rc_create (ctx);
1337 68 : if (NULL == ctx)
1338 : {
1339 0 : GNUNET_break (0);
1340 0 : global_ret = EXIT_FAILURE;
1341 0 : return;
1342 : }
1343 68 : if (GNUNET_OK !=
1344 68 : TALER_EXCHANGEDB_load_accounts (TALER_ARL_cfg,
1345 : TALER_EXCHANGEDB_ALO_CREDIT
1346 : | TALER_EXCHANGEDB_ALO_AUTHDATA))
1347 : {
1348 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1349 : "No bank accounts configured\n");
1350 0 : global_ret = EXIT_NOTCONFIGURED;
1351 0 : GNUNET_SCHEDULER_shutdown ();
1352 0 : return;
1353 : }
1354 68 : TALER_EXCHANGEDB_find_accounts (&process_account_cb,
1355 : NULL);
1356 :
1357 68 : if (0 == test_mode)
1358 : {
1359 0 : struct GNUNET_DB_EventHeaderP es = {
1360 0 : .size = htons (sizeof (es)),
1361 0 : .type = htons (TALER_DBEVENT_EXCHANGE_AUDITOR_WAKE_HELPER_WIRE)
1362 : };
1363 :
1364 0 : eh = TALER_ARL_adb->event_listen (TALER_ARL_adb->cls,
1365 : &es,
1366 0 : GNUNET_TIME_UNIT_FOREVER_REL,
1367 : &db_notify,
1368 : NULL);
1369 0 : GNUNET_assert (NULL != eh);
1370 : }
1371 68 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
1372 68 : begin_transaction ())
1373 : {
1374 0 : GNUNET_break (0);
1375 0 : global_ret = EXIT_FAILURE;
1376 0 : GNUNET_SCHEDULER_shutdown ();
1377 0 : return;
1378 : }
1379 : }
1380 :
1381 :
1382 : /**
1383 : * The main function of the wire auditing tool. Checks that
1384 : * the exchange's records of wire transfers match that of
1385 : * the wire gateway.
1386 : *
1387 : * @param argc number of arguments from the command line
1388 : * @param argv command line arguments
1389 : * @return 0 ok, 1 on error
1390 : */
1391 : int
1392 68 : main (int argc,
1393 : char *const *argv)
1394 : {
1395 68 : const struct GNUNET_GETOPT_CommandLineOption options[] = {
1396 68 : GNUNET_GETOPT_option_flag ('i',
1397 : "internal",
1398 : "perform checks only applicable for exchange-internal audits",
1399 : &internal_checks),
1400 68 : GNUNET_GETOPT_option_flag ('I',
1401 : "ignore-not-found",
1402 : "continue, even if the bank account of the exchange was not found",
1403 : &ignore_account_404),
1404 68 : GNUNET_GETOPT_option_flag ('t',
1405 : "test",
1406 : "run in test mode and exit when idle",
1407 : &test_mode),
1408 68 : GNUNET_GETOPT_option_timetravel ('T',
1409 : "timetravel"),
1410 : GNUNET_GETOPT_OPTION_END
1411 : };
1412 : enum GNUNET_GenericReturnValue ret;
1413 :
1414 68 : ret = GNUNET_PROGRAM_run (
1415 : TALER_AUDITOR_project_data (),
1416 : argc,
1417 : argv,
1418 : "taler-helper-auditor-wire-credit",
1419 : gettext_noop (
1420 : "Audit exchange database for consistency with the bank's wire transfers"),
1421 : options,
1422 : &run,
1423 : NULL);
1424 68 : if (GNUNET_SYSERR == ret)
1425 0 : return EXIT_INVALIDARGUMENT;
1426 68 : if (GNUNET_NO == ret)
1427 0 : return EXIT_SUCCESS;
1428 68 : return global_ret;
1429 : }
1430 :
1431 :
1432 : /* end of taler-helper-auditor-wire-credit.c */
|