exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

taler-helper-auditor-wire-debit.c (58754B)


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