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-transfer.c
18 : * @brief audits that deposits past due date are
19 : * aggregated and have a matching wire transfer
20 : * database.
21 : *
22 : * Three checks, each with its own cursor over the exchange's tables:
23 : *
24 : * 1) every batch deposit must eventually be aggregated (`total_amount_lag`),
25 : * 2) nothing may be aggregated before there is a deposit to justify it
26 : * (`total_early_aggregation`),
27 : * 3) every aggregation must eventually turn into a wire transfer.
28 : *
29 : * The third is what keeps "the exchange paid the merchant" apart from "the
30 : * exchange decided what it owes and then sat on the money": an
31 : * `aggregation_tracking` row is not proof of payment, a `wire_out` row is.
32 : * What is still owed is split by the reason the exchange gave for not paying
33 : * -- `total_aml_hold` for an open KYC requirement, `total_small_aggregate` for
34 : * an aggregate that does not yet cover its wire fee, and `total_transfer_lag`
35 : * for a transfer the exchange never gave any reason for -- and each held
36 : * transfer is listed in `auditor_aml_holds`.
37 : *
38 : * @author Christian Grothoff
39 : */
40 : #include "platform.h"
41 : #include <gnunet/gnunet_util_lib.h>
42 : #include <gnunet/gnunet_curl_lib.h>
43 : #include "auditordb_lib.h"
44 : #include "exchangedb_lib.h"
45 : #include "taler/taler_json_lib.h"
46 : #include "report-lib.h"
47 : #include "taler/taler_dbevents.h"
48 : #include "auditor-database/delete_aml_hold.h"
49 : #include "auditor-database/delete_early_aggregation.h"
50 : #include "auditor-database/delete_pending_deposit.h"
51 : #include "auditor-database/event_listen.h"
52 : #include "auditor-database/get_auditor_progress.h"
53 : #include "auditor-database/get_balance.h"
54 : #include "auditor-database/insert_aml_hold.h"
55 : #include "auditor-database/insert_amount_arithmetic_inconsistency.h"
56 : #include "auditor-database/insert_auditor_progress.h"
57 : #include "auditor-database/insert_balance.h"
58 : #include "auditor-database/insert_early_aggregation.h"
59 : #include "auditor-database/insert_pending_deposit.h"
60 : #include "auditor-database/insert_row_inconsistency.h"
61 : #include "auditor-database/preflight.h"
62 : #include "auditor-database/start.h"
63 : #include "auditor-database/update_aml_hold.h"
64 : #include "exchange-database/get_aggregation_deferral_by_wtid.h"
65 : #include "exchange-database/get_aggregation_transient_by_wtid.h"
66 : #include "exchange-database/get_open_legitimization_measure.h"
67 : #include "exchange-database/get_pending_aggregation.h"
68 : #include "exchange-database/preflight.h"
69 : #include "exchange-database/rollback.h"
70 : struct AggregationContext;
71 : #define TALER_EXCHANGEDB_AGGREGATION_RESULT_CLOSURE struct AggregationContext
72 : #include "exchange-database/iterate_aggregations_above_serial_id.h"
73 : struct ImportMissingWireContext;
74 : #define TALER_EXCHANGEDB_WIRE_MISSING_RESULT_CLOSURE struct \
75 : ImportMissingWireContext
76 : #include "exchange-database/iterate_batch_deposits_missing_wire.h"
77 : struct HoldContext;
78 : #define TALER_EXCHANGEDB_AGGREGATION_WTID_RESULT_CLOSURE struct HoldContext
79 : #include "exchange-database/iterate_aggregation_wtids_above_serial_id.h"
80 : #define TALER_AUDITORDB_AML_HOLD_RESULT_CLOSURE struct HoldContext
81 : #include "auditor-database/iterate_aml_holds.h"
82 : #include "exchange-database/start_read_only.h"
83 :
84 :
85 : /**
86 : * Run in test mode. Exit when idle instead of
87 : * going to sleep and waiting for more work.
88 : */
89 : static int test_mode;
90 :
91 : /**
92 : * Return value from main().
93 : */
94 : static int global_ret;
95 :
96 : /**
97 : * Last reserve_out / wire_out serial IDs seen.
98 : */
99 : static TALER_ARL_DEF_PP (wire_batch_deposit_id);
100 : static TALER_ARL_DEF_PP (wire_aggregation_id);
101 :
102 : /**
103 : * Row of `aggregation_tracking` up to which we have looked for wire transfers
104 : * the exchange aggregated but did not execute.
105 : */
106 : static TALER_ARL_DEF_PP (wire_hold_aggregation_id);
107 :
108 : /**
109 : * Total amount which the exchange did not aggregate/transfer in time.
110 : */
111 : static TALER_ARL_DEF_AB (total_amount_lag);
112 :
113 : /**
114 : * Total amount which the exchange did aggregate/transfer too early.
115 : */
116 : static TALER_ARL_DEF_AB (total_early_aggregation);
117 :
118 : /**
119 : * Total amount the exchange aggregated but did not wire because it says a
120 : * legitimization requirement against the recipient is still open. Money the
121 : * exchange is withholding for legal reasons.
122 : */
123 : static TALER_ARL_DEF_AB (total_aml_hold);
124 :
125 : /**
126 : * Total amount the exchange aggregated but did not wire because what it has
127 : * collected for the account so far does not cover the wire fee. Money that is
128 : * waiting for the next deposit rather than for anyone's decision, and thus not
129 : * an irregularity at all -- it is kept apart so that it does not inflate the
130 : * two balances that are.
131 : */
132 : static TALER_ARL_DEF_AB (total_small_aggregate);
133 :
134 : /**
135 : * Total amount the exchange aggregated but did not wire and gave no reason
136 : * for. Money the exchange simply has not paid out.
137 : */
138 : static TALER_ARL_DEF_AB (total_transfer_lag);
139 :
140 : /**
141 : * Should we run checks that only work for exchange-internal audits?
142 : */
143 : static int internal_checks;
144 :
145 : /**
146 : * Database event handler to wake us up again.
147 : */
148 : static struct GNUNET_DB_EventHandler *eh;
149 :
150 : /**
151 : * The auditors's configuration.
152 : */
153 : static const struct GNUNET_CONFIGURATION_Handle *cfg;
154 :
155 :
156 : /**
157 : * Task run on shutdown.
158 : *
159 : * @param cls NULL
160 : */
161 : static void
162 6 : do_shutdown (void *cls)
163 : {
164 : (void) cls;
165 6 : if (NULL != eh)
166 : {
167 0 : TALER_AUDITORDB_event_listen_cancel (eh);
168 0 : eh = NULL;
169 : }
170 6 : TALER_ARL_done ();
171 6 : TALER_EXCHANGEDB_unload_accounts ();
172 6 : TALER_ARL_cfg = NULL;
173 6 : }
174 :
175 :
176 : /**
177 : * Closure for import_wire_missing_cb().
178 : */
179 : struct ImportMissingWireContext
180 : {
181 : /**
182 : * Set to maximum row ID encountered.
183 : */
184 : uint64_t max_batch_deposit_uuid;
185 :
186 : /**
187 : * Set to database errors in callback.
188 : */
189 : enum GNUNET_DB_QueryStatus err;
190 : };
191 :
192 :
193 : /**
194 : * Function called on deposits that need to be checked for their
195 : * wire transfer.
196 : *
197 : * @param wc closure, points to a `struct ImportMissingWireContext`
198 : * @param batch_deposit_serial_id serial of the entry in the batch deposits table
199 : * @param total_amount value of the missing deposits, including fee
200 : * @param wire_target_h_payto where should the funds be wired
201 : * @param deadline what was the earliest requested wire transfer deadline
202 : */
203 : static void
204 34 : import_wire_missing_cb (
205 : struct ImportMissingWireContext *wc,
206 : uint64_t batch_deposit_serial_id,
207 : const struct TALER_Amount *total_amount,
208 : const struct TALER_FullPaytoHashP *wire_target_h_payto,
209 : struct GNUNET_TIME_Timestamp deadline)
210 : {
211 : enum GNUNET_DB_QueryStatus qs;
212 :
213 34 : if (wc->err < 0)
214 0 : return; /* already failed */
215 34 : GNUNET_assert (batch_deposit_serial_id >= wc->max_batch_deposit_uuid);
216 34 : wc->max_batch_deposit_uuid = batch_deposit_serial_id + 1;
217 34 : qs = TALER_AUDITORDB_delete_early_aggregation (
218 : TALER_ARL_adb,
219 : batch_deposit_serial_id);
220 34 : switch (qs)
221 : {
222 0 : case GNUNET_DB_STATUS_SOFT_ERROR:
223 0 : GNUNET_break (0);
224 0 : wc->err = qs;
225 0 : return;
226 0 : case GNUNET_DB_STATUS_HARD_ERROR:
227 0 : GNUNET_break (0);
228 0 : wc->err = qs;
229 0 : return;
230 34 : case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
231 34 : qs = TALER_AUDITORDB_insert_pending_deposit (
232 : TALER_ARL_adb,
233 : batch_deposit_serial_id,
234 : wire_target_h_payto,
235 : total_amount,
236 : deadline);
237 34 : if (0 > qs)
238 : {
239 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
240 0 : wc->err = qs;
241 0 : return;
242 : }
243 34 : if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
244 34 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_amount_lag),
245 : &TALER_ARL_USE_AB (total_amount_lag),
246 : total_amount);
247 34 : break;
248 0 : case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
249 0 : TALER_ARL_amount_subtract (&TALER_ARL_USE_AB (total_early_aggregation),
250 : &TALER_ARL_USE_AB (total_early_aggregation),
251 : total_amount);
252 0 : break;
253 0 : default:
254 0 : GNUNET_assert (0);
255 : }
256 : }
257 :
258 :
259 : /**
260 : * Checks for wire transfers that should have happened.
261 : *
262 : * @return transaction status
263 : */
264 : static enum GNUNET_DB_QueryStatus
265 6 : check_for_required_transfers (void)
266 : {
267 : enum GNUNET_DB_QueryStatus qs;
268 6 : struct ImportMissingWireContext wc = {
269 : .max_batch_deposit_uuid = TALER_ARL_USE_PP (wire_batch_deposit_id),
270 : .err = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT
271 : };
272 :
273 6 : qs = TALER_EXCHANGEDB_iterate_batch_deposits_missing_wire (
274 : TALER_ARL_edb,
275 : TALER_ARL_USE_PP (wire_batch_deposit_id),
276 : &import_wire_missing_cb,
277 : &wc);
278 6 : if (0 > qs)
279 : {
280 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
281 0 : return qs;
282 : }
283 6 : if (0 > wc.err)
284 : {
285 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == wc.err);
286 0 : return wc.err;
287 : }
288 6 : TALER_ARL_USE_PP (wire_batch_deposit_id) = wc.max_batch_deposit_uuid;
289 6 : return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
290 : }
291 :
292 :
293 : /**
294 : * Closure for #clear_finished_transfer_cb().
295 : */
296 : struct AggregationContext
297 : {
298 : /**
299 : * Set to maximum row ID encountered.
300 : */
301 : uint64_t max_aggregation_serial;
302 :
303 : /**
304 : * Set to database errors in callback.
305 : */
306 : enum GNUNET_DB_QueryStatus err;
307 : };
308 :
309 :
310 : /**
311 : * Function called on aggregations that were done for
312 : * a (batch) deposit.
313 : *
314 : * @param ac closure
315 : * @param amount affected amount
316 : * @param tracking_serial_id where in the table are we
317 : * @param batch_deposit_serial_id which batch deposit was aggregated
318 : */
319 : static void
320 28 : clear_finished_transfer_cb (
321 : struct AggregationContext *ac,
322 : const struct TALER_Amount *amount,
323 : uint64_t tracking_serial_id,
324 : uint64_t batch_deposit_serial_id)
325 : {
326 : enum GNUNET_DB_QueryStatus qs;
327 :
328 28 : if (0 > ac->err)
329 0 : return; /* already failed */
330 28 : GNUNET_assert (ac->max_aggregation_serial <= tracking_serial_id);
331 28 : ac->max_aggregation_serial = tracking_serial_id + 1;
332 28 : qs = TALER_AUDITORDB_delete_pending_deposit (
333 : TALER_ARL_adb,
334 : batch_deposit_serial_id);
335 28 : switch (qs)
336 : {
337 0 : case GNUNET_DB_STATUS_SOFT_ERROR:
338 0 : GNUNET_break (0);
339 0 : ac->err = qs;
340 0 : return;
341 0 : case GNUNET_DB_STATUS_HARD_ERROR:
342 0 : GNUNET_break (0);
343 0 : ac->err = qs;
344 0 : return;
345 0 : case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
346 0 : qs = TALER_AUDITORDB_insert_early_aggregation (
347 : TALER_ARL_adb,
348 : batch_deposit_serial_id,
349 : tracking_serial_id,
350 : amount);
351 0 : if (0 > qs)
352 : {
353 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
354 0 : ac->err = qs;
355 0 : return;
356 : }
357 0 : if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
358 0 : TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_early_aggregation),
359 : &TALER_ARL_USE_AB (total_early_aggregation),
360 : amount);
361 0 : break;
362 28 : case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
363 28 : TALER_ARL_amount_subtract (&TALER_ARL_USE_AB (total_amount_lag),
364 : &TALER_ARL_USE_AB (total_amount_lag),
365 : amount);
366 28 : break;
367 0 : default:
368 0 : GNUNET_assert (0);
369 : }
370 : }
371 :
372 :
373 : /**
374 : * Checks that all wire transfers that should have happened
375 : * (based on deposits) have indeed happened.
376 : *
377 : * @return transaction status
378 : */
379 : static enum GNUNET_DB_QueryStatus
380 6 : check_for_completed_transfers (void)
381 : {
382 6 : struct AggregationContext ac = {
383 : .max_aggregation_serial = TALER_ARL_USE_PP (wire_aggregation_id),
384 : .err = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT
385 : };
386 : enum GNUNET_DB_QueryStatus qs;
387 :
388 6 : qs = TALER_EXCHANGEDB_iterate_aggregations_above_serial_id (
389 : TALER_ARL_edb,
390 : TALER_ARL_USE_PP (wire_aggregation_id),
391 : &clear_finished_transfer_cb,
392 : &ac);
393 6 : if (0 > qs)
394 : {
395 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
396 0 : return qs;
397 : }
398 6 : if (0 > ac.err)
399 : {
400 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == ac.err);
401 0 : return ac.err;
402 : }
403 6 : TALER_ARL_USE_PP (wire_aggregation_id) = ac.max_aggregation_serial;
404 6 : return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
405 : }
406 :
407 :
408 : /**
409 : * Closure for #note_held_transfer_cb() and #evaluate_hold_cb().
410 : */
411 : struct HoldContext
412 : {
413 : /**
414 : * Wire transfers already evaluated in this round, so that the three
415 : * `aggregation_tracking` rows of one transfer cost one evaluation, and so
416 : * that the pass over the holds already on file does not redo them.
417 : */
418 : struct GNUNET_CONTAINER_MultiHashMap *seen;
419 :
420 : /**
421 : * Sum of the holds the exchange attributes to an open KYC requirement.
422 : */
423 : struct TALER_Amount aml_hold;
424 :
425 : /**
426 : * Sum of the holds the exchange attributes to the aggregate being too small
427 : * to be worth its wire fee.
428 : */
429 : struct TALER_Amount small_aggregate;
430 :
431 : /**
432 : * Sum of the holds the exchange gave no reason for.
433 : */
434 : struct TALER_Amount transfer_lag;
435 :
436 : /**
437 : * Set to maximum row ID encountered.
438 : */
439 : uint64_t max_aggregation_serial;
440 :
441 : /**
442 : * Set to database errors in the callback.
443 : */
444 : enum GNUNET_DB_QueryStatus err;
445 : };
446 :
447 :
448 : /**
449 : * Report a row inconsistency.
450 : *
451 : * @param[in,out] hc our state, to flag database failures in
452 : * @param table name of the exchange table the bad row is in
453 : * @param rowid row that is bad, 0 if the finding is not about one row
454 : * @param diagnostic what is wrong with it
455 : */
456 : static void
457 0 : report_row (struct HoldContext *hc,
458 : const char *table,
459 : uint64_t rowid,
460 : const char *diagnostic)
461 : {
462 0 : struct TALER_AUDITORDB_RowInconsistency ri = {
463 : .problem_row_id = rowid,
464 : .row_table = (char *) table,
465 : .diagnostic = (char *) diagnostic
466 : };
467 : enum GNUNET_DB_QueryStatus qs;
468 :
469 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
470 : "Row %llu of `%s' is bad: %s\n",
471 : (unsigned long long) rowid,
472 : table,
473 : diagnostic);
474 0 : qs = TALER_AUDITORDB_insert_row_inconsistency (TALER_ARL_adb,
475 : &ri);
476 0 : if (0 > qs)
477 : {
478 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
479 0 : hc->err = qs;
480 : }
481 0 : }
482 :
483 :
484 : /**
485 : * Work out whether the exchange still owes the wire transfer @a wtid, how much
486 : * it owes and what it says is keeping it from paying; then record the result
487 : * and count it towards the running totals.
488 : *
489 : * The classification is the exchange's own, read out of `aggregation_deferrals`
490 : * -- append-only, and therefore the one statement of its reasons that survives
491 : * replication to an external auditor. Taking it at face value is deliberate:
492 : * whether a hold is *justified* is a question for the people reading
493 : * `/monitoring/aml-holds`, not for the auditor. What the auditor does check is
494 : * that the claim is consistent with everything else it can see: the amount
495 : * against its own recomputation, a KYC claim against the legitimization tables,
496 : * and under `-i` the whole thing against the live `aggregation_transient`. A
497 : * disagreement is reported rather than resolved in either side's favour.
498 : *
499 : * A transfer with no `aggregation_deferrals` row at all is a transfer the
500 : * exchange decided on, did not make, and never explained; that is
501 : * `total_transfer_lag`.
502 : *
503 : * @param[in,out] hc our state
504 : * @param wtid wire transfer to examine
505 : */
506 : static void
507 0 : evaluate_hold (struct HoldContext *hc,
508 : const struct TALER_WireTransferIdentifierRawP *wtid)
509 : {
510 : struct TALER_FullPaytoHashP h_payto;
511 0 : struct TALER_FullPayto payto = { NULL };
512 : struct TALER_NormalizedPaytoHashP h_normalized_payto;
513 : struct TALER_Amount deposited;
514 : struct TALER_Amount refunded;
515 : struct TALER_Amount deposit_fee;
516 : struct TALER_Amount deductions;
517 : struct TALER_Amount amount;
518 : struct TALER_Amount claimed;
519 : struct GNUNET_TIME_Timestamp deferral_time;
520 : struct GNUNET_TIME_Absolute measure_start;
521 0 : enum TALER_EXCHANGEDB_DeferralReason reason = TALER_EXCHANGEDB_DR_NONE;
522 0 : uint64_t claimed_measure = 0;
523 0 : uint64_t measure = 0;
524 : bool have_claim;
525 : bool open_measure;
526 : enum GNUNET_DB_QueryStatus qs;
527 :
528 0 : qs = TALER_EXCHANGEDB_get_pending_aggregation (TALER_ARL_edb,
529 : wtid,
530 : &h_payto,
531 : &payto,
532 : &deposited,
533 : &refunded,
534 : &deposit_fee);
535 0 : if (0 > qs)
536 : {
537 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
538 0 : hc->err = qs;
539 0 : return;
540 : }
541 0 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
542 : {
543 : /* The `wire_out' row has appeared: the money left the exchange, so the
544 : hold ends and stops counting towards either total. This is also the
545 : only place a hold is ever cleared -- there is no separate pass over
546 : `wire_out`, because a transfer that was executed simply stops being a
547 : pending aggregation. */
548 0 : qs = TALER_AUDITORDB_delete_aml_hold (TALER_ARL_adb,
549 : wtid);
550 0 : if (0 > qs)
551 : {
552 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
553 0 : hc->err = qs;
554 : }
555 0 : return;
556 : }
557 :
558 : /* What the exchange still owes, computed the way `exchange_do_aggregate'
559 : computes it, so that it is comparable with what the exchange itself
560 : recorded. */
561 0 : TALER_ARL_amount_add (&deductions,
562 : &refunded,
563 : &deposit_fee);
564 0 : if (TALER_ARL_SR_INVALID_NEGATIVE ==
565 0 : TALER_ARL_amount_subtract_neg (&amount,
566 : &deposited,
567 : &deductions))
568 : {
569 : char *diag;
570 :
571 0 : GNUNET_asprintf (&diag,
572 : "refunds and deposit fees exceed the deposits aggregated"
573 : " into wire transfer %s",
574 : TALER_B2S (wtid));
575 0 : report_row (hc,
576 : "aggregation_tracking",
577 : 0,
578 : diag);
579 0 : GNUNET_free (diag);
580 0 : GNUNET_assert (GNUNET_OK ==
581 : TALER_amount_set_zero (TALER_ARL_currency,
582 : &amount));
583 : }
584 :
585 : /* What the exchange says about it. Absent for a transfer it never
586 : explained, and for one deferred by a version of the exchange from before
587 : `aggregation_deferrals' existed. */
588 0 : qs = TALER_EXCHANGEDB_get_aggregation_deferral_by_wtid (TALER_ARL_edb,
589 : wtid,
590 : &claimed,
591 : &reason,
592 : &claimed_measure,
593 : &deferral_time);
594 0 : if (0 > qs)
595 : {
596 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
597 0 : hc->err = qs;
598 0 : GNUNET_free (payto.full_payto);
599 0 : return;
600 : }
601 0 : have_claim = (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs);
602 0 : if (! have_claim)
603 : {
604 0 : reason = TALER_EXCHANGEDB_DR_NONE;
605 0 : claimed_measure = 0;
606 : }
607 :
608 : /* `legitimization_measures' is reached through the account's *normalized*
609 : payto hash, and `wire_targets.h_normalized_payto' is not replicated, so
610 : the normalization has to happen here rather than in the query. */
611 0 : TALER_full_payto_normalize_and_hash (payto,
612 : &h_normalized_payto);
613 0 : qs = TALER_EXCHANGEDB_get_open_legitimization_measure (
614 : TALER_ARL_edb,
615 : &h_normalized_payto,
616 : &measure,
617 : &measure_start);
618 0 : if (0 > qs)
619 : {
620 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
621 0 : hc->err = qs;
622 0 : GNUNET_free (payto.full_payto);
623 0 : return;
624 : }
625 0 : open_measure = (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs);
626 0 : if (! open_measure)
627 0 : measure = 0;
628 0 : switch (reason)
629 : {
630 0 : case TALER_EXCHANGEDB_DR_NONE:
631 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
632 : "Exchange holds %s for %s and says nothing about why\n",
633 : TALER_amount2s (&amount),
634 : payto.full_payto);
635 0 : break;
636 0 : case TALER_EXCHANGEDB_DR_AMOUNT_TOO_SMALL:
637 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
638 : "Exchange holds %s for %s, too little to cover the wire fee\n",
639 : TALER_amount2s (&amount),
640 : payto.full_payto);
641 0 : break;
642 0 : case TALER_EXCHANGEDB_DR_KYC:
643 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
644 : "Exchange holds %s for %s awaiting KYC measure %llu\n",
645 : TALER_amount2s (&amount),
646 : payto.full_payto,
647 : (unsigned long long) claimed_measure);
648 0 : break;
649 : }
650 :
651 0 : if (have_claim &&
652 0 : (0 != TALER_amount_cmp (&claimed,
653 : &amount)) )
654 : {
655 0 : struct TALER_AUDITORDB_AmountArithmeticInconsistency aai = {
656 : .problem_row_id = 0,
657 : .operation = (char *) "aggregation deferral",
658 : .exchange_amount = claimed,
659 : .auditor_amount = amount,
660 : /* claiming to hold less than it owes understates the exchange's
661 : liabilities, which is the direction that favours the exchange */
662 0 : .profitable = (0 > TALER_amount_cmp (&claimed,
663 : &amount))
664 0 : };
665 :
666 0 : qs = TALER_AUDITORDB_insert_amount_arithmetic_inconsistency (
667 : TALER_ARL_adb,
668 : &aai);
669 0 : if (0 > qs)
670 : {
671 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
672 0 : hc->err = qs;
673 : }
674 : }
675 :
676 0 : if ( (TALER_EXCHANGEDB_DR_KYC == reason) &&
677 0 : (! open_measure) )
678 : {
679 : char *diag;
680 :
681 : /* The one claim the auditor can check on its own: a legitimization
682 : requirement is a row in `legitimization_measures' that no
683 : `legitimization_processes' row carried through to a decision, and that
684 : is replicated. Claiming KYC without one is how an exchange would dress
685 : up simply keeping the money. */
686 0 : GNUNET_asprintf (&diag,
687 : "exchange withholds wire transfer %s for legitimization"
688 : " measure %llu, which is not open",
689 : TALER_B2S (wtid),
690 : (unsigned long long) claimed_measure);
691 0 : report_row (hc,
692 : "aggregation_deferrals",
693 : claimed_measure,
694 : diag);
695 0 : GNUNET_free (diag);
696 : }
697 :
698 0 : if (internal_checks)
699 : {
700 : struct TALER_Amount transient;
701 : uint64_t transient_measure;
702 :
703 : /* `aggregation_transient' is the live version of what the deferral row
704 : claims: updated in place, deleted on payout, and unreplicable. On an
705 : internal audit we can hold the two against each other, which is what
706 : catches a `aggregation_deferrals' row that was written once and then
707 : left behind by an aggregate that has moved on. */
708 0 : qs = TALER_EXCHANGEDB_get_aggregation_transient_by_wtid (
709 : TALER_ARL_edb,
710 : &h_payto,
711 : wtid,
712 : &transient,
713 : &transient_measure);
714 0 : if (0 > qs)
715 : {
716 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
717 0 : hc->err = qs;
718 0 : GNUNET_free (payto.full_payto);
719 0 : return;
720 : }
721 0 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
722 : {
723 : char *diag;
724 :
725 : /* The exchange marked the deposits as aggregated, did not transfer the
726 : money, and is not tracking that it owes it either. */
727 0 : GNUNET_asprintf (&diag,
728 : "wire transfer %s was aggregated but is neither in"
729 : " wire_out nor in aggregation_transient",
730 : TALER_B2S (wtid));
731 0 : report_row (hc,
732 : "aggregation_transient",
733 : 0,
734 : diag);
735 0 : GNUNET_free (diag);
736 : }
737 : else
738 : {
739 0 : if (0 !=
740 0 : TALER_amount_cmp (&transient,
741 : &amount))
742 : {
743 0 : struct TALER_AUDITORDB_AmountArithmeticInconsistency aai = {
744 : .problem_row_id = 0,
745 : .operation = (char *) "aggregation transient",
746 : .exchange_amount = transient,
747 : .auditor_amount = amount,
748 : /* claiming to hold less than it owes understates the exchange's
749 : liabilities, which is the direction that favours the exchange */
750 0 : .profitable = (0 > TALER_amount_cmp (&transient,
751 : &amount))
752 0 : };
753 :
754 0 : qs = TALER_AUDITORDB_insert_amount_arithmetic_inconsistency (
755 : TALER_ARL_adb,
756 : &aai);
757 0 : if (0 > qs)
758 : {
759 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
760 0 : hc->err = qs;
761 : }
762 : }
763 0 : if ( (TALER_EXCHANGEDB_DR_KYC == reason) !=
764 : (0 != transient_measure) )
765 : {
766 : char *diag;
767 :
768 0 : GNUNET_asprintf (&diag,
769 : "exchange is tracking wire transfer %s as %s but its"
770 : " last aggregation_deferrals row says %s",
771 : TALER_B2S (wtid),
772 0 : (0 != transient_measure)
773 : ? "awaiting legitimization"
774 : : "not awaiting legitimization",
775 0 : (TALER_EXCHANGEDB_DR_KYC == reason)
776 : ? "it is"
777 : : "it is not");
778 0 : report_row (hc,
779 : "aggregation_transient",
780 : transient_measure,
781 : diag);
782 0 : GNUNET_free (diag);
783 : }
784 : }
785 : }
786 :
787 0 : qs = TALER_AUDITORDB_insert_aml_hold (TALER_ARL_adb,
788 : wtid,
789 : &h_payto,
790 : payto,
791 : &amount,
792 : (uint32_t) reason,
793 : claimed_measure);
794 0 : GNUNET_free (payto.full_payto);
795 0 : if (0 > qs)
796 : {
797 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
798 0 : hc->err = qs;
799 0 : return;
800 : }
801 : /* Insert-then-update, not "insert if new": the amount grows as further
802 : deposits join the transfer, and the reason the exchange gives for holding
803 : it can change long after the hold began. */
804 0 : qs = TALER_AUDITORDB_update_aml_hold (TALER_ARL_adb,
805 : wtid,
806 : &amount,
807 : (uint32_t) reason,
808 : claimed_measure);
809 0 : if (0 > qs)
810 : {
811 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
812 0 : hc->err = qs;
813 0 : return;
814 : }
815 : {
816 : struct TALER_Amount *bucket;
817 :
818 0 : switch (reason)
819 : {
820 0 : case TALER_EXCHANGEDB_DR_AMOUNT_TOO_SMALL:
821 0 : bucket = &hc->small_aggregate;
822 0 : break;
823 0 : case TALER_EXCHANGEDB_DR_KYC:
824 0 : bucket = &hc->aml_hold;
825 0 : break;
826 0 : case TALER_EXCHANGEDB_DR_NONE:
827 : default:
828 0 : bucket = &hc->transfer_lag;
829 0 : break;
830 : }
831 0 : TALER_ARL_amount_add (bucket,
832 : bucket,
833 : &amount);
834 : }
835 : }
836 :
837 :
838 : /**
839 : * Function called on each row of the aggregation tracking table.
840 : *
841 : * @param hc closure
842 : * @param rowid row of the entry in the aggregation tracking table
843 : * @param wtid wire transfer the deposit was aggregated into
844 : * @param wire_target_h_payto account the wire transfer should go to
845 : * @param pending false if the exchange really did make the transfer
846 : * @return #GNUNET_OK to continue to iterate
847 : */
848 : static enum GNUNET_GenericReturnValue
849 28 : note_held_transfer_cb (
850 : struct HoldContext *hc,
851 : uint64_t rowid,
852 : const struct TALER_WireTransferIdentifierRawP *wtid,
853 : const struct TALER_FullPaytoHashP *wire_target_h_payto,
854 : bool pending)
855 : {
856 : struct GNUNET_HashCode key;
857 :
858 : (void) wire_target_h_payto;
859 28 : if (0 > hc->err)
860 0 : return GNUNET_SYSERR; /* already failed */
861 28 : GNUNET_assert (hc->max_aggregation_serial <= rowid);
862 28 : hc->max_aggregation_serial = rowid + 1;
863 28 : if (! pending)
864 : {
865 : /* Aggregated and paid, the ordinary case. It cannot have a hold on file
866 : either: a wtid is only ever wired once, so the rows we are walking here
867 : are the ones that created it, and a hold from an earlier run is caught
868 : by the pass over the holds instead. */
869 28 : return GNUNET_OK;
870 : }
871 0 : GNUNET_CRYPTO_hash (wtid,
872 : sizeof (*wtid),
873 : &key);
874 0 : if (GNUNET_OK !=
875 0 : GNUNET_CONTAINER_multihashmap_put (
876 : hc->seen,
877 : &key,
878 0 : hc->seen,
879 : GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
880 0 : return GNUNET_OK; /* another row of the same transfer, already evaluated */
881 0 : evaluate_hold (hc,
882 : wtid);
883 0 : if (0 > hc->err)
884 0 : return GNUNET_SYSERR;
885 0 : return GNUNET_OK;
886 : }
887 :
888 :
889 : /**
890 : * Function called on each hold we already had on file.
891 : *
892 : * @param hc closure
893 : * @param ah the hold
894 : * @return #GNUNET_OK to continue to iterate
895 : */
896 : static enum GNUNET_GenericReturnValue
897 0 : evaluate_hold_cb (struct HoldContext *hc,
898 : const struct TALER_AUDITORDB_AmlHold *ah)
899 : {
900 : struct GNUNET_HashCode key;
901 :
902 0 : if (0 > hc->err)
903 0 : return GNUNET_SYSERR; /* already failed */
904 0 : GNUNET_CRYPTO_hash (&ah->wtid,
905 : sizeof (ah->wtid),
906 : &key);
907 0 : if (GNUNET_YES ==
908 0 : GNUNET_CONTAINER_multihashmap_contains (hc->seen,
909 : &key))
910 0 : return GNUNET_OK; /* fresh rows arrived for it, already re-evaluated */
911 0 : evaluate_hold (hc,
912 : &ah->wtid);
913 0 : if (0 > hc->err)
914 0 : return GNUNET_SYSERR;
915 0 : return GNUNET_OK;
916 : }
917 :
918 :
919 : /**
920 : * Checks which wire transfers the exchange aggregated but did not execute, and
921 : * what it says is keeping it from making them.
922 : *
923 : * Unlike the two checks above, the three balances this maintains are current
924 : * state and not a running tally: they are recomputed from the holds on file on
925 : * every run. That is what keeps them convergent, because a hold changes in
926 : * three independent ways -- it grows as deposits are added to the transfer, it
927 : * is reclassified when the exchange states a different reason, and it ends when
928 : * the transfer is finally made -- and only the first of those is announced by
929 : * a new row that a cursor could walk.
930 : *
931 : * @return transaction status
932 : */
933 : static enum GNUNET_DB_QueryStatus
934 6 : check_for_held_transfers (void)
935 : {
936 6 : struct HoldContext hc = {
937 : .max_aggregation_serial = TALER_ARL_USE_PP (wire_hold_aggregation_id),
938 : .err = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT
939 : };
940 : enum GNUNET_DB_QueryStatus qs;
941 :
942 6 : GNUNET_assert (GNUNET_OK ==
943 : TALER_amount_set_zero (TALER_ARL_currency,
944 : &hc.aml_hold));
945 6 : GNUNET_assert (GNUNET_OK ==
946 : TALER_amount_set_zero (TALER_ARL_currency,
947 : &hc.small_aggregate));
948 6 : GNUNET_assert (GNUNET_OK ==
949 : TALER_amount_set_zero (TALER_ARL_currency,
950 : &hc.transfer_lag));
951 6 : hc.seen = GNUNET_CONTAINER_multihashmap_create (128,
952 : GNUNET_NO);
953 : /* First the aggregations we have not seen before, then the holds we already
954 : knew about; the map keeps the two from doing each other's work. */
955 6 : qs = TALER_EXCHANGEDB_iterate_aggregation_wtids_above_serial_id (
956 : TALER_ARL_edb,
957 : TALER_ARL_USE_PP (wire_hold_aggregation_id),
958 : ¬e_held_transfer_cb,
959 : &hc);
960 6 : if (0 > qs)
961 : {
962 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
963 0 : goto cleanup;
964 : }
965 6 : if (0 > hc.err)
966 : {
967 0 : qs = hc.err;
968 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
969 0 : goto cleanup;
970 : }
971 6 : qs = TALER_AUDITORDB_iterate_aml_holds (TALER_ARL_adb,
972 : &evaluate_hold_cb,
973 : &hc);
974 6 : if (0 > qs)
975 : {
976 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
977 0 : goto cleanup;
978 : }
979 6 : if (0 > hc.err)
980 : {
981 0 : qs = hc.err;
982 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
983 0 : goto cleanup;
984 : }
985 6 : TALER_ARL_USE_PP (wire_hold_aggregation_id) = hc.max_aggregation_serial;
986 6 : TALER_ARL_USE_AB (total_aml_hold) = hc.aml_hold;
987 6 : TALER_ARL_USE_AB (total_small_aggregate) = hc.small_aggregate;
988 6 : TALER_ARL_USE_AB (total_transfer_lag) = hc.transfer_lag;
989 : /* One TALER_amount2s() per statement: it returns a single static buffer. */
990 6 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
991 : "Exchange is holding %s awaiting KYC\n",
992 : TALER_amount2s (&hc.aml_hold));
993 6 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
994 : "Exchange is holding %s in aggregates below the wire fee\n",
995 : TALER_amount2s (&hc.small_aggregate));
996 6 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
997 : "Exchange is holding %s for no stated reason\n",
998 : TALER_amount2s (&hc.transfer_lag));
999 6 : qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
1000 6 : cleanup:
1001 6 : GNUNET_CONTAINER_multihashmap_destroy (hc.seen);
1002 6 : return qs;
1003 : }
1004 :
1005 :
1006 : /**
1007 : * Start the database transactions and begin the audit.
1008 : *
1009 : * @return transaction status
1010 : */
1011 : static enum GNUNET_DB_QueryStatus
1012 6 : begin_transaction (void)
1013 : {
1014 : enum GNUNET_DB_QueryStatus qs;
1015 :
1016 6 : if (GNUNET_SYSERR ==
1017 6 : TALER_EXCHANGEDB_preflight (TALER_ARL_edb))
1018 : {
1019 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1020 : "Failed to initialize exchange database connection.\n");
1021 0 : return GNUNET_DB_STATUS_HARD_ERROR;
1022 : }
1023 6 : if (GNUNET_SYSERR ==
1024 6 : TALER_AUDITORDB_preflight (TALER_ARL_adb))
1025 : {
1026 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1027 : "Failed to initialize auditor database session.\n");
1028 0 : return GNUNET_DB_STATUS_HARD_ERROR;
1029 : }
1030 6 : if (GNUNET_OK !=
1031 6 : TALER_AUDITORDB_start (TALER_ARL_adb,
1032 : "auditor-transfer"))
1033 : {
1034 0 : GNUNET_break (0);
1035 0 : return GNUNET_DB_STATUS_HARD_ERROR;
1036 : }
1037 6 : if (GNUNET_OK !=
1038 6 : TALER_EXCHANGEDB_start_read_only (TALER_ARL_edb,
1039 : "transfer auditor"))
1040 : {
1041 0 : GNUNET_break (0);
1042 0 : TALER_AUDITORDB_rollback (TALER_ARL_adb);
1043 0 : return GNUNET_DB_STATUS_HARD_ERROR;
1044 : }
1045 6 : qs = TALER_AUDITORDB_get_auditor_progress (
1046 : TALER_ARL_adb,
1047 : TALER_ARL_GET_PP (wire_batch_deposit_id),
1048 : TALER_ARL_GET_PP (wire_aggregation_id),
1049 : TALER_ARL_GET_PP (wire_hold_aggregation_id),
1050 : NULL);
1051 6 : if (0 > qs)
1052 0 : goto handle_db_error;
1053 :
1054 6 : qs = TALER_AUDITORDB_get_balance (
1055 : TALER_ARL_adb,
1056 : TALER_ARL_GET_AB (total_amount_lag),
1057 : TALER_ARL_GET_AB (total_early_aggregation),
1058 : TALER_ARL_GET_AB (total_aml_hold),
1059 : TALER_ARL_GET_AB (total_small_aggregate),
1060 : TALER_ARL_GET_AB (total_transfer_lag),
1061 : NULL);
1062 6 : if (0 > qs)
1063 0 : goto handle_db_error;
1064 6 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
1065 : {
1066 0 : GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
1067 : "First analysis of with transfer auditor, starting audit from scratch\n");
1068 : }
1069 : else
1070 : {
1071 6 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1072 : "Resuming transfer audit at %llu / %llu / %llu\n",
1073 : (unsigned long long) TALER_ARL_USE_PP (wire_batch_deposit_id),
1074 : (unsigned long long) TALER_ARL_USE_PP (wire_aggregation_id),
1075 : (unsigned long long) TALER_ARL_USE_PP (
1076 : wire_hold_aggregation_id));
1077 : }
1078 :
1079 6 : qs = check_for_required_transfers ();
1080 6 : if (0 > qs)
1081 0 : goto handle_db_error;
1082 6 : qs = check_for_completed_transfers ();
1083 6 : if (0 > qs)
1084 0 : goto handle_db_error;
1085 6 : qs = check_for_held_transfers ();
1086 6 : if (0 > qs)
1087 0 : goto handle_db_error;
1088 :
1089 6 : qs = TALER_AUDITORDB_insert_auditor_progress (
1090 : TALER_ARL_adb,
1091 : TALER_ARL_SET_PP (wire_batch_deposit_id),
1092 : TALER_ARL_SET_PP (wire_aggregation_id),
1093 : TALER_ARL_SET_PP (wire_hold_aggregation_id),
1094 : NULL);
1095 6 : if (0 > qs)
1096 0 : goto handle_db_error;
1097 6 : qs = TALER_AUDITORDB_insert_balance (
1098 : TALER_ARL_adb,
1099 : TALER_ARL_SET_AB (total_amount_lag),
1100 : TALER_ARL_SET_AB (total_early_aggregation),
1101 : TALER_ARL_SET_AB (total_aml_hold),
1102 : TALER_ARL_SET_AB (total_small_aggregate),
1103 : TALER_ARL_SET_AB (total_transfer_lag),
1104 : NULL);
1105 6 : if (0 > qs)
1106 0 : goto handle_db_error;
1107 6 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1108 : "Concluded audit step at %llu/%llu/%llu\n",
1109 : (unsigned long long) TALER_ARL_USE_PP (wire_aggregation_id),
1110 : (unsigned long long) TALER_ARL_USE_PP (wire_batch_deposit_id),
1111 : (unsigned long long) TALER_ARL_USE_PP (
1112 : wire_hold_aggregation_id));
1113 6 : TALER_EXCHANGEDB_rollback (TALER_ARL_edb);
1114 6 : qs = TALER_AUDITORDB_commit (TALER_ARL_adb);
1115 6 : if (0 > qs)
1116 0 : goto handle_db_error;
1117 6 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1118 : "Transaction concluded!\n");
1119 6 : return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
1120 0 : handle_db_error:
1121 0 : TALER_AUDITORDB_rollback (TALER_ARL_adb);
1122 0 : TALER_EXCHANGEDB_rollback (TALER_ARL_edb);
1123 0 : GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
1124 0 : return qs;
1125 : }
1126 :
1127 :
1128 : /**
1129 : * Start auditor process.
1130 : */
1131 : static void
1132 6 : start (void)
1133 : {
1134 : enum GNUNET_DB_QueryStatus qs;
1135 :
1136 6 : for (unsigned int max_retries = 3; max_retries>0; max_retries--)
1137 : {
1138 6 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1139 : "Trying again (%u attempts left)\n",
1140 : max_retries);
1141 6 : qs = begin_transaction ();
1142 6 : if (GNUNET_DB_STATUS_SOFT_ERROR != qs)
1143 6 : break;
1144 : }
1145 6 : if (0 > qs)
1146 : {
1147 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1148 : "Audit failed\n");
1149 0 : GNUNET_break (0);
1150 0 : global_ret = EXIT_FAILURE;
1151 0 : GNUNET_SCHEDULER_shutdown ();
1152 0 : return;
1153 : }
1154 : }
1155 :
1156 :
1157 : /**
1158 : * Function called on events received from Postgres.
1159 : *
1160 : * @param cls closure, NULL
1161 : * @param extra additional event data provided
1162 : * @param extra_size number of bytes in @a extra
1163 : */
1164 : static void
1165 0 : db_notify (void *cls,
1166 : const void *extra,
1167 : size_t extra_size)
1168 : {
1169 : (void) cls;
1170 : (void) extra;
1171 : (void) extra_size;
1172 :
1173 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1174 : "Received notification to wake transfer helper\n");
1175 0 : start ();
1176 0 : }
1177 :
1178 :
1179 : /**
1180 : * Main function that will be run.
1181 : *
1182 : * @param cls closure
1183 : * @param args remaining command-line arguments
1184 : * @param cfgfile name of the configuration file used (for saving, can be NULL!)
1185 : * @param c configuration
1186 : */
1187 : static void
1188 6 : run (void *cls,
1189 : char *const *args,
1190 : const char *cfgfile,
1191 : const struct GNUNET_CONFIGURATION_Handle *c)
1192 : {
1193 : (void) cls;
1194 : (void) args;
1195 : (void) cfgfile;
1196 6 : cfg = c;
1197 6 : if (EXIT_SUCCESS !=
1198 6 : (global_ret = TALER_ARL_init (c)))
1199 : {
1200 0 : return;
1201 : }
1202 6 : GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
1203 : NULL);
1204 6 : if (GNUNET_OK !=
1205 6 : TALER_EXCHANGEDB_load_accounts (TALER_ARL_cfg,
1206 : TALER_EXCHANGEDB_ALO_DEBIT
1207 : | TALER_EXCHANGEDB_ALO_CREDIT
1208 : | TALER_EXCHANGEDB_ALO_AUTHDATA))
1209 : {
1210 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1211 : "No bank accounts configured\n");
1212 0 : global_ret = EXIT_NOTCONFIGURED;
1213 0 : GNUNET_SCHEDULER_shutdown ();
1214 0 : return;
1215 : }
1216 6 : if (0 == test_mode)
1217 : {
1218 : // FIXME-Optimization: use different event type in the future!
1219 0 : struct GNUNET_DB_EventHeaderP es = {
1220 0 : .size = htons (sizeof (es)),
1221 0 : .type = htons (TALER_DBEVENT_EXCHANGE_AUDITOR_WAKE_HELPER_WIRE)
1222 : };
1223 :
1224 0 : eh = TALER_AUDITORDB_event_listen (TALER_ARL_adb,
1225 : &es,
1226 0 : GNUNET_TIME_UNIT_FOREVER_REL,
1227 : &db_notify,
1228 : NULL);
1229 0 : GNUNET_assert (NULL != eh);
1230 : }
1231 6 : start ();
1232 : }
1233 :
1234 :
1235 : /**
1236 : * The main function of the wire auditing tool. Checks that
1237 : * the exchange's records of wire transfers match that of
1238 : * the wire gateway.
1239 : *
1240 : * @param argc number of arguments from the command line
1241 : * @param argv command line arguments
1242 : * @return 0 ok, 1 on error
1243 : */
1244 : int
1245 6 : main (int argc,
1246 : char *const *argv)
1247 : {
1248 6 : const struct GNUNET_GETOPT_CommandLineOption options[] = {
1249 6 : GNUNET_GETOPT_option_flag ('i',
1250 : "internal",
1251 : "perform checks only applicable for exchange-internal audits",
1252 : &internal_checks),
1253 6 : GNUNET_GETOPT_option_flag ('t',
1254 : "test",
1255 : "run in test mode and exit when idle",
1256 : &test_mode),
1257 6 : GNUNET_GETOPT_option_timetravel ('T',
1258 : "timetravel"),
1259 : GNUNET_GETOPT_OPTION_END
1260 : };
1261 : enum GNUNET_GenericReturnValue ret;
1262 :
1263 6 : ret = GNUNET_PROGRAM_run (
1264 : TALER_AUDITOR_project_data (),
1265 : argc,
1266 : argv,
1267 : "taler-helper-auditor-transfer",
1268 : gettext_noop (
1269 : "Audit exchange database for consistency of aggregations/transfers with respect to deposit deadlines"),
1270 : options,
1271 : &run,
1272 : NULL);
1273 6 : if (GNUNET_SYSERR == ret)
1274 0 : return EXIT_NOTCONFIGURED;
1275 6 : if (GNUNET_NO == ret)
1276 0 : return EXIT_SUCCESS;
1277 6 : return global_ret;
1278 : }
1279 :
1280 :
1281 : /* end of taler-helper-auditor-transfer.c */
|