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-credit.c (42235B)


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