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


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