LCOV - code coverage report
Current view: top level - auditor - taler-helper-auditor-wire.c (source / functions) Hit Total Coverage
Test: GNU Taler exchange coverage report Lines: 421 556 75.7 %
Date: 2021-08-30 06:43:37 Functions: 29 29 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   Copyright (C) 2017-2021 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.c
      18             :  * @brief audits that wire transfers match those from an exchange database.
      19             :  * @author Christian Grothoff
      20             :  *
      21             :  * - First, this auditor verifies that 'reserves_in' actually matches
      22             :  *   the incoming wire transfers from the bank.
      23             :  * - Second, we check that the outgoing wire transfers match those
      24             :  *   given in the 'wire_out' and 'reserve_closures' tables
      25             :  * - Finally, we check that all wire transfers that should have been made,
      26             :  *   were actually made
      27             :  */
      28             : #include "platform.h"
      29             : #include <gnunet/gnunet_util_lib.h>
      30             : #include <gnunet/gnunet_curl_lib.h>
      31             : #include "taler_auditordb_plugin.h"
      32             : #include "taler_exchangedb_lib.h"
      33             : #include "taler_json_lib.h"
      34             : #include "taler_bank_service.h"
      35             : #include "taler_signatures.h"
      36             : #include "report-lib.h"
      37             : 
      38             : 
      39             : /**
      40             :  * How much time do we allow the aggregator to lag behind?  If
      41             :  * wire transfers should have been made more than #GRACE_PERIOD
      42             :  * before, we issue warnings.
      43             :  */
      44             : #define GRACE_PERIOD GNUNET_TIME_UNIT_HOURS
      45             : 
      46             : /**
      47             :  * How much do we allow the bank and the exchange to disagree about
      48             :  * timestamps? Should be sufficiently large to avoid bogus reports from deltas
      49             :  * created by imperfect clock synchronization and network delay.
      50             :  */
      51             : #define TIME_TOLERANCE GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, \
      52             :                                                       15)
      53             : 
      54             : 
      55             : /**
      56             :  * Information we keep for each supported account.
      57             :  */
      58             : struct WireAccount
      59             : {
      60             :   /**
      61             :    * Accounts are kept in a DLL.
      62             :    */
      63             :   struct WireAccount *next;
      64             : 
      65             :   /**
      66             :    * Plugins are kept in a DLL.
      67             :    */
      68             :   struct WireAccount *prev;
      69             : 
      70             :   /**
      71             :    * Account details.
      72             :    */
      73             :   const struct TALER_EXCHANGEDB_AccountInfo *ai;
      74             : 
      75             :   /**
      76             :    * Active wire request for the transaction history.
      77             :    */
      78             :   struct TALER_BANK_CreditHistoryHandle *chh;
      79             : 
      80             :   /**
      81             :    * Active wire request for the transaction history.
      82             :    */
      83             :   struct TALER_BANK_DebitHistoryHandle *dhh;
      84             : 
      85             :   /**
      86             :    * Progress point for this account.
      87             :    */
      88             :   struct TALER_AUDITORDB_WireAccountProgressPoint pp;
      89             : 
      90             :   /**
      91             :    * Initial progress point for this account.
      92             :    */
      93             :   struct TALER_AUDITORDB_WireAccountProgressPoint start_pp;
      94             : 
      95             :   /**
      96             :    * Where we are in the inbound (CREDIT) transaction history.
      97             :    */
      98             :   uint64_t in_wire_off;
      99             : 
     100             :   /**
     101             :    * Where we are in the inbound (DEBIT) transaction history.
     102             :    */
     103             :   uint64_t out_wire_off;
     104             : 
     105             :   /**
     106             :    * Return value when we got this account's progress point.
     107             :    */
     108             :   enum GNUNET_DB_QueryStatus qsx;
     109             : };
     110             : 
     111             : 
     112             : /**
     113             :  * Information we track for a reserve being closed.
     114             :  */
     115             : struct ReserveClosure
     116             : {
     117             :   /**
     118             :    * Row in the reserves_closed table for this action.
     119             :    */
     120             :   uint64_t rowid;
     121             : 
     122             :   /**
     123             :    * When was the reserve closed?
     124             :    */
     125             :   struct GNUNET_TIME_Absolute execution_date;
     126             : 
     127             :   /**
     128             :    * Amount transferred (amount remaining minus fee).
     129             :    */
     130             :   struct TALER_Amount amount;
     131             : 
     132             :   /**
     133             :    * Target account where the money was sent.
     134             :    */
     135             :   char *receiver_account;
     136             : 
     137             :   /**
     138             :    * Wire transfer subject used.
     139             :    */
     140             :   struct TALER_WireTransferIdentifierRawP wtid;
     141             : };
     142             : 
     143             : 
     144             : /**
     145             :  * Map from H(wtid,receiver_account) to `struct ReserveClosure` entries.
     146             :  */
     147             : static struct GNUNET_CONTAINER_MultiHashMap *reserve_closures;
     148             : 
     149             : /**
     150             :  * Return value from main().
     151             :  */
     152             : static int global_ret;
     153             : 
     154             : /**
     155             :  * Map with information about incoming wire transfers.
     156             :  * Maps hashes of the wire offsets to `struct ReserveInInfo`s.
     157             :  */
     158             : static struct GNUNET_CONTAINER_MultiHashMap *in_map;
     159             : 
     160             : /**
     161             :  * Map with information about outgoing wire transfers.
     162             :  * Maps hashes of the wire subjects (in binary encoding)
     163             :  * to `struct ReserveOutInfo`s.
     164             :  */
     165             : static struct GNUNET_CONTAINER_MultiHashMap *out_map;
     166             : 
     167             : /**
     168             :  * Head of list of wire accounts we still need to look at.
     169             :  */
     170             : static struct WireAccount *wa_head;
     171             : 
     172             : /**
     173             :  * Tail of list of wire accounts we still need to look at.
     174             :  */
     175             : static struct WireAccount *wa_tail;
     176             : 
     177             : /**
     178             :  * Query status for the incremental processing status in the auditordb.
     179             :  * Return value from our call to the "get_wire_auditor_progress" function.
     180             :  */
     181             : static enum GNUNET_DB_QueryStatus qsx_gwap;
     182             : 
     183             : /**
     184             :  * Last reserve_in / wire_out serial IDs seen.
     185             :  */
     186             : static struct TALER_AUDITORDB_WireProgressPoint pp;
     187             : 
     188             : /**
     189             :  * Last reserve_in / wire_out serial IDs seen.
     190             :  */
     191             : static struct TALER_AUDITORDB_WireProgressPoint start_pp;
     192             : 
     193             : /**
     194             :  * Array of reports about row inconsitencies in wire_out table.
     195             :  */
     196             : static json_t *report_wire_out_inconsistencies;
     197             : 
     198             : /**
     199             :  * Array of reports about row inconsitencies in reserves_in table.
     200             :  */
     201             : static json_t *report_reserve_in_inconsistencies;
     202             : 
     203             : /**
     204             :  * Array of reports about wrong bank account being recorded for
     205             :  * incoming wire transfers.
     206             :  */
     207             : static json_t *report_missattribution_in_inconsistencies;
     208             : 
     209             : /**
     210             :  * Array of reports about row inconsistencies.
     211             :  */
     212             : static json_t *report_row_inconsistencies;
     213             : 
     214             : /**
     215             :  * Array of reports about inconsistencies in the database about
     216             :  * the incoming wire transfers (exchange is not exactly to blame).
     217             :  */
     218             : static json_t *report_wire_format_inconsistencies;
     219             : 
     220             : /**
     221             :  * Array of reports about minor row inconsistencies.
     222             :  */
     223             : static json_t *report_row_minor_inconsistencies;
     224             : 
     225             : /**
     226             :  * Array of reports about lagging transactions from deposits.
     227             :  */
     228             : static json_t *report_lags;
     229             : 
     230             : /**
     231             :  * Array of reports about lagging transactions from reserve closures.
     232             :  */
     233             : static json_t *report_closure_lags;
     234             : 
     235             : /**
     236             :  * Array of per-account progress data.
     237             :  */
     238             : static json_t *report_account_progress;
     239             : 
     240             : /**
     241             :  * Amount that is considered "tiny"
     242             :  */
     243             : static struct TALER_Amount tiny_amount;
     244             : 
     245             : /**
     246             :  * Total amount that was transferred too much from the exchange.
     247             :  */
     248             : static struct TALER_Amount total_bad_amount_out_plus;
     249             : 
     250             : /**
     251             :  * Total amount that was transferred too little from the exchange.
     252             :  */
     253             : static struct TALER_Amount total_bad_amount_out_minus;
     254             : 
     255             : /**
     256             :  * Total amount that was transferred too much to the exchange.
     257             :  */
     258             : static struct TALER_Amount total_bad_amount_in_plus;
     259             : 
     260             : /**
     261             :  * Total amount that was transferred too little to the exchange.
     262             :  */
     263             : static struct TALER_Amount total_bad_amount_in_minus;
     264             : 
     265             : /**
     266             :  * Total amount where the exchange has the wrong sender account
     267             :  * for incoming funds and may thus wire funds to the wrong
     268             :  * destination when closing the reserve.
     269             :  */
     270             : static struct TALER_Amount total_missattribution_in;
     271             : 
     272             : /**
     273             :  * Total amount which the exchange did not transfer in time.
     274             :  */
     275             : static struct TALER_Amount total_amount_lag;
     276             : 
     277             : /**
     278             :  * Total amount of reserve closures which the exchange did not transfer in time.
     279             :  */
     280             : static struct TALER_Amount total_closure_amount_lag;
     281             : 
     282             : /**
     283             :  * Total amount affected by wire format trouble.s
     284             :  */
     285             : static struct TALER_Amount total_wire_format_amount;
     286             : 
     287             : /**
     288             :  * Amount of zero in our currency.
     289             :  */
     290             : static struct TALER_Amount zero;
     291             : 
     292             : /**
     293             :  * Handle to the context for interacting with the bank.
     294             :  */
     295             : static struct GNUNET_CURL_Context *ctx;
     296             : 
     297             : /**
     298             :  * Scheduler context for running the @e ctx.
     299             :  */
     300             : static struct GNUNET_CURL_RescheduleContext *rc;
     301             : 
     302             : /**
     303             :  * Should we run checks that only work for exchange-internal audits?
     304             :  */
     305             : static int internal_checks;
     306             : 
     307             : /* *****************************   Shutdown   **************************** */
     308             : 
     309             : /**
     310             :  * Entry in map with wire information we expect to obtain from the
     311             :  * bank later.
     312             :  */
     313             : struct ReserveInInfo
     314             : {
     315             : 
     316             :   /**
     317             :    * Hash of expected row offset.
     318             :    */
     319             :   struct GNUNET_HashCode row_off_hash;
     320             : 
     321             :   /**
     322             :    * Expected details about the wire transfer.
     323             :    * The member "account_url" is to be allocated
     324             :    * at the end of this struct!
     325             :    */
     326             :   struct TALER_BANK_CreditDetails details;
     327             : 
     328             :   /**
     329             :    * RowID in reserves_in table.
     330             :    */
     331             :   uint64_t rowid;
     332             : 
     333             : };
     334             : 
     335             : 
     336             : /**
     337             :  * Entry in map with wire information we expect to obtain from the
     338             :  * #TALER_ARL_edb later.
     339             :  */
     340             : struct ReserveOutInfo
     341             : {
     342             : 
     343             :   /**
     344             :    * Hash of the wire transfer subject.
     345             :    */
     346             :   struct GNUNET_HashCode subject_hash;
     347             : 
     348             :   /**
     349             :    * Expected details about the wire transfer.
     350             :    */
     351             :   struct TALER_BANK_DebitDetails details;
     352             : 
     353             : };
     354             : 
     355             : 
     356             : /**
     357             :  * Free entry in #in_map.
     358             :  *
     359             :  * @param cls NULL
     360             :  * @param key unused key
     361             :  * @param value the `struct ReserveInInfo` to free
     362             :  * @return #GNUNET_OK
     363             :  */
     364             : static int
     365          83 : free_rii (void *cls,
     366             :           const struct GNUNET_HashCode *key,
     367             :           void *value)
     368             : {
     369          83 :   struct ReserveInInfo *rii = value;
     370             : 
     371             :   (void) cls;
     372          83 :   GNUNET_assert (GNUNET_YES ==
     373             :                  GNUNET_CONTAINER_multihashmap_remove (in_map,
     374             :                                                        key,
     375             :                                                        rii));
     376          83 :   GNUNET_free (rii);
     377          83 :   return GNUNET_OK;
     378             : }
     379             : 
     380             : 
     381             : /**
     382             :  * Free entry in #out_map.
     383             :  *
     384             :  * @param cls NULL
     385             :  * @param key unused key
     386             :  * @param value the `struct ReserveOutInfo` to free
     387             :  * @return #GNUNET_OK
     388             :  */
     389             : static int
     390          32 : free_roi (void *cls,
     391             :           const struct GNUNET_HashCode *key,
     392             :           void *value)
     393             : {
     394          32 :   struct ReserveOutInfo *roi = value;
     395             : 
     396             :   (void) cls;
     397          32 :   GNUNET_assert (GNUNET_YES ==
     398             :                  GNUNET_CONTAINER_multihashmap_remove (out_map,
     399             :                                                        key,
     400             :                                                        roi));
     401          32 :   GNUNET_free (roi);
     402          32 :   return GNUNET_OK;
     403             : }
     404             : 
     405             : 
     406             : /**
     407             :  * Free entry in #reserve_closures.
     408             :  *
     409             :  * @param cls NULL
     410             :  * @param key unused key
     411             :  * @param value the `struct ReserveClosure` to free
     412             :  * @return #GNUNET_OK
     413             :  */
     414             : static int
     415          59 : free_rc (void *cls,
     416             :          const struct GNUNET_HashCode *key,
     417             :          void *value)
     418             : {
     419          59 :   struct ReserveClosure *rc = value;
     420             : 
     421             :   (void) cls;
     422          59 :   GNUNET_assert (GNUNET_YES ==
     423             :                  GNUNET_CONTAINER_multihashmap_remove (reserve_closures,
     424             :                                                        key,
     425             :                                                        rc));
     426          59 :   GNUNET_free (rc->receiver_account);
     427          59 :   GNUNET_free (rc);
     428          59 :   return GNUNET_OK;
     429             : }
     430             : 
     431             : 
     432             : /**
     433             :  * Task run on shutdown.
     434             :  *
     435             :  * @param cls NULL
     436             :  */
     437             : static void
     438          83 : do_shutdown (void *cls)
     439             : {
     440             :   struct WireAccount *wa;
     441             : 
     442             :   (void) cls;
     443          83 :   if (NULL != report_row_inconsistencies)
     444             :   {
     445          83 :     GNUNET_assert (NULL != report_row_minor_inconsistencies);
     446          83 :     TALER_ARL_done (
     447          83 :       GNUNET_JSON_PACK (
     448             :         /* Tested in test-auditor.sh #11, #15, #20 */
     449             :         GNUNET_JSON_pack_array_steal ("wire_out_amount_inconsistencies",
     450             :                                       report_wire_out_inconsistencies),
     451             :         TALER_JSON_pack_amount ("total_wire_out_delta_plus",
     452             :                                 &total_bad_amount_out_plus),
     453             :         /* Tested in test-auditor.sh #11, #15, #19 */
     454             :         TALER_JSON_pack_amount ("total_wire_out_delta_minus",
     455             :                                 &total_bad_amount_out_minus),
     456             :         /* Tested in test-auditor.sh #2 */
     457             :         GNUNET_JSON_pack_array_steal ("reserve_in_amount_inconsistencies",
     458             :                                       report_reserve_in_inconsistencies),
     459             :         /* Tested in test-auditor.sh #2 */
     460             :         TALER_JSON_pack_amount ("total_wire_in_delta_plus",
     461             :                                 &total_bad_amount_in_plus),
     462             :         /* Tested in test-auditor.sh #3 */
     463             :         TALER_JSON_pack_amount ("total_wire_in_delta_minus",
     464             :                                 &total_bad_amount_in_minus),
     465             :         /* Tested in test-auditor.sh #9 */
     466             :         GNUNET_JSON_pack_array_steal ("missattribution_in_inconsistencies",
     467             :                                       report_missattribution_in_inconsistencies),
     468             :         /* Tested in test-auditor.sh #9 */
     469             :         TALER_JSON_pack_amount ("total_missattribution_in",
     470             :                                 &total_missattribution_in),
     471             :         GNUNET_JSON_pack_array_steal ("row_inconsistencies",
     472             :                                       report_row_inconsistencies),
     473             :         /* Tested in test-auditor.sh #10/#17 */
     474             :         GNUNET_JSON_pack_array_steal ("row_minor_inconsistencies",
     475             :                                       report_row_minor_inconsistencies),
     476             :         /* Tested in test-auditor.sh #19 */
     477             :         TALER_JSON_pack_amount ("total_wire_format_amount",
     478             :                                 &total_wire_format_amount),
     479             :         /* Tested in test-auditor.sh #19 */
     480             :         GNUNET_JSON_pack_array_steal ("wire_format_inconsistencies",
     481             :                                       report_wire_format_inconsistencies),
     482             :         /* Tested in test-auditor.sh #1 */
     483             :         TALER_JSON_pack_amount ("total_amount_lag",
     484             :                                 &total_amount_lag),
     485             :         /* Tested in test-auditor.sh #1 */
     486             :         GNUNET_JSON_pack_array_steal ("lag_details",
     487             :                                       report_lags),
     488             :         /* Tested in test-auditor.sh #22 */
     489             :         TALER_JSON_pack_amount ("total_closure_amount_lag",
     490             :                                 &total_closure_amount_lag),
     491             :         /* Tested in test-auditor.sh #22 */
     492             :         GNUNET_JSON_pack_array_steal ("reserve_lag_details",
     493             :                                       report_closure_lags),
     494             :         TALER_JSON_pack_time_abs_human ("wire_auditor_start_time",
     495             :                                         start_time),
     496             :         TALER_JSON_pack_time_abs_human ("wire_auditor_end_time",
     497             :                                         GNUNET_TIME_absolute_get ()),
     498             :         GNUNET_JSON_pack_uint64 ("start_pp_reserve_close_uuid",
     499             :                                  start_pp.last_reserve_close_uuid),
     500             :         GNUNET_JSON_pack_uint64 ("end_pp_reserve_close_uuid",
     501             :                                  pp.last_reserve_close_uuid),
     502             :         TALER_JSON_pack_time_abs_human ("start_pp_last_timestamp",
     503             :                                         start_pp.last_timestamp),
     504             :         TALER_JSON_pack_time_abs_human ("end_pp_last_timestamp",
     505             :                                         pp.last_timestamp),
     506             :         GNUNET_JSON_pack_array_steal ("account_progress",
     507             :                                       report_account_progress)));
     508          83 :     report_wire_out_inconsistencies = NULL;
     509          83 :     report_reserve_in_inconsistencies = NULL;
     510          83 :     report_row_inconsistencies = NULL;
     511          83 :     report_row_minor_inconsistencies = NULL;
     512          83 :     report_missattribution_in_inconsistencies = NULL;
     513          83 :     report_lags = NULL;
     514          83 :     report_closure_lags = NULL;
     515          83 :     report_account_progress = NULL;
     516          83 :     report_wire_format_inconsistencies = NULL;
     517             :   }
     518             :   else
     519             :   {
     520           0 :     TALER_ARL_done (NULL);
     521             :   }
     522          83 :   if (NULL != reserve_closures)
     523             :   {
     524          83 :     GNUNET_CONTAINER_multihashmap_iterate (reserve_closures,
     525             :                                            &free_rc,
     526             :                                            NULL);
     527          83 :     GNUNET_CONTAINER_multihashmap_destroy (reserve_closures);
     528          83 :     reserve_closures = NULL;
     529             :   }
     530          83 :   if (NULL != in_map)
     531             :   {
     532           0 :     GNUNET_CONTAINER_multihashmap_iterate (in_map,
     533             :                                            &free_rii,
     534             :                                            NULL);
     535           0 :     GNUNET_CONTAINER_multihashmap_destroy (in_map);
     536           0 :     in_map = NULL;
     537             :   }
     538          83 :   if (NULL != out_map)
     539             :   {
     540           0 :     GNUNET_CONTAINER_multihashmap_iterate (out_map,
     541             :                                            &free_roi,
     542             :                                            NULL);
     543           0 :     GNUNET_CONTAINER_multihashmap_destroy (out_map);
     544           0 :     out_map = NULL;
     545             :   }
     546         166 :   while (NULL != (wa = wa_head))
     547             :   {
     548          83 :     if (NULL != wa->dhh)
     549             :     {
     550           0 :       TALER_BANK_debit_history_cancel (wa->dhh);
     551           0 :       wa->dhh = NULL;
     552             :     }
     553          83 :     if (NULL != wa->chh)
     554             :     {
     555           0 :       TALER_BANK_credit_history_cancel (wa->chh);
     556           0 :       wa->chh = NULL;
     557             :     }
     558          83 :     GNUNET_CONTAINER_DLL_remove (wa_head,
     559             :                                  wa_tail,
     560             :                                  wa);
     561          83 :     GNUNET_free (wa);
     562             :   }
     563          83 :   if (NULL != ctx)
     564             :   {
     565          83 :     GNUNET_CURL_fini (ctx);
     566          83 :     ctx = NULL;
     567             :   }
     568          83 :   if (NULL != rc)
     569             :   {
     570          83 :     GNUNET_CURL_gnunet_rc_destroy (rc);
     571          83 :     rc = NULL;
     572             :   }
     573          83 :   TALER_EXCHANGEDB_unload_accounts ();
     574          83 :   TALER_ARL_cfg = NULL;
     575          83 : }
     576             : 
     577             : 
     578             : /**
     579             :  * Detect any entries in #reserve_closures that were not yet
     580             :  * observed on the wire transfer side and update the progress
     581             :  * point accordingly.
     582             :  *
     583             :  * @param cls NULL
     584             :  * @param key unused key
     585             :  * @param value the `struct ReserveClosure` to free
     586             :  * @return #GNUNET_OK
     587             :  */
     588             : static int
     589          58 : check_pending_rc (void *cls,
     590             :                   const struct GNUNET_HashCode *key,
     591             :                   void *value)
     592             : {
     593          58 :   struct ReserveClosure *rc = value;
     594             : 
     595             :   (void) cls;
     596             :   (void) key;
     597          58 :   TALER_ARL_amount_add (&total_closure_amount_lag,
     598             :                         &total_closure_amount_lag,
     599             :                         &rc->amount);
     600          58 :   if ( (0 != rc->amount.value) ||
     601          56 :        (0 != rc->amount.fraction) )
     602           2 :     TALER_ARL_report (report_closure_lags,
     603           2 :                       GNUNET_JSON_PACK (
     604             :                         GNUNET_JSON_pack_uint64 ("row",
     605             :                                                  rc->rowid),
     606             :                         TALER_JSON_pack_amount ("amount",
     607             :                                                 &rc->amount),
     608             :                         TALER_JSON_pack_time_abs_human ("deadline",
     609             :                                                         rc->execution_date),
     610             :                         GNUNET_JSON_pack_data_auto ("wtid",
     611             :                                                     &rc->wtid),
     612             :                         GNUNET_JSON_pack_string ("account",
     613             :                                                  rc->receiver_account)));
     614             :   pp.last_reserve_close_uuid
     615          58 :     = GNUNET_MIN (pp.last_reserve_close_uuid,
     616             :                   rc->rowid);
     617          58 :   return GNUNET_OK;
     618             : }
     619             : 
     620             : 
     621             : /**
     622             :  * Compute the key under which a reserve closure for a given
     623             :  * @a receiver_account and @a wtid would be stored.
     624             :  *
     625             :  * @param receiver_account payto://-URI of the account
     626             :  * @param wtid wire transfer identifier used
     627             :  * @param[out] key set to the key
     628             :  */
     629             : static void
     630          61 : hash_rc (const char *receiver_account,
     631             :          const struct TALER_WireTransferIdentifierRawP *wtid,
     632             :          struct GNUNET_HashCode *key)
     633          61 : {
     634          61 :   size_t slen = strlen (receiver_account);
     635          61 :   char buf[sizeof (struct TALER_WireTransferIdentifierRawP) + slen];
     636             : 
     637          61 :   memcpy (buf,
     638             :           wtid,
     639             :           sizeof (*wtid));
     640          61 :   memcpy (&buf[sizeof (*wtid)],
     641             :           receiver_account,
     642             :           slen);
     643          61 :   GNUNET_CRYPTO_hash (buf,
     644             :                       sizeof (buf),
     645             :                       key);
     646          61 : }
     647             : 
     648             : 
     649             : /**
     650             :  * Commit the transaction, checkpointing our progress in the auditor DB.
     651             :  *
     652             :  * @param qs transaction status so far
     653             :  * @return transaction status code
     654             :  */
     655             : static enum GNUNET_DB_QueryStatus
     656          83 : commit (enum GNUNET_DB_QueryStatus qs)
     657             : {
     658          83 :   if (0 > qs)
     659             :   {
     660           0 :     if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
     661           0 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     662             :                   "Serialization issue, not recording progress\n");
     663             :     else
     664           0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     665             :                   "Hard error, not recording progress\n");
     666           0 :     TALER_ARL_adb->rollback (TALER_ARL_adb->cls);
     667           0 :     TALER_ARL_edb->rollback (TALER_ARL_edb->cls);
     668           0 :     return qs;
     669             :   }
     670         166 :   for (struct WireAccount *wa = wa_head;
     671             :        NULL != wa;
     672          83 :        wa = wa->next)
     673             :   {
     674          83 :     GNUNET_assert (
     675             :       0 ==
     676             :       json_array_append_new (
     677             :         report_account_progress,
     678             :         GNUNET_JSON_PACK (
     679             :           GNUNET_JSON_pack_string ("account",
     680             :                                    wa->ai->section_name),
     681             :           GNUNET_JSON_pack_uint64 ("start_reserve_in",
     682             :                                    wa->start_pp.last_reserve_in_serial_id),
     683             :           GNUNET_JSON_pack_uint64 ("end_reserve_in",
     684             :                                    wa->pp.last_reserve_in_serial_id),
     685             :           GNUNET_JSON_pack_uint64 ("start_wire_out",
     686             :                                    wa->start_pp.
     687             :                                    last_wire_out_serial_id),
     688             :           GNUNET_JSON_pack_uint64 ("end_wire_out",
     689             :                                    wa->pp.last_wire_out_serial_id))));
     690          83 :     if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == wa->qsx)
     691          42 :       qs = TALER_ARL_adb->update_wire_auditor_account_progress (
     692          42 :         TALER_ARL_adb->cls,
     693             :         &TALER_ARL_master_pub,
     694          42 :         wa->ai->section_name,
     695          42 :         &wa->pp,
     696             :         wa->in_wire_off,
     697             :         wa->out_wire_off);
     698             :     else
     699          41 :       qs = TALER_ARL_adb->insert_wire_auditor_account_progress (
     700          41 :         TALER_ARL_adb->cls,
     701             :         &TALER_ARL_master_pub,
     702          41 :         wa->ai->section_name,
     703          41 :         &wa->pp,
     704             :         wa->in_wire_off,
     705             :         wa->out_wire_off);
     706          83 :     if (0 >= qs)
     707             :     {
     708           0 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     709             :                   "Failed to update auditor DB, not recording progress\n");
     710           0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     711           0 :       return qs;
     712             :     }
     713             :   }
     714          83 :   GNUNET_CONTAINER_multihashmap_iterate (reserve_closures,
     715             :                                          &check_pending_rc,
     716             :                                          NULL);
     717          83 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qsx_gwap)
     718          42 :     qs = TALER_ARL_adb->update_wire_auditor_progress (TALER_ARL_adb->cls,
     719             :                                                       &TALER_ARL_master_pub,
     720             :                                                       &pp);
     721             :   else
     722          41 :     qs = TALER_ARL_adb->insert_wire_auditor_progress (TALER_ARL_adb->cls,
     723             :                                                       &TALER_ARL_master_pub,
     724             :                                                       &pp);
     725          83 :   if (0 >= qs)
     726             :   {
     727           0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     728             :                 "Failed to update auditor DB, not recording progress\n");
     729           0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     730           0 :     return qs;
     731             :   }
     732          83 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     733             :               "Concluded audit step at %s\n",
     734             :               GNUNET_STRINGS_absolute_time_to_string (pp.last_timestamp));
     735             : 
     736          83 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
     737             :   {
     738          83 :     qs = TALER_ARL_edb->commit (TALER_ARL_edb->cls);
     739          83 :     if (0 > qs)
     740             :     {
     741           0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     742           0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     743             :                   "Exchange DB commit failed, rolling back transaction\n");
     744           0 :       TALER_ARL_adb->rollback (TALER_ARL_adb->cls);
     745             :     }
     746             :     else
     747             :     {
     748          83 :       qs = TALER_ARL_adb->commit (TALER_ARL_adb->cls);
     749          83 :       if (0 > qs)
     750             :       {
     751           0 :         GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     752           0 :         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     753             :                     "Auditor DB commit failed!\n");
     754             :       }
     755             :     }
     756             :   }
     757             :   else
     758             :   {
     759           0 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     760             :                 "Processing failed, rolling back transaction\n");
     761           0 :     TALER_ARL_adb->rollback (TALER_ARL_adb->cls);
     762           0 :     TALER_ARL_edb->rollback (TALER_ARL_edb->cls);
     763             :   }
     764          83 :   return qs;
     765             : }
     766             : 
     767             : 
     768             : /* ***************************** Analyze required transfers ************************ */
     769             : 
     770             : /**
     771             :  * Function called on deposits that are past their due date
     772             :  * and have not yet seen a wire transfer.
     773             :  *
     774             :  * @param cls closure
     775             :  * @param rowid deposit table row of the coin's deposit
     776             :  * @param coin_pub public key of the coin
     777             :  * @param amount value of the deposit, including fee
     778             :  * @param wire where should the funds be wired
     779             :  * @param deadline what was the requested wire transfer deadline
     780             :  * @param tiny did the exchange defer this transfer because it is too small?
     781             :  *             NOTE: only valid in internal audit mode!
     782             :  * @param done did the exchange claim that it made a transfer?
     783             :  *             NOTE: only valid in internal audit mode!
     784             :  */
     785             : static void
     786          42 : wire_missing_cb (void *cls,
     787             :                  uint64_t rowid,
     788             :                  const struct TALER_CoinSpendPublicKeyP *coin_pub,
     789             :                  const struct TALER_Amount *amount,
     790             :                  const json_t *wire,
     791             :                  struct GNUNET_TIME_Absolute deadline,
     792             :                  /* bool? */ int tiny,
     793             :                  /* bool? */ int done)
     794             : {
     795             :   json_t *rep;
     796             : 
     797             :   (void) cls;
     798          42 :   TALER_ARL_amount_add (&total_amount_lag,
     799             :                         &total_amount_lag,
     800             :                         amount);
     801          42 :   if (internal_checks)
     802             :   {
     803             :     /* In internal mode, we insist that the entry was
     804             :        actually marked as tiny. */
     805          42 :     if ( (GNUNET_YES == tiny) &&
     806           0 :          (0 > TALER_amount_cmp (amount,
     807             :                                 &tiny_amount)) )
     808           0 :       return; /* acceptable, amount was tiny */
     809             :   }
     810             :   else
     811             :   {
     812             :     /* External auditors do not replicate tiny, so they
     813             :        only check that the amount is tiny */
     814           0 :     if (0 > TALER_amount_cmp (amount,
     815             :                               &tiny_amount))
     816           0 :       return; /* acceptable, amount was tiny */
     817             :   }
     818          42 :   rep = GNUNET_JSON_PACK (
     819             :     GNUNET_JSON_pack_uint64 ("row",
     820             :                              rowid),
     821             :     TALER_JSON_pack_amount ("amount",
     822             :                             amount),
     823             :     TALER_JSON_pack_time_abs_human ("deadline",
     824             :                                     deadline),
     825             :     GNUNET_JSON_pack_data_auto ("coin_pub",
     826             :                                 coin_pub),
     827             :     GNUNET_JSON_pack_object_incref ("account",
     828             :                                     (json_t *) wire));
     829          42 :   if (internal_checks)
     830             :   {
     831             :     /* the 'done' bit is only useful in 'internal' mode */
     832          42 :     GNUNET_assert (0 ==
     833             :                    json_object_set (rep,
     834             :                                     "claimed_done",
     835             :                                     json_string ((done) ? "yes" : "no")));
     836             :   }
     837          42 :   TALER_ARL_report (report_lags,
     838             :                     rep);
     839             : }
     840             : 
     841             : 
     842             : /**
     843             :  * Checks that all wire transfers that should have happened
     844             :  * (based on deposits) have indeed happened.
     845             :  */
     846             : static void
     847          83 : check_for_required_transfers (void)
     848             : {
     849             :   struct GNUNET_TIME_Absolute next_timestamp;
     850             :   enum GNUNET_DB_QueryStatus qs;
     851             : 
     852          83 :   next_timestamp = GNUNET_TIME_absolute_get ();
     853          83 :   (void) GNUNET_TIME_round_abs (&next_timestamp);
     854             :   /* Subtract #GRACE_PERIOD, so we can be a bit behind in processing
     855             :      without immediately raising undue concern */
     856          83 :   next_timestamp = GNUNET_TIME_absolute_subtract (next_timestamp,
     857             :                                                   GRACE_PERIOD);
     858          83 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     859             :               "Analyzing exchange's unfinished deposits (deadline: %s)\n",
     860             :               GNUNET_STRINGS_absolute_time_to_string (next_timestamp));
     861          83 :   qs = TALER_ARL_edb->select_deposits_missing_wire (TALER_ARL_edb->cls,
     862             :                                                     pp.last_timestamp,
     863             :                                                     next_timestamp,
     864             :                                                     &wire_missing_cb,
     865             :                                                     &next_timestamp);
     866          83 :   if (0 > qs)
     867             :   {
     868           0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     869           0 :     global_ret = EXIT_FAILURE;
     870           0 :     GNUNET_SCHEDULER_shutdown ();
     871           0 :     return;
     872             :   }
     873          83 :   pp.last_timestamp = next_timestamp;
     874             :   /* conclude with success */
     875          83 :   commit (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
     876          83 :   GNUNET_SCHEDULER_shutdown ();
     877             : }
     878             : 
     879             : 
     880             : /* ***************************** Analyze reserves_out ************************ */
     881             : 
     882             : /**
     883             :  * Clean up after processing wire out data.
     884             :  */
     885             : static void
     886          83 : conclude_wire_out (void)
     887             : {
     888          83 :   GNUNET_CONTAINER_multihashmap_destroy (out_map);
     889          83 :   out_map = NULL;
     890          83 :   check_for_required_transfers ();
     891          83 : }
     892             : 
     893             : 
     894             : /**
     895             :  * Check that @a want is within #TIME_TOLERANCE of @a have.
     896             :  * Otherwise report an inconsistency in row @a rowid of @a table.
     897             :  *
     898             :  * @param table where is the inconsistency (if any)
     899             :  * @param rowid what is the row
     900             :  * @param want what is the expected time
     901             :  * @param have what is the time we got
     902             :  */
     903             : static void
     904          27 : check_time_difference (const char *table,
     905             :                        uint64_t rowid,
     906             :                        struct GNUNET_TIME_Absolute want,
     907             :                        struct GNUNET_TIME_Absolute have)
     908             : {
     909             :   struct GNUNET_TIME_Relative delta;
     910             :   char *details;
     911             : 
     912          27 :   if (have.abs_value_us > want.abs_value_us)
     913          13 :     delta = GNUNET_TIME_absolute_get_difference (want,
     914             :                                                  have);
     915             :   else
     916          14 :     delta = GNUNET_TIME_absolute_get_difference (have,
     917             :                                                  want);
     918          27 :   if (delta.rel_value_us <= TIME_TOLERANCE.rel_value_us)
     919          26 :     return;
     920             : 
     921           1 :   GNUNET_asprintf (&details,
     922             :                    "execution date mismatch (%s)",
     923             :                    GNUNET_STRINGS_relative_time_to_string (delta,
     924             :                                                            GNUNET_YES));
     925           1 :   TALER_ARL_report (report_row_minor_inconsistencies,
     926           1 :                     GNUNET_JSON_PACK (
     927             :                       GNUNET_JSON_pack_string ("table",
     928             :                                                table),
     929             :                       GNUNET_JSON_pack_uint64 ("row",
     930             :                                                rowid),
     931             :                       GNUNET_JSON_pack_string ("diagnostic",
     932             :                                                details)));
     933           1 :   GNUNET_free (details);
     934             : }
     935             : 
     936             : 
     937             : /**
     938             :  * Function called with details about outgoing wire transfers
     939             :  * as claimed by the exchange DB.
     940             :  *
     941             :  * @param cls a `struct WireAccount`
     942             :  * @param rowid unique serial ID for the refresh session in our DB
     943             :  * @param date timestamp of the transfer (roughly)
     944             :  * @param wtid wire transfer subject
     945             :  * @param wire wire transfer details of the receiver
     946             :  * @param amount amount that was wired
     947             :  * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
     948             :  */
     949             : static int
     950          30 : wire_out_cb (void *cls,
     951             :              uint64_t rowid,
     952             :              struct GNUNET_TIME_Absolute date,
     953             :              const struct TALER_WireTransferIdentifierRawP *wtid,
     954             :              const json_t *wire,
     955             :              const struct TALER_Amount *amount)
     956             : {
     957          30 :   struct WireAccount *wa = cls;
     958             :   struct GNUNET_HashCode key;
     959             :   struct ReserveOutInfo *roi;
     960             : 
     961          30 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     962             :               "Exchange wire OUT at %s of %s with WTID %s\n",
     963             :               GNUNET_STRINGS_absolute_time_to_string (date),
     964             :               TALER_amount2s (amount),
     965             :               TALER_B2S (wtid));
     966          30 :   GNUNET_CRYPTO_hash (wtid,
     967             :                       sizeof (struct TALER_WireTransferIdentifierRawP),
     968             :                       &key);
     969          30 :   roi = GNUNET_CONTAINER_multihashmap_get (out_map,
     970             :                                            &key);
     971          30 :   if (NULL == roi)
     972             :   {
     973             :     /* Wire transfer was not made (yet) at all (but would have been
     974             :        justified), so the entire amount is missing / still to be done.
     975             :        This is moderately harmless, it might just be that the aggreator
     976             :        has not yet fully caught up with the transfers it should do. */
     977           0 :     TALER_ARL_report (report_wire_out_inconsistencies,
     978           0 :                       GNUNET_JSON_PACK (
     979             :                         GNUNET_JSON_pack_uint64 ("row",
     980             :                                                  rowid),
     981             :                         TALER_JSON_pack_amount ("amount_wired",
     982             :                                                 &zero),
     983             :                         TALER_JSON_pack_amount ("amount_justified",
     984             :                                                 amount),
     985             :                         GNUNET_JSON_pack_data_auto ("wtid",
     986             :                                                     wtid),
     987             :                         TALER_JSON_pack_time_abs_human ("timestamp",
     988             :                                                         date),
     989             :                         GNUNET_JSON_pack_string ("diagnostic",
     990             :                                                  "wire transfer not made (yet?)"),
     991             :                         GNUNET_JSON_pack_string ("account_section",
     992             :                                                  wa->ai->section_name)));
     993           0 :     TALER_ARL_amount_add (&total_bad_amount_out_minus,
     994             :                           &total_bad_amount_out_minus,
     995             :                           amount);
     996           0 :     if (TALER_ARL_do_abort ())
     997           0 :       return GNUNET_SYSERR;
     998           0 :     return GNUNET_OK;
     999             :   }
    1000             :   {
    1001             :     char *payto_uri;
    1002             : 
    1003          30 :     payto_uri = TALER_JSON_wire_to_payto (wire);
    1004          30 :     if (0 != strcasecmp (payto_uri,
    1005             :                          roi->details.credit_account_uri))
    1006             :     {
    1007             :       /* Destination bank account is wrong in actual wire transfer, so
    1008             :          we should count the wire transfer as entirely spurious, and
    1009             :          additionally consider the justified wire transfer as missing. */
    1010           0 :       TALER_ARL_report (report_wire_out_inconsistencies,
    1011           0 :                         GNUNET_JSON_PACK (
    1012             :                           GNUNET_JSON_pack_uint64 ("row",
    1013             :                                                    rowid),
    1014             :                           TALER_JSON_pack_amount ("amount_wired",
    1015             :                                                   &roi->details.amount),
    1016             :                           TALER_JSON_pack_amount ("amount_justified",
    1017             :                                                   &zero),
    1018             :                           GNUNET_JSON_pack_data_auto ("wtid", wtid),
    1019             :                           TALER_JSON_pack_time_abs_human ("timestamp",
    1020             :                                                           date),
    1021             :                           GNUNET_JSON_pack_string ("diagnostic",
    1022             :                                                    "receiver account mismatch"),
    1023             :                           GNUNET_JSON_pack_string ("target",
    1024             :                                                    payto_uri),
    1025             :                           GNUNET_JSON_pack_string ("account_section",
    1026             :                                                    wa->ai->section_name)));
    1027           0 :       TALER_ARL_amount_add (&total_bad_amount_out_plus,
    1028             :                             &total_bad_amount_out_plus,
    1029             :                             &roi->details.amount);
    1030           0 :       TALER_ARL_report (report_wire_out_inconsistencies,
    1031           0 :                         GNUNET_JSON_PACK (
    1032             :                           GNUNET_JSON_pack_uint64 ("row",
    1033             :                                                    rowid),
    1034             :                           TALER_JSON_pack_amount ("amount_wired",
    1035             :                                                   &zero),
    1036             :                           TALER_JSON_pack_amount ("amount_justified",
    1037             :                                                   amount),
    1038             :                           GNUNET_JSON_pack_data_auto ("wtid",
    1039             :                                                       wtid),
    1040             :                           TALER_JSON_pack_time_abs_human ("timestamp",
    1041             :                                                           date),
    1042             :                           GNUNET_JSON_pack_string ("diagnostic",
    1043             :                                                    "receiver account mismatch"),
    1044             :                           GNUNET_JSON_pack_string ("target",
    1045             :                                                    roi->details.
    1046             :                                                    credit_account_uri),
    1047             :                           GNUNET_JSON_pack_string ("account_section",
    1048             :                                                    wa->ai->section_name)));
    1049           0 :       TALER_ARL_amount_add (&total_bad_amount_out_minus,
    1050             :                             &total_bad_amount_out_minus,
    1051             :                             amount);
    1052           0 :       GNUNET_free (payto_uri);
    1053           0 :       goto cleanup;
    1054             :     }
    1055          30 :     GNUNET_free (payto_uri);
    1056             :   }
    1057          30 :   if (0 != TALER_amount_cmp (&roi->details.amount,
    1058             :                              amount))
    1059             :   {
    1060           4 :     TALER_ARL_report (report_wire_out_inconsistencies,
    1061           4 :                       GNUNET_JSON_PACK (
    1062             :                         GNUNET_JSON_pack_uint64 ("row",
    1063             :                                                  rowid),
    1064             :                         TALER_JSON_pack_amount ("amount_justified",
    1065             :                                                 amount),
    1066             :                         TALER_JSON_pack_amount ("amount_wired",
    1067             :                                                 &roi->details.amount),
    1068             :                         GNUNET_JSON_pack_data_auto ("wtid",
    1069             :                                                     wtid),
    1070             :                         TALER_JSON_pack_time_abs_human ("timestamp",
    1071             :                                                         date),
    1072             :                         GNUNET_JSON_pack_string ("diagnostic",
    1073             :                                                  "wire amount does not match"),
    1074             :                         GNUNET_JSON_pack_string ("account_section",
    1075             :                                                  wa->ai->section_name)));
    1076           4 :     if (0 < TALER_amount_cmp (amount,
    1077           4 :                               &roi->details.amount))
    1078             :     {
    1079             :       /* amount > roi->details.amount: wire transfer was smaller than it should have been */
    1080             :       struct TALER_Amount delta;
    1081             : 
    1082           2 :       TALER_ARL_amount_subtract (&delta,
    1083             :                                  amount,
    1084             :                                  &roi->details.amount);
    1085           2 :       TALER_ARL_amount_add (&total_bad_amount_out_minus,
    1086             :                             &total_bad_amount_out_minus,
    1087             :                             &delta);
    1088             :     }
    1089             :     else
    1090             :     {
    1091             :       /* roi->details.amount < amount: wire transfer was larger than it should have been */
    1092             :       struct TALER_Amount delta;
    1093             : 
    1094           2 :       TALER_ARL_amount_subtract (&delta,
    1095             :                                  &roi->details.amount,
    1096             :                                  amount);
    1097           2 :       TALER_ARL_amount_add (&total_bad_amount_out_plus,
    1098             :                             &total_bad_amount_out_plus,
    1099             :                             &delta);
    1100             :     }
    1101           4 :     goto cleanup;
    1102             :   }
    1103             : 
    1104          26 :   check_time_difference ("wire_out",
    1105             :                          rowid,
    1106             :                          date,
    1107             :                          roi->details.execution_date);
    1108          30 : cleanup:
    1109          30 :   GNUNET_assert (GNUNET_OK ==
    1110             :                  free_roi (NULL,
    1111             :                            &key,
    1112             :                            roi));
    1113          30 :   wa->pp.last_wire_out_serial_id = rowid + 1;
    1114          30 :   if (TALER_ARL_do_abort ())
    1115           0 :     return GNUNET_SYSERR;
    1116          30 :   return GNUNET_OK;
    1117             : }
    1118             : 
    1119             : 
    1120             : /**
    1121             :  * Closure for #check_rc_matches
    1122             :  */
    1123             : struct CheckMatchContext
    1124             : {
    1125             : 
    1126             :   /**
    1127             :    * Reserve operation looking for a match
    1128             :    */
    1129             :   const struct ReserveOutInfo *roi;
    1130             : 
    1131             :   /**
    1132             :    * Set to true if we found a match.
    1133             :    */
    1134             :   bool found;
    1135             : };
    1136             : 
    1137             : 
    1138             : /**
    1139             :  * Check if any of the reserve closures match the given wire transfer.
    1140             :  *
    1141             :  * @param[in,out] cls a `struct CheckMatchContext`
    1142             :  * @param key key of @a value in #reserve_closures
    1143             :  * @param value a `struct ReserveClosure`
    1144             :  */
    1145             : static int
    1146           1 : check_rc_matches (void *cls,
    1147             :                   const struct GNUNET_HashCode *key,
    1148             :                   void *value)
    1149             : {
    1150           1 :   struct CheckMatchContext *ctx = cls;
    1151           1 :   struct ReserveClosure *rc = value;
    1152             : 
    1153           1 :   if ( (0 == GNUNET_memcmp (&ctx->roi->details.wtid,
    1154           1 :                             &rc->wtid)) &&
    1155           1 :        (0 == strcasecmp (rc->receiver_account,
    1156           2 :                          ctx->roi->details.credit_account_uri)) &&
    1157           1 :        (0 == TALER_amount_cmp (&rc->amount,
    1158           1 :                                &ctx->roi->details.amount)) )
    1159             :   {
    1160           1 :     check_time_difference ("reserves_closures",
    1161             :                            rc->rowid,
    1162             :                            rc->execution_date,
    1163           1 :                            ctx->roi->details.execution_date);
    1164           1 :     ctx->found = true;
    1165           1 :     free_rc (NULL,
    1166             :              key,
    1167             :              rc);
    1168           1 :     return GNUNET_NO;
    1169             :   }
    1170           0 :   return GNUNET_OK;
    1171             : }
    1172             : 
    1173             : 
    1174             : /**
    1175             :  * Check whether the given transfer was justified by a reserve closure. If
    1176             :  * not, complain that we failed to match an entry from #out_map.  This means a
    1177             :  * wire transfer was made without proper justification.
    1178             :  *
    1179             :  * @param cls a `struct WireAccount`
    1180             :  * @param key unused key
    1181             :  * @param value the `struct ReserveOutInfo` to report
    1182             :  * @return #GNUNET_OK
    1183             :  */
    1184             : static int
    1185           2 : complain_out_not_found (void *cls,
    1186             :                         const struct GNUNET_HashCode *key,
    1187             :                         void *value)
    1188             : {
    1189           2 :   struct WireAccount *wa = cls;
    1190           2 :   struct ReserveOutInfo *roi = value;
    1191             :   struct GNUNET_HashCode rkey;
    1192           2 :   struct CheckMatchContext ctx = {
    1193             :     .roi = roi,
    1194             :     .found = false
    1195             :   };
    1196             : 
    1197             :   (void) key;
    1198           2 :   hash_rc (roi->details.credit_account_uri,
    1199           2 :            &roi->details.wtid,
    1200             :            &rkey);
    1201           2 :   GNUNET_CONTAINER_multihashmap_get_multiple (reserve_closures,
    1202             :                                               &rkey,
    1203             :                                               &check_rc_matches,
    1204             :                                               &ctx);
    1205           2 :   if (ctx.found)
    1206           1 :     return GNUNET_OK;
    1207           1 :   TALER_ARL_report (report_wire_out_inconsistencies,
    1208           1 :                     GNUNET_JSON_PACK (
    1209             :                       GNUNET_JSON_pack_uint64 ("row",
    1210             :                                                0),
    1211             :                       TALER_JSON_pack_amount ("amount_wired",
    1212             :                                               &roi->details.amount),
    1213             :                       TALER_JSON_pack_amount ("amount_justified",
    1214             :                                               &zero),
    1215             :                       GNUNET_JSON_pack_data_auto ("wtid",
    1216             :                                                   &roi->details.wtid),
    1217             :                       TALER_JSON_pack_time_abs_human ("timestamp",
    1218             :                                                       roi->details.
    1219             :                                                       execution_date),
    1220             :                       GNUNET_JSON_pack_string ("account_section",
    1221             :                                                wa->ai->section_name),
    1222             :                       GNUNET_JSON_pack_string ("diagnostic",
    1223             :                                                "justification for wire transfer not found")));
    1224           1 :   TALER_ARL_amount_add (&total_bad_amount_out_plus,
    1225             :                         &total_bad_amount_out_plus,
    1226             :                         &roi->details.amount);
    1227           1 :   return GNUNET_OK;
    1228             : }
    1229             : 
    1230             : 
    1231             : /**
    1232             :  * Main function for processing 'reserves_out' data.  We start by going over
    1233             :  * the DEBIT transactions this time, and then verify that all of them are
    1234             :  * justified by 'reserves_out'.
    1235             :  *
    1236             :  * @param cls `struct WireAccount` with a wire account list to process
    1237             :  */
    1238             : static void
    1239             : process_debits (void *cls);
    1240             : 
    1241             : 
    1242             : /**
    1243             :  * Go over the "wire_out" table of the exchange and
    1244             :  * verify that all wire outs are in that table.
    1245             :  *
    1246             :  * @param wa wire account we are processing
    1247             :  */
    1248             : static void
    1249          83 : check_exchange_wire_out (struct WireAccount *wa)
    1250             : {
    1251             :   enum GNUNET_DB_QueryStatus qs;
    1252             : 
    1253          83 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1254             :               "Analyzing exchange's wire OUT table for account `%s'\n",
    1255             :               wa->ai->section_name);
    1256          83 :   qs = TALER_ARL_edb->select_wire_out_above_serial_id_by_account (
    1257          83 :     TALER_ARL_edb->cls,
    1258          83 :     wa->ai->section_name,
    1259             :     wa->pp.last_wire_out_serial_id,
    1260             :     &wire_out_cb,
    1261             :     wa);
    1262          83 :   if (0 > qs)
    1263             :   {
    1264           0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1265           0 :     global_ret = EXIT_FAILURE;
    1266           0 :     GNUNET_SCHEDULER_shutdown ();
    1267           0 :     return;
    1268             :   }
    1269          83 :   GNUNET_CONTAINER_multihashmap_iterate (out_map,
    1270             :                                          &complain_out_not_found,
    1271             :                                          wa);
    1272             :   /* clean up */
    1273          83 :   GNUNET_CONTAINER_multihashmap_iterate (out_map,
    1274             :                                          &free_roi,
    1275             :                                          NULL);
    1276          83 :   process_debits (wa->next);
    1277             : }
    1278             : 
    1279             : 
    1280             : /**
    1281             :  * This function is called for all transactions that
    1282             :  * are debited from the exchange's account (outgoing
    1283             :  * transactions).
    1284             :  *
    1285             :  * @param cls `struct WireAccount` with current wire account to process
    1286             :  * @param http_status_code http status of the request
    1287             :  * @param ec error code in case something went wrong
    1288             :  * @param row_off identification of the position at which we are querying
    1289             :  * @param details details about the wire transfer
    1290             :  * @param json original response in JSON format
    1291             :  * @return #GNUNET_OK to continue, #GNUNET_SYSERR to abort iteration
    1292             :  */
    1293             : static int
    1294         116 : history_debit_cb (void *cls,
    1295             :                   unsigned int http_status_code,
    1296             :                   enum TALER_ErrorCode ec,
    1297             :                   uint64_t row_off,
    1298             :                   const struct TALER_BANK_DebitDetails *details,
    1299             :                   const json_t *json)
    1300             : {
    1301         116 :   struct WireAccount *wa = cls;
    1302             :   struct ReserveOutInfo *roi;
    1303             :   size_t slen;
    1304             : 
    1305             :   (void) json;
    1306         116 :   if (NULL == details)
    1307             :   {
    1308          83 :     wa->dhh = NULL;
    1309          83 :     if (TALER_EC_NONE != ec)
    1310             :     {
    1311           0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1312             :                   "Error fetching debit history of account %s: %u/%u!\n",
    1313             :                   wa->ai->section_name,
    1314             :                   http_status_code,
    1315             :                   (unsigned int) ec);
    1316           0 :       commit (GNUNET_DB_STATUS_HARD_ERROR);
    1317           0 :       global_ret = EXIT_FAILURE;
    1318           0 :       GNUNET_SCHEDULER_shutdown ();
    1319           0 :       return GNUNET_SYSERR;
    1320             :     }
    1321          83 :     check_exchange_wire_out (wa);
    1322          83 :     return GNUNET_OK;
    1323             :   }
    1324          33 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1325             :               "Analyzing bank DEBIT at %s of %s with WTID %s\n",
    1326             :               GNUNET_STRINGS_absolute_time_to_string (details->execution_date),
    1327             :               TALER_amount2s (&details->amount),
    1328             :               TALER_B2S (&details->wtid));
    1329             :   /* Update offset */
    1330          33 :   wa->out_wire_off = row_off;
    1331          33 :   slen = strlen (details->credit_account_uri) + 1;
    1332          33 :   roi = GNUNET_malloc (sizeof (struct ReserveOutInfo)
    1333             :                        + slen);
    1334          33 :   GNUNET_CRYPTO_hash (&details->wtid,
    1335             :                       sizeof (details->wtid),
    1336             :                       &roi->subject_hash);
    1337          33 :   roi->details.amount = details->amount;
    1338          33 :   roi->details.execution_date = details->execution_date;
    1339          33 :   roi->details.wtid = details->wtid;
    1340          33 :   roi->details.credit_account_uri = (const char *) &roi[1];
    1341          33 :   memcpy (&roi[1],
    1342          33 :           details->credit_account_uri,
    1343             :           slen);
    1344          33 :   if (GNUNET_OK !=
    1345          33 :       GNUNET_CONTAINER_multihashmap_put (out_map,
    1346          33 :                                          &roi->subject_hash,
    1347             :                                          roi,
    1348             :                                          GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
    1349             :   {
    1350             :     char *diagnostic;
    1351             : 
    1352           1 :     GNUNET_asprintf (&diagnostic,
    1353             :                      "duplicate subject hash `%s'",
    1354           1 :                      TALER_B2S (&roi->subject_hash));
    1355           1 :     TALER_ARL_amount_add (&total_wire_format_amount,
    1356             :                           &total_wire_format_amount,
    1357             :                           &details->amount);
    1358           1 :     TALER_ARL_report (report_wire_format_inconsistencies,
    1359           1 :                       GNUNET_JSON_PACK (
    1360             :                         TALER_JSON_pack_amount ("amount",
    1361             :                                                 &details->amount),
    1362             :                         GNUNET_JSON_pack_uint64 ("wire_offset",
    1363             :                                                  row_off),
    1364             :                         GNUNET_JSON_pack_string ("diagnostic",
    1365             :                                                  diagnostic)));
    1366           1 :     GNUNET_free (diagnostic);
    1367           1 :     return GNUNET_OK;
    1368             :   }
    1369          32 :   return GNUNET_OK;
    1370             : }
    1371             : 
    1372             : 
    1373             : /**
    1374             :  * Main function for processing 'reserves_out' data.  We start by going over
    1375             :  * the DEBIT transactions this time, and then verify that all of them are
    1376             :  * justified by 'reserves_out'.
    1377             :  *
    1378             :  * @param cls `struct WireAccount` with a wire account list to process
    1379             :  */
    1380             : static void
    1381         166 : process_debits (void *cls)
    1382             : {
    1383         166 :   struct WireAccount *wa = cls;
    1384             : 
    1385             :   /* skip accounts where DEBIT is not enabled */
    1386         166 :   while ( (NULL != wa) &&
    1387          83 :           (GNUNET_NO == wa->ai->debit_enabled) )
    1388           0 :     wa = wa->next;
    1389         166 :   if (NULL == wa)
    1390             :   {
    1391             :     /* end of iteration, now check wire_out to see
    1392             :        if it matches #out_map */
    1393          83 :     conclude_wire_out ();
    1394          83 :     return;
    1395             :   }
    1396          83 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1397             :               "Checking bank DEBIT records of account `%s'\n",
    1398             :               wa->ai->section_name);
    1399          83 :   GNUNET_assert (NULL == wa->dhh);
    1400          83 :   wa->dhh = TALER_BANK_debit_history (ctx,
    1401          83 :                                       wa->ai->auth,
    1402             :                                       wa->out_wire_off,
    1403             :                                       INT64_MAX,
    1404             :                                       GNUNET_TIME_UNIT_ZERO,
    1405             :                                       &history_debit_cb,
    1406             :                                       wa);
    1407          83 :   if (NULL == wa->dhh)
    1408             :   {
    1409           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1410             :                 "Failed to obtain bank transaction history for `%s'\n",
    1411             :                 wa->ai->section_name);
    1412           0 :     commit (GNUNET_DB_STATUS_HARD_ERROR);
    1413           0 :     global_ret = EXIT_FAILURE;
    1414           0 :     GNUNET_SCHEDULER_shutdown ();
    1415           0 :     return;
    1416             :   }
    1417             : }
    1418             : 
    1419             : 
    1420             : /**
    1421             :  * Begin analyzing wire_out.
    1422             :  */
    1423             : static void
    1424          83 : begin_debit_audit (void)
    1425             : {
    1426          83 :   out_map = GNUNET_CONTAINER_multihashmap_create (1024,
    1427             :                                                   GNUNET_YES);
    1428          83 :   process_debits (wa_head);
    1429          83 : }
    1430             : 
    1431             : 
    1432             : /* ***************************** Analyze reserves_in ************************ */
    1433             : 
    1434             : /**
    1435             :  * Conclude the credit history check by logging entries that
    1436             :  * were not found and freeing resources. Then move on to
    1437             :  * processing debits.
    1438             :  */
    1439             : static void
    1440          83 : conclude_credit_history (void)
    1441             : {
    1442          83 :   GNUNET_CONTAINER_multihashmap_destroy (in_map);
    1443          83 :   in_map = NULL;
    1444             :   /* credit done, now check debits */
    1445          83 :   begin_debit_audit ();
    1446          83 : }
    1447             : 
    1448             : 
    1449             : /**
    1450             :  * Function called with details about incoming wire transfers
    1451             :  * as claimed by the exchange DB.
    1452             :  *
    1453             :  * @param cls a `struct WireAccount` we are processing
    1454             :  * @param rowid unique serial ID for the entry in our DB
    1455             :  * @param reserve_pub public key of the reserve (also the WTID)
    1456             :  * @param credit amount that was received
    1457             :  * @param sender_account_details payto://-URL of the sender's bank account
    1458             :  * @param wire_reference unique identifier for the wire transfer
    1459             :  * @param execution_date when did we receive the funds
    1460             :  * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
    1461             :  */
    1462             : static int
    1463          83 : reserve_in_cb (void *cls,
    1464             :                uint64_t rowid,
    1465             :                const struct TALER_ReservePublicKeyP *reserve_pub,
    1466             :                const struct TALER_Amount *credit,
    1467             :                const char *sender_account_details,
    1468             :                uint64_t wire_reference,
    1469             :                struct GNUNET_TIME_Absolute execution_date)
    1470             : {
    1471          83 :   struct WireAccount *wa = cls;
    1472             :   struct ReserveInInfo *rii;
    1473             :   size_t slen;
    1474             : 
    1475          83 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1476             :               "Analyzing exchange wire IN (%llu) at %s of %s with reserve_pub %s\n",
    1477             :               (unsigned long long) rowid,
    1478             :               GNUNET_STRINGS_absolute_time_to_string (execution_date),
    1479             :               TALER_amount2s (credit),
    1480             :               TALER_B2S (reserve_pub));
    1481          83 :   slen = strlen (sender_account_details) + 1;
    1482          83 :   rii = GNUNET_malloc (sizeof (struct ReserveInInfo) + slen);
    1483          83 :   rii->rowid = rowid;
    1484          83 :   rii->details.amount = *credit;
    1485          83 :   rii->details.execution_date = execution_date;
    1486          83 :   rii->details.reserve_pub = *reserve_pub;
    1487          83 :   rii->details.debit_account_uri = (const char *) &rii[1];
    1488          83 :   memcpy (&rii[1],
    1489             :           sender_account_details,
    1490             :           slen);
    1491          83 :   GNUNET_CRYPTO_hash (&wire_reference,
    1492             :                       sizeof (uint64_t),
    1493             :                       &rii->row_off_hash);
    1494          83 :   if (GNUNET_OK !=
    1495          83 :       GNUNET_CONTAINER_multihashmap_put (in_map,
    1496          83 :                                          &rii->row_off_hash,
    1497             :                                          rii,
    1498             :                                          GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
    1499             :   {
    1500           0 :     TALER_ARL_report (report_row_inconsistencies,
    1501           0 :                       GNUNET_JSON_PACK (
    1502             :                         GNUNET_JSON_pack_string ("table",
    1503             :                                                  "reserves_in"),
    1504             :                         GNUNET_JSON_pack_uint64 ("row",
    1505             :                                                  rowid),
    1506             :                         GNUNET_JSON_pack_data_auto ("wire_offset_hash",
    1507             :                                                     &rii->row_off_hash),
    1508             :                         GNUNET_JSON_pack_string ("diagnostic",
    1509             :                                                  "duplicate wire offset")));
    1510           0 :     GNUNET_free (rii);
    1511           0 :     if (TALER_ARL_do_abort ())
    1512           0 :       return GNUNET_SYSERR;
    1513           0 :     return GNUNET_OK;
    1514             :   }
    1515          83 :   wa->pp.last_reserve_in_serial_id = rowid + 1;
    1516          83 :   if (TALER_ARL_do_abort ())
    1517           0 :     return GNUNET_SYSERR;
    1518          83 :   return GNUNET_OK;
    1519             : }
    1520             : 
    1521             : 
    1522             : /**
    1523             :  * Complain that we failed to match an entry from #in_map.
    1524             :  *
    1525             :  * @param cls a `struct WireAccount`
    1526             :  * @param key unused key
    1527             :  * @param value the `struct ReserveInInfo` to free
    1528             :  * @return #GNUNET_OK
    1529             :  */
    1530             : static int
    1531           1 : complain_in_not_found (void *cls,
    1532             :                        const struct GNUNET_HashCode *key,
    1533             :                        void *value)
    1534             : {
    1535           1 :   struct WireAccount *wa = cls;
    1536           1 :   struct ReserveInInfo *rii = value;
    1537             : 
    1538             :   (void) key;
    1539           1 :   TALER_ARL_report (report_reserve_in_inconsistencies,
    1540           1 :                     GNUNET_JSON_PACK (
    1541             :                       GNUNET_JSON_pack_uint64 ("row",
    1542             :                                                rii->rowid),
    1543             :                       TALER_JSON_pack_amount ("amount_exchange_expected",
    1544             :                                               &rii->details.amount),
    1545             :                       TALER_JSON_pack_amount ("amount_wired",
    1546             :                                               &zero),
    1547             :                       GNUNET_JSON_pack_data_auto ("reserve_pub",
    1548             :                                                   &rii->details.reserve_pub),
    1549             :                       TALER_JSON_pack_time_abs_human ("timestamp",
    1550             :                                                       rii->details.
    1551             :                                                       execution_date),
    1552             :                       GNUNET_JSON_pack_string ("account",
    1553             :                                                wa->ai->section_name),
    1554             :                       GNUNET_JSON_pack_string ("diagnostic",
    1555             :                                                "incoming wire transfer claimed by exchange not found")));
    1556           1 :   TALER_ARL_amount_add (&total_bad_amount_in_minus,
    1557             :                         &total_bad_amount_in_minus,
    1558             :                         &rii->details.amount);
    1559           1 :   return GNUNET_OK;
    1560             : }
    1561             : 
    1562             : 
    1563             : /**
    1564             :  * Start processing the next wire account.
    1565             :  * Shuts down if we are done.
    1566             :  *
    1567             :  * @param cls `struct WireAccount` with a wire account list to process
    1568             :  */
    1569             : static void
    1570             : process_credits (void *cls);
    1571             : 
    1572             : 
    1573             : /**
    1574             :  * This function is called for all transactions that
    1575             :  * are credited to the exchange's account (incoming
    1576             :  * transactions).
    1577             :  *
    1578             :  * @param cls `struct WireAccount` we are processing
    1579             :  * @param http_status HTTP status returned by the bank
    1580             :  * @param ec error code in case something went wrong
    1581             :  * @param row_off identification of the position at which we are querying
    1582             :  * @param details details about the wire transfer
    1583             :  * @param json raw response
    1584             :  * @return #GNUNET_OK to continue, #GNUNET_SYSERR to abort iteration
    1585             :  */
    1586             : static int
    1587         165 : history_credit_cb (void *cls,
    1588             :                    unsigned int http_status,
    1589             :                    enum TALER_ErrorCode ec,
    1590             :                    uint64_t row_off,
    1591             :                    const struct TALER_BANK_CreditDetails *details,
    1592             :                    const json_t *json)
    1593             : {
    1594         165 :   struct WireAccount *wa = cls;
    1595             :   struct ReserveInInfo *rii;
    1596             :   struct GNUNET_HashCode key;
    1597             : 
    1598             :   (void) json;
    1599         165 :   if (NULL == details)
    1600             :   {
    1601          83 :     wa->chh = NULL;
    1602          83 :     if (TALER_EC_NONE != ec)
    1603             :     {
    1604           0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1605             :                   "Error fetching credit history of account %s: %u/%s!\n",
    1606             :                   wa->ai->section_name,
    1607             :                   http_status,
    1608             :                   TALER_ErrorCode_get_hint (ec));
    1609           0 :       commit (GNUNET_DB_STATUS_HARD_ERROR);
    1610           0 :       global_ret = EXIT_FAILURE;
    1611           0 :       GNUNET_SCHEDULER_shutdown ();
    1612           0 :       return GNUNET_SYSERR;
    1613             :     }
    1614             :     /* end of operation */
    1615          83 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1616             :                 "Reconciling CREDIT processing of account `%s'\n",
    1617             :                 wa->ai->section_name);
    1618          83 :     GNUNET_CONTAINER_multihashmap_iterate (in_map,
    1619             :                                            &complain_in_not_found,
    1620             :                                            wa);
    1621             :     /* clean up before 2nd phase */
    1622          83 :     GNUNET_CONTAINER_multihashmap_iterate (in_map,
    1623             :                                            &free_rii,
    1624             :                                            NULL);
    1625          83 :     process_credits (wa->next);
    1626          83 :     return GNUNET_OK;
    1627             :   }
    1628          82 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1629             :               "Analyzing bank CREDIT at %s of %s with Reserve-pub %s\n",
    1630             :               GNUNET_STRINGS_absolute_time_to_string (details->execution_date),
    1631             :               TALER_amount2s (&details->amount),
    1632             :               TALER_B2S (&details->reserve_pub));
    1633          82 :   GNUNET_CRYPTO_hash (&row_off,
    1634             :                       sizeof (row_off),
    1635             :                       &key);
    1636          82 :   rii = GNUNET_CONTAINER_multihashmap_get (in_map,
    1637             :                                            &key);
    1638          82 :   if (NULL == rii)
    1639             :   {
    1640           0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1641             :                 "Failed to find wire transfer at `%s' in exchange database. Audit ends at this point in time.\n",
    1642             :                 GNUNET_STRINGS_absolute_time_to_string (
    1643             :                   details->execution_date));
    1644           0 :     wa->chh = NULL;
    1645           0 :     process_credits (wa->next);
    1646           0 :     return GNUNET_SYSERR; /* not an error, just end of processing */
    1647             :   }
    1648             : 
    1649             :   /* Update offset */
    1650          82 :   wa->in_wire_off = row_off;
    1651             :   /* compare records with expected data */
    1652          82 :   if (0 != GNUNET_memcmp (&details->reserve_pub,
    1653             :                           &rii->details.reserve_pub))
    1654             :   {
    1655           1 :     TALER_ARL_report (report_reserve_in_inconsistencies,
    1656           1 :                       GNUNET_JSON_PACK (
    1657             :                         GNUNET_JSON_pack_uint64 ("row",
    1658             :                                                  rii->rowid),
    1659             :                         GNUNET_JSON_pack_uint64 ("bank_row",
    1660             :                                                  row_off),
    1661             :                         TALER_JSON_pack_amount ("amount_exchange_expected",
    1662             :                                                 &rii->details.amount),
    1663             :                         TALER_JSON_pack_amount ("amount_wired",
    1664             :                                                 &zero),
    1665             :                         GNUNET_JSON_pack_data_auto ("reserve_pub",
    1666             :                                                     &rii->details.reserve_pub),
    1667             :                         TALER_JSON_pack_time_abs_human ("timestamp",
    1668             :                                                         rii->details.
    1669             :                                                         execution_date),
    1670             :                         GNUNET_JSON_pack_string ("diagnostic",
    1671             :                                                  "wire subject does not match")));
    1672           1 :     TALER_ARL_amount_add (&total_bad_amount_in_minus,
    1673             :                           &total_bad_amount_in_minus,
    1674             :                           &rii->details.amount);
    1675           1 :     TALER_ARL_report (report_reserve_in_inconsistencies,
    1676           1 :                       GNUNET_JSON_PACK (
    1677             :                         GNUNET_JSON_pack_uint64 ("row",
    1678             :                                                  rii->rowid),
    1679             :                         GNUNET_JSON_pack_uint64 ("bank_row",
    1680             :                                                  row_off),
    1681             :                         TALER_JSON_pack_amount ("amount_exchange_expected",
    1682             :                                                 &zero),
    1683             :                         TALER_JSON_pack_amount ("amount_wired",
    1684             :                                                 &details->amount),
    1685             :                         GNUNET_JSON_pack_data_auto ("reserve_pub",
    1686             :                                                     &details->reserve_pub),
    1687             :                         TALER_JSON_pack_time_abs_human ("timestamp",
    1688             :                                                         details->execution_date),
    1689             :                         GNUNET_JSON_pack_string ("diagnostic",
    1690             :                                                  "wire subject does not match")));
    1691             : 
    1692           1 :     TALER_ARL_amount_add (&total_bad_amount_in_plus,
    1693             :                           &total_bad_amount_in_plus,
    1694             :                           &details->amount);
    1695           1 :     goto cleanup;
    1696             :   }
    1697          81 :   if (0 != TALER_amount_cmp (&rii->details.amount,
    1698             :                              &details->amount))
    1699             :   {
    1700           5 :     TALER_ARL_report (report_reserve_in_inconsistencies,
    1701           5 :                       GNUNET_JSON_PACK (
    1702             :                         GNUNET_JSON_pack_uint64 ("row",
    1703             :                                                  rii->rowid),
    1704             :                         GNUNET_JSON_pack_uint64 ("bank_row",
    1705             :                                                  row_off),
    1706             :                         TALER_JSON_pack_amount ("amount_exchange_expected",
    1707             :                                                 &rii->details.amount),
    1708             :                         TALER_JSON_pack_amount ("amount_wired",
    1709             :                                                 &details->amount),
    1710             :                         GNUNET_JSON_pack_data_auto ("reserve_pub",
    1711             :                                                     &details->reserve_pub),
    1712             :                         TALER_JSON_pack_time_abs_human ("timestamp",
    1713             :                                                         details->execution_date),
    1714             :                         GNUNET_JSON_pack_string ("diagnostic",
    1715             :                                                  "wire amount does not match")));
    1716           5 :     if (0 < TALER_amount_cmp (&details->amount,
    1717           5 :                               &rii->details.amount))
    1718             :     {
    1719             :       /* details->amount > rii->details.amount: wire transfer was larger than it should have been */
    1720             :       struct TALER_Amount delta;
    1721             : 
    1722           1 :       TALER_ARL_amount_subtract (&delta,
    1723             :                                  &details->amount,
    1724             :                                  &rii->details.amount);
    1725           1 :       TALER_ARL_amount_add (&total_bad_amount_in_plus,
    1726             :                             &total_bad_amount_in_plus,
    1727             :                             &delta);
    1728             :     }
    1729             :     else
    1730             :     {
    1731             :       /* rii->details.amount < details->amount: wire transfer was smaller than it should have been */
    1732             :       struct TALER_Amount delta;
    1733             : 
    1734           4 :       TALER_ARL_amount_subtract (&delta,
    1735             :                                  &rii->details.amount,
    1736             :                                  &details->amount);
    1737           4 :       TALER_ARL_amount_add (&total_bad_amount_in_minus,
    1738             :                             &total_bad_amount_in_minus,
    1739             :                             &delta);
    1740             :     }
    1741           5 :     goto cleanup;
    1742             :   }
    1743          76 :   if (0 != strcasecmp (details->debit_account_uri,
    1744             :                        rii->details.debit_account_uri))
    1745             :   {
    1746           1 :     TALER_ARL_report (report_missattribution_in_inconsistencies,
    1747           1 :                       GNUNET_JSON_PACK (
    1748             :                         TALER_JSON_pack_amount ("amount",
    1749             :                                                 &rii->details.amount),
    1750             :                         GNUNET_JSON_pack_uint64 ("row",
    1751             :                                                  rii->rowid),
    1752             :                         GNUNET_JSON_pack_uint64 ("bank_row",
    1753             :                                                  row_off),
    1754             :                         GNUNET_JSON_pack_data_auto (
    1755             :                           "reserve_pub",
    1756             :                           &rii->details.reserve_pub)));
    1757           1 :     TALER_ARL_amount_add (&total_missattribution_in,
    1758             :                           &total_missattribution_in,
    1759             :                           &rii->details.amount);
    1760             :   }
    1761          76 :   if (details->execution_date.abs_value_us !=
    1762          76 :       rii->details.execution_date.abs_value_us)
    1763             :   {
    1764           1 :     TALER_ARL_report (report_row_minor_inconsistencies,
    1765           1 :                       GNUNET_JSON_PACK (
    1766             :                         GNUNET_JSON_pack_string ("table",
    1767             :                                                  "reserves_in"),
    1768             :                         GNUNET_JSON_pack_uint64 ("row",
    1769             :                                                  rii->rowid),
    1770             :                         GNUNET_JSON_pack_uint64 ("bank_row",
    1771             :                                                  row_off),
    1772             :                         GNUNET_JSON_pack_string ("diagnostic",
    1773             :                                                  "execution date mismatch")));
    1774             :   }
    1775          75 : cleanup:
    1776          82 :   GNUNET_assert (GNUNET_OK ==
    1777             :                  free_rii (NULL,
    1778             :                            &key,
    1779             :                            rii));
    1780          82 :   return GNUNET_OK;
    1781             : }
    1782             : 
    1783             : 
    1784             : /* ***************************** Setup logic ************************ */
    1785             : 
    1786             : 
    1787             : /**
    1788             :  * Start processing the next wire account.
    1789             :  * Shuts down if we are done.
    1790             :  *
    1791             :  * @param cls `struct WireAccount` with a wire account list to process
    1792             :  */
    1793             : static void
    1794         166 : process_credits (void *cls)
    1795             : {
    1796         166 :   struct WireAccount *wa = cls;
    1797             :   enum GNUNET_DB_QueryStatus qs;
    1798             : 
    1799             :   /* skip accounts where CREDIT is not enabled */
    1800         166 :   while ( (NULL != wa) &&
    1801          83 :           (GNUNET_NO == wa->ai->credit_enabled) )
    1802           0 :     wa = wa->next;
    1803         166 :   if (NULL == wa)
    1804             :   {
    1805             :     /* done with all accounts, conclude check */
    1806          83 :     conclude_credit_history ();
    1807          83 :     return;
    1808             :   }
    1809          83 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1810             :               "Analyzing exchange's wire IN table for account `%s'\n",
    1811             :               wa->ai->section_name);
    1812          83 :   qs = TALER_ARL_edb->select_reserves_in_above_serial_id_by_account (
    1813          83 :     TALER_ARL_edb->cls,
    1814          83 :     wa->ai->section_name,
    1815             :     wa->pp.last_reserve_in_serial_id,
    1816             :     &reserve_in_cb,
    1817             :     wa);
    1818          83 :   if (0 > qs)
    1819             :   {
    1820           0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1821           0 :     global_ret = EXIT_FAILURE;
    1822           0 :     GNUNET_SCHEDULER_shutdown ();
    1823           0 :     return;
    1824             :   }
    1825             : 
    1826          83 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1827             :               "Starting bank CREDIT history of account `%s'\n",
    1828             :               wa->ai->section_name);
    1829          83 :   wa->chh = TALER_BANK_credit_history (ctx,
    1830          83 :                                        wa->ai->auth,
    1831             :                                        wa->in_wire_off,
    1832             :                                        INT64_MAX,
    1833             :                                        GNUNET_TIME_UNIT_ZERO,
    1834             :                                        &history_credit_cb,
    1835             :                                        wa);
    1836          83 :   if (NULL == wa->chh)
    1837             :   {
    1838           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1839             :                 "Failed to obtain bank transaction history\n");
    1840           0 :     commit (GNUNET_DB_STATUS_HARD_ERROR);
    1841           0 :     global_ret = EXIT_FAILURE;
    1842           0 :     GNUNET_SCHEDULER_shutdown ();
    1843           0 :     return;
    1844             :   }
    1845             : }
    1846             : 
    1847             : 
    1848             : /**
    1849             :  * Begin audit of CREDITs to the exchange.
    1850             :  */
    1851             : static void
    1852          83 : begin_credit_audit (void)
    1853             : {
    1854          83 :   in_map = GNUNET_CONTAINER_multihashmap_create (1024,
    1855             :                                                  GNUNET_YES);
    1856             :   /* now go over all bank accounts and check delta with in_map */
    1857          83 :   process_credits (wa_head);
    1858          83 : }
    1859             : 
    1860             : 
    1861             : /**
    1862             :  * Function called about reserve closing operations
    1863             :  * the aggregator triggered.
    1864             :  *
    1865             :  * @param cls closure
    1866             :  * @param rowid row identifier used to uniquely identify the reserve closing operation
    1867             :  * @param execution_date when did we execute the close operation
    1868             :  * @param amount_with_fee how much did we debit the reserve
    1869             :  * @param closing_fee how much did we charge for closing the reserve
    1870             :  * @param reserve_pub public key of the reserve
    1871             :  * @param receiver_account where did we send the funds, in payto://-format
    1872             :  * @param wtid identifier used for the wire transfer
    1873             :  * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
    1874             :  */
    1875             : static int
    1876          59 : reserve_closed_cb (void *cls,
    1877             :                    uint64_t rowid,
    1878             :                    struct GNUNET_TIME_Absolute execution_date,
    1879             :                    const struct TALER_Amount *amount_with_fee,
    1880             :                    const struct TALER_Amount *closing_fee,
    1881             :                    const struct TALER_ReservePublicKeyP *reserve_pub,
    1882             :                    const char *receiver_account,
    1883             :                    const struct TALER_WireTransferIdentifierRawP *wtid)
    1884             : {
    1885             :   struct ReserveClosure *rc;
    1886             :   struct GNUNET_HashCode key;
    1887             : 
    1888             :   (void) cls;
    1889          59 :   rc = GNUNET_new (struct ReserveClosure);
    1890          59 :   if (TALER_ARL_SR_INVALID_NEGATIVE ==
    1891          59 :       TALER_ARL_amount_subtract_neg (&rc->amount,
    1892             :                                      amount_with_fee,
    1893             :                                      closing_fee))
    1894             :   {
    1895           0 :     TALER_ARL_report (report_row_inconsistencies,
    1896           0 :                       GNUNET_JSON_PACK (
    1897             :                         GNUNET_JSON_pack_string ("table",
    1898             :                                                  "reserves_closures"),
    1899             :                         GNUNET_JSON_pack_uint64 ("row",
    1900             :                                                  rowid),
    1901             :                         GNUNET_JSON_pack_data_auto ("reserve_pub",
    1902             :                                                     reserve_pub),
    1903             :                         TALER_JSON_pack_amount ("amount_with_fee",
    1904             :                                                 amount_with_fee),
    1905             :                         TALER_JSON_pack_amount ("closing_fee",
    1906             :                                                 closing_fee),
    1907             :                         GNUNET_JSON_pack_string ("diagnostic",
    1908             :                                                  "closing fee above total amount")));
    1909           0 :     GNUNET_free (rc);
    1910           0 :     if (TALER_ARL_do_abort ())
    1911           0 :       return GNUNET_SYSERR;
    1912           0 :     return GNUNET_OK;
    1913             :   }
    1914             :   pp.last_reserve_close_uuid
    1915          59 :     = GNUNET_MAX (pp.last_reserve_close_uuid,
    1916             :                   rowid + 1);
    1917          59 :   rc->receiver_account = GNUNET_strdup (receiver_account);
    1918          59 :   rc->wtid = *wtid;
    1919          59 :   rc->execution_date = execution_date;
    1920          59 :   rc->rowid = rowid;
    1921          59 :   hash_rc (receiver_account,
    1922             :            wtid,
    1923             :            &key);
    1924          59 :   (void) GNUNET_CONTAINER_multihashmap_put (reserve_closures,
    1925             :                                             &key,
    1926             :                                             rc,
    1927             :                                             GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
    1928          59 :   if (TALER_ARL_do_abort ())
    1929           0 :     return GNUNET_SYSERR;
    1930          59 :   return GNUNET_OK;
    1931             : }
    1932             : 
    1933             : 
    1934             : /**
    1935             :  * Start the database transactions and begin the audit.
    1936             :  *
    1937             :  * @return transaction status code
    1938             :  */
    1939             : static enum GNUNET_DB_QueryStatus
    1940          83 : begin_transaction (void)
    1941             : {
    1942          83 :   if (GNUNET_SYSERR ==
    1943          83 :       TALER_ARL_edb->preflight (TALER_ARL_edb->cls))
    1944             :   {
    1945           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1946             :                 "Failed to initialize exchange database connection.\n");
    1947           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    1948             :   }
    1949          83 :   if (GNUNET_SYSERR ==
    1950          83 :       TALER_ARL_adb->preflight (TALER_ARL_adb->cls))
    1951             :   {
    1952           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1953             :                 "Failed to initialize auditor database session.\n");
    1954           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    1955             :   }
    1956          83 :   if (GNUNET_OK !=
    1957          83 :       TALER_ARL_adb->start (TALER_ARL_adb->cls))
    1958             :   {
    1959           0 :     GNUNET_break (0);
    1960           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    1961             :   }
    1962          83 :   TALER_ARL_edb->preflight (TALER_ARL_edb->cls);
    1963          83 :   if (GNUNET_OK !=
    1964          83 :       TALER_ARL_edb->start (TALER_ARL_edb->cls,
    1965             :                             "wire auditor"))
    1966             :   {
    1967           0 :     GNUNET_break (0);
    1968           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    1969             :   }
    1970         166 :   for (struct WireAccount *wa = wa_head;
    1971             :        NULL != wa;
    1972          83 :        wa = wa->next)
    1973             :   {
    1974         166 :     wa->qsx = TALER_ARL_adb->get_wire_auditor_account_progress (
    1975          83 :       TALER_ARL_adb->cls,
    1976             :       &TALER_ARL_master_pub,
    1977          83 :       wa->ai->section_name,
    1978             :       &wa->pp,
    1979             :       &wa->in_wire_off,
    1980             :       &wa->out_wire_off);
    1981          83 :     if (0 > wa->qsx)
    1982             :     {
    1983           0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == wa->qsx);
    1984           0 :       return GNUNET_DB_STATUS_HARD_ERROR;
    1985             :     }
    1986          83 :     wa->start_pp = wa->pp;
    1987             :   }
    1988          83 :   qsx_gwap = TALER_ARL_adb->get_wire_auditor_progress (TALER_ARL_adb->cls,
    1989             :                                                        &TALER_ARL_master_pub,
    1990             :                                                        &pp);
    1991          83 :   if (0 > qsx_gwap)
    1992             :   {
    1993           0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qsx_gwap);
    1994           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    1995             :   }
    1996          83 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qsx_gwap)
    1997             :   {
    1998          41 :     GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
    1999             :                 "First analysis of with wire auditor, starting audit from scratch\n");
    2000             :   }
    2001             :   else
    2002             :   {
    2003          42 :     start_pp = pp;
    2004          42 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    2005             :                 "Resuming wire audit at %s / %llu\n",
    2006             :                 GNUNET_STRINGS_absolute_time_to_string (pp.last_timestamp),
    2007             :                 (unsigned long long) pp.last_reserve_close_uuid);
    2008             :   }
    2009             : 
    2010             :   {
    2011             :     enum GNUNET_DB_QueryStatus qs;
    2012             : 
    2013          83 :     qs = TALER_ARL_edb->select_reserve_closed_above_serial_id (
    2014          83 :       TALER_ARL_edb->cls,
    2015             :       pp.last_reserve_close_uuid,
    2016             :       &reserve_closed_cb,
    2017             :       NULL);
    2018          83 :     if (0 > qs)
    2019             :     {
    2020           0 :       GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
    2021           0 :       return GNUNET_DB_STATUS_HARD_ERROR;
    2022             :     }
    2023             :   }
    2024          83 :   begin_credit_audit ();
    2025          83 :   return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
    2026             : }
    2027             : 
    2028             : 
    2029             : /**
    2030             :  * Function called with information about a wire account.  Adds the
    2031             :  * account to our list for processing (if it is enabled and we can
    2032             :  * load the plugin).
    2033             :  *
    2034             :  * @param cls closure, NULL
    2035             :  * @param ai account information
    2036             :  */
    2037             : static void
    2038          83 : process_account_cb (void *cls,
    2039             :                     const struct TALER_EXCHANGEDB_AccountInfo *ai)
    2040             : {
    2041             :   struct WireAccount *wa;
    2042             : 
    2043             :   (void) cls;
    2044          83 :   if ( (! ai->debit_enabled) &&
    2045           0 :        (! ai->credit_enabled) )
    2046           0 :     return; /* not an active exchange account */
    2047          83 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    2048             :               "Found exchange account `%s'\n",
    2049             :               ai->section_name);
    2050          83 :   wa = GNUNET_new (struct WireAccount);
    2051          83 :   wa->ai = ai;
    2052          83 :   GNUNET_CONTAINER_DLL_insert (wa_head,
    2053             :                                wa_tail,
    2054             :                                wa);
    2055             : }
    2056             : 
    2057             : 
    2058             : /**
    2059             :  * Main function that will be run.
    2060             :  *
    2061             :  * @param cls closure
    2062             :  * @param args remaining command-line arguments
    2063             :  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
    2064             :  * @param c configuration
    2065             :  */
    2066             : static void
    2067          83 : run (void *cls,
    2068             :      char *const *args,
    2069             :      const char *cfgfile,
    2070             :      const struct GNUNET_CONFIGURATION_Handle *c)
    2071             : {
    2072             :   (void) cls;
    2073             :   (void) args;
    2074             :   (void) cfgfile;
    2075          83 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    2076             :               "Launching wire auditor\n");
    2077          83 :   if (GNUNET_OK !=
    2078          83 :       TALER_ARL_init (c))
    2079             :   {
    2080           0 :     global_ret = EXIT_FAILURE;
    2081           0 :     return;
    2082             :   }
    2083          83 :   if (GNUNET_OK !=
    2084          83 :       TALER_config_get_amount (TALER_ARL_cfg,
    2085             :                                "auditor",
    2086             :                                "TINY_AMOUNT",
    2087             :                                &tiny_amount))
    2088             :   {
    2089           0 :     global_ret = EXIT_NOTCONFIGURED;
    2090           0 :     return;
    2091             :   }
    2092          83 :   GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
    2093             :                                  NULL);
    2094          83 :   ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
    2095             :                           &rc);
    2096          83 :   rc = GNUNET_CURL_gnunet_rc_create (ctx);
    2097          83 :   if (NULL == ctx)
    2098             :   {
    2099           0 :     GNUNET_break (0);
    2100           0 :     global_ret = EXIT_FAILURE;
    2101           0 :     return;
    2102             :   }
    2103          83 :   reserve_closures = GNUNET_CONTAINER_multihashmap_create (1024,
    2104             :                                                            GNUNET_NO);
    2105          83 :   GNUNET_assert (NULL !=
    2106             :                  (report_wire_out_inconsistencies = json_array ()));
    2107          83 :   GNUNET_assert (NULL !=
    2108             :                  (report_reserve_in_inconsistencies = json_array ()));
    2109          83 :   GNUNET_assert (NULL !=
    2110             :                  (report_row_minor_inconsistencies = json_array ()));
    2111          83 :   GNUNET_assert (NULL !=
    2112             :                  (report_wire_format_inconsistencies
    2113             :                     = json_array ()));
    2114          83 :   GNUNET_assert (NULL !=
    2115             :                  (report_row_inconsistencies = json_array ()));
    2116          83 :   GNUNET_assert (NULL !=
    2117             :                  (report_missattribution_in_inconsistencies
    2118             :                     = json_array ()));
    2119          83 :   GNUNET_assert (NULL !=
    2120             :                  (report_lags = json_array ()));
    2121          83 :   GNUNET_assert (NULL !=
    2122             :                  (report_closure_lags = json_array ()));
    2123          83 :   GNUNET_assert (NULL !=
    2124             :                  (report_account_progress = json_array ()));
    2125          83 :   GNUNET_assert (GNUNET_OK ==
    2126             :                  TALER_amount_set_zero (TALER_ARL_currency,
    2127             :                                         &total_bad_amount_out_plus));
    2128          83 :   GNUNET_assert (GNUNET_OK ==
    2129             :                  TALER_amount_set_zero (TALER_ARL_currency,
    2130             :                                         &total_bad_amount_out_minus));
    2131          83 :   GNUNET_assert (GNUNET_OK ==
    2132             :                  TALER_amount_set_zero (TALER_ARL_currency,
    2133             :                                         &total_bad_amount_in_plus));
    2134          83 :   GNUNET_assert (GNUNET_OK ==
    2135             :                  TALER_amount_set_zero (TALER_ARL_currency,
    2136             :                                         &total_bad_amount_in_minus));
    2137          83 :   GNUNET_assert (GNUNET_OK ==
    2138             :                  TALER_amount_set_zero (TALER_ARL_currency,
    2139             :                                         &total_missattribution_in));
    2140          83 :   GNUNET_assert (GNUNET_OK ==
    2141             :                  TALER_amount_set_zero (TALER_ARL_currency,
    2142             :                                         &total_amount_lag));
    2143          83 :   GNUNET_assert (GNUNET_OK ==
    2144             :                  TALER_amount_set_zero (TALER_ARL_currency,
    2145             :                                         &total_closure_amount_lag));
    2146          83 :   GNUNET_assert (GNUNET_OK ==
    2147             :                  TALER_amount_set_zero (TALER_ARL_currency,
    2148             :                                         &total_wire_format_amount));
    2149          83 :   GNUNET_assert (GNUNET_OK ==
    2150             :                  TALER_amount_set_zero (TALER_ARL_currency,
    2151             :                                         &zero));
    2152          83 :   if (GNUNET_OK !=
    2153          83 :       TALER_EXCHANGEDB_load_accounts (TALER_ARL_cfg,
    2154             :                                       TALER_EXCHANGEDB_ALO_DEBIT
    2155             :                                       | TALER_EXCHANGEDB_ALO_CREDIT
    2156             :                                       | TALER_EXCHANGEDB_ALO_AUTHDATA))
    2157             :   {
    2158           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    2159             :                 "No bank accounts configured\n");
    2160           0 :     global_ret = EXIT_NOTCONFIGURED;
    2161           0 :     GNUNET_SCHEDULER_shutdown ();
    2162             :   }
    2163          83 :   TALER_EXCHANGEDB_find_accounts (&process_account_cb,
    2164             :                                   NULL);
    2165          83 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
    2166          83 :       begin_transaction ())
    2167             :   {
    2168           0 :     GNUNET_break (0);
    2169           0 :     global_ret = EXIT_FAILURE;
    2170           0 :     GNUNET_SCHEDULER_shutdown ();
    2171             :   }
    2172             : }
    2173             : 
    2174             : 
    2175             : /**
    2176             :  * The main function of the wire auditing tool. Checks that
    2177             :  * the exchange's records of wire transfers match that of
    2178             :  * the wire gateway.
    2179             :  *
    2180             :  * @param argc number of arguments from the command line
    2181             :  * @param argv command line arguments
    2182             :  * @return 0 ok, 1 on error
    2183             :  */
    2184             : int
    2185          83 : main (int argc,
    2186             :       char *const *argv)
    2187             : {
    2188          83 :   const struct GNUNET_GETOPT_CommandLineOption options[] = {
    2189          83 :     GNUNET_GETOPT_option_flag ('i',
    2190             :                                "internal",
    2191             :                                "perform checks only applicable for exchange-internal audits",
    2192             :                                &internal_checks),
    2193          83 :     GNUNET_GETOPT_option_base32_auto ('m',
    2194             :                                       "exchange-key",
    2195             :                                       "KEY",
    2196             :                                       "public key of the exchange (Crockford base32 encoded)",
    2197             :                                       &TALER_ARL_master_pub),
    2198          83 :     GNUNET_GETOPT_option_timetravel ('T',
    2199             :                                      "timetravel"),
    2200             :     GNUNET_GETOPT_OPTION_END
    2201             :   };
    2202             :   enum GNUNET_GenericReturnValue ret;
    2203             : 
    2204             :   /* force linker to link against libtalerutil; if we do
    2205             :      not do this, the linker may "optimize" libtalerutil
    2206             :      away and skip #TALER_OS_init(), which we do need */
    2207          83 :   (void) TALER_project_data_default ();
    2208          83 :   if (GNUNET_OK !=
    2209          83 :       GNUNET_STRINGS_get_utf8_args (argc, argv,
    2210             :                                     &argc, &argv))
    2211           0 :     return EXIT_INVALIDARGUMENT;
    2212          83 :   ret = GNUNET_PROGRAM_run (
    2213             :     argc,
    2214             :     argv,
    2215             :     "taler-helper-auditor-wire",
    2216             :     gettext_noop (
    2217             :       "Audit exchange database for consistency with the bank's wire transfers"),
    2218             :     options,
    2219             :     &run,
    2220             :     NULL);
    2221          83 :   GNUNET_free_nz ((void *) argv);
    2222          83 :   if (GNUNET_SYSERR == ret)
    2223           0 :     return EXIT_INVALIDARGUMENT;
    2224          83 :   if (GNUNET_NO == ret)
    2225           0 :     return EXIT_SUCCESS;
    2226          83 :   return global_ret;
    2227             : }
    2228             : 
    2229             : 
    2230             : /* end of taler-helper-auditor-wire.c */

Generated by: LCOV version 1.14