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 (58110B)


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