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


      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/iterate_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/delete_kycauth_in_inconsistency.h"
     43 #include "auditor-database/get_kycauth_in_inconsistency.h"
     44 #include "auditor-database/insert_kycauth_in_inconsistency.h"
     45 #include "auditor-database/insert_misattribution_in_inconsistency.h"
     46 #include "auditor-database/insert_reserve_in_inconsistency.h"
     47 #include "auditor-database/insert_row_inconsistency.h"
     48 #include "auditor-database/insert_row_minor_inconsistencies.h"
     49 #include "auditor-database/preflight.h"
     50 #include "auditor-database/get_reserve_in_inconsistency.h"
     51 #include "auditor-database/start.h"
     52 #include "exchange-database/preflight.h"
     53 #include "exchange-database/rollback.h"
     54 #include \
     55   "exchange-database/iterate_kycauth_in_above_serial_id_by_account.h"
     56 #include \
     57   "exchange-database/iterate_reserves_in_above_serial_id_by_account.h"
     58 #include "exchange-database/start_read_only.h"
     59 
     60 /**
     61  * How much time do we allow the aggregator to lag behind?  If
     62  * wire transfers should have been made more than #GRACE_PERIOD
     63  * before, we issue warnings.
     64  */
     65 #define GRACE_PERIOD GNUNET_TIME_UNIT_HOURS
     66 
     67 /**
     68  * Maximum number of wire transfers we process per
     69  * (database) transaction.
     70  */
     71 #define MAX_PER_TRANSACTION 1024
     72 
     73 /**
     74  * How much do we allow the bank and the exchange to disagree about
     75  * timestamps? Should be sufficiently large to avoid bogus reports from deltas
     76  * created by imperfect clock synchronization and network delay.
     77  */
     78 #define TIME_TOLERANCE GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, \
     79                                                       15)
     80 
     81 
     82 /**
     83  * Run in test mode. Exit when idle instead of
     84  * going to sleep and waiting for more work.
     85  */
     86 static int test_mode;
     87 
     88 /**
     89  * Information we keep for each supported account.
     90  */
     91 struct WireAccount
     92 {
     93   /**
     94    * Accounts are kept in a DLL.
     95    */
     96   struct WireAccount *next;
     97 
     98   /**
     99    * Plugins are kept in a DLL.
    100    */
    101   struct WireAccount *prev;
    102 
    103   /**
    104    * Account details.
    105    */
    106   const struct TALER_EXCHANGEDB_AccountInfo *ai;
    107 
    108   /**
    109    * Active wire request for the transaction history.
    110    */
    111   struct TALER_BANK_CreditHistoryHandle *chh;
    112 
    113   /**
    114    * Progress point for this account.
    115    */
    116   uint64_t last_reserve_in_serial_id;
    117 
    118   /**
    119    * Initial progress point for this account.
    120    */
    121   uint64_t start_reserve_in_serial_id;
    122 
    123   /**
    124    * Progress point for this account in the kycauths_in table.
    125    */
    126   uint64_t last_kycauth_in_serial_id;
    127 
    128   /**
    129    * Where we are in the inbound transaction history.
    130    */
    131   uint64_t wire_off_in;
    132 
    133   /**
    134    * Label under which we store our pp's reserve_in_serial_id.
    135    */
    136   char *label_reserve_in_serial_id;
    137 
    138   /**
    139    * Label under which we store our pp's kycauth_in_serial_id.
    140    */
    141   char *label_kycauth_in_serial_id;
    142 
    143   /**
    144    * Label under which we store our wire_off_in.
    145    */
    146   char *label_wire_off_in;
    147 
    148 };
    149 
    150 
    151 /**
    152  * Return value from main().
    153  */
    154 static int global_ret;
    155 
    156 /**
    157  * State of the current database transaction with
    158  * the auditor DB.
    159  */
    160 static enum GNUNET_DB_QueryStatus global_qs;
    161 
    162 /**
    163  * Map with information about incoming wire transfers.
    164  * Maps hashes of the wire offsets to `struct ReserveInInfo`s.
    165  */
    166 static struct GNUNET_CONTAINER_MultiHashMap *in_map;
    167 
    168 /**
    169  * Map with information about incoming KYCAUTH wire transfers.
    170  * Maps hashes of the wire offsets to `struct KycauthInInfo`s.
    171  */
    172 static struct GNUNET_CONTAINER_MultiHashMap *kycauth_map;
    173 
    174 /**
    175  * Head of list of wire accounts we still need to look at.
    176  */
    177 static struct WireAccount *wa_head;
    178 
    179 /**
    180  * Tail of list of wire accounts we still need to look at.
    181  */
    182 static struct WireAccount *wa_tail;
    183 
    184 /**
    185  * Amount that is considered "tiny"
    186  */
    187 static struct TALER_Amount tiny_amount;
    188 
    189 /**
    190  * Total amount that was transferred too much to the exchange.
    191  */
    192 static TALER_ARL_DEF_AB (total_bad_amount_in_plus);
    193 
    194 /**
    195  * Total amount that was transferred too little to the exchange.
    196  */
    197 static TALER_ARL_DEF_AB (total_bad_amount_in_minus);
    198 
    199 /**
    200  * Total amount where the exchange has the wrong sender account
    201  * for incoming funds and may thus wire funds to the wrong
    202  * destination when closing the reserve.
    203  */
    204 static TALER_ARL_DEF_AB (total_misattribution_in);
    205 
    206 /**
    207  * Total amount credited to exchange accounts, as claimed by the exchange.
    208  * Includes #total_kycauth_revenue, so that this is the credit side of the
    209  * balance the exchange's bank account is expected to have.
    210  */
    211 static TALER_ARL_DEF_AB (total_wire_in);
    212 
    213 /**
    214  * Total amount credited to exchange accounts via KYCAUTH, as reported by
    215  * the bank.
    216  */
    217 static TALER_ARL_DEF_AB (total_kycauth_in);
    218 
    219 /**
    220  * Total amount credited to exchange accounts via KYCAUTH, as booked by the
    221  * exchange in its `kycauths_in` table.  KYCAUTH funds are not credited to
    222  * any reserve and are never paid back, so they are revenue of the exchange
    223  * operator, like wire fees and closing fees, and may be drained together
    224  * with those.  Counted towards #total_wire_in for that reason.
    225  */
    226 static TALER_ARL_DEF_AB (total_kycauth_revenue);
    227 
    228 /**
    229  * Total wire credit fees charged to the exchange account.
    230  */
    231 static TALER_ARL_DEF_AB (total_wire_credit_fees);
    232 
    233 /**
    234  * Amount of zero in our currency.
    235  */
    236 static struct TALER_Amount zero;
    237 
    238 /**
    239  * Handle to the context for interacting with the bank.
    240  */
    241 static struct GNUNET_CURL_Context *ctx;
    242 
    243 /**
    244  * Scheduler context for running the @e ctx.
    245  */
    246 static struct GNUNET_CURL_RescheduleContext *rc;
    247 
    248 /**
    249  * Should we run checks that only work for exchange-internal audits?
    250  */
    251 static int internal_checks;
    252 
    253 /**
    254  * Should we ignore if the bank does not know our bank
    255  * account?
    256  */
    257 static int ignore_account_404;
    258 
    259 /**
    260  * Database event handler to wake us up again.
    261  */
    262 static struct GNUNET_DB_EventHandler *eh;
    263 
    264 /**
    265  * The auditors's configuration.
    266  */
    267 static const struct GNUNET_CONFIGURATION_Handle *cfg;
    268 
    269 /* *****************************   Shutdown   **************************** */
    270 
    271 /**
    272  * Entry in map with wire information we expect to obtain from the
    273  * bank later.
    274  */
    275 struct ReserveInInfo
    276 {
    277 
    278   /**
    279    * Hash of expected row offset.
    280    */
    281   struct GNUNET_HashCode row_off_hash;
    282 
    283   /**
    284    * Expected details about the wire transfer.
    285    * The member "account_url" is to be allocated
    286    * at the end of this struct!
    287    */
    288   struct TALER_BANK_CreditDetails credit_details;
    289 
    290   /**
    291    * RowID in reserves_in table.
    292    */
    293   uint64_t rowid;
    294 
    295 };
    296 
    297 
    298 /**
    299  * Entry in #kycauth_map with the KYCAUTH transfer the exchange claims to
    300  * have received, waiting to be matched against the bank's credit history.
    301  */
    302 struct KycauthInInfo
    303 {
    304 
    305   /**
    306    * Hash of expected row offset.
    307    */
    308   struct GNUNET_HashCode row_off_hash;
    309 
    310   /**
    311    * RowID in the kycauths_in table.
    312    */
    313   uint64_t rowid;
    314 
    315   /**
    316    * Public key the transfer associated with the debited account.
    317    */
    318   union TALER_AccountPublicKeyP account_pub;
    319 
    320   /**
    321    * Amount the exchange says it received.
    322    */
    323   struct TALER_Amount credit;
    324 
    325   /**
    326    * When the exchange says it received the funds.
    327    */
    328   struct GNUNET_TIME_Timestamp execution_date;
    329 
    330   /**
    331    * payto://-URL of the debited account, allocated at the end of this
    332    * struct.  NULL if the exchange has no `wire_targets` row for the
    333    * account, which it only creates the first time it sees one.
    334    */
    335   struct TALER_FullPayto sender_account_details;
    336 
    337 };
    338 
    339 
    340 /**
    341  * Free entry in #in_map.
    342  *
    343  * @param cls NULL
    344  * @param key unused key
    345  * @param value the `struct ReserveInInfo` to free
    346  * @return #GNUNET_OK
    347  */
    348 static enum GNUNET_GenericReturnValue
    349 free_rii (void *cls,
    350           const struct GNUNET_HashCode *key,
    351           void *value)
    352 {
    353   struct ReserveInInfo *rii = value;
    354 
    355   (void) cls;
    356   GNUNET_assert (GNUNET_YES ==
    357                  GNUNET_CONTAINER_multihashmap_remove (in_map,
    358                                                        key,
    359                                                        rii));
    360   GNUNET_free (rii);
    361   return GNUNET_OK;
    362 }
    363 
    364 
    365 /**
    366  * Free entry in #kycauth_map.
    367  *
    368  * @param cls NULL
    369  * @param key unused key
    370  * @param value the `struct KycauthInInfo` to free
    371  * @return #GNUNET_OK
    372  */
    373 static enum GNUNET_GenericReturnValue
    374 free_kii (void *cls,
    375           const struct GNUNET_HashCode *key,
    376           void *value)
    377 {
    378   struct KycauthInInfo *kii = value;
    379 
    380   (void) cls;
    381   GNUNET_assert (GNUNET_YES ==
    382                  GNUNET_CONTAINER_multihashmap_remove (kycauth_map,
    383                                                        key,
    384                                                        kii));
    385   GNUNET_free (kii);
    386   return GNUNET_OK;
    387 }
    388 
    389 
    390 /**
    391  * Task run on shutdown.
    392  *
    393  * @param cls NULL
    394  */
    395 static void
    396 do_shutdown (void *cls)
    397 {
    398   struct WireAccount *wa;
    399 
    400   (void) cls;
    401   if (NULL != eh)
    402   {
    403     TALER_AUDITORDB_event_listen_cancel (eh);
    404     eh = NULL;
    405   }
    406   TALER_ARL_done ();
    407   if (NULL != in_map)
    408   {
    409     GNUNET_CONTAINER_multihashmap_iterate (in_map,
    410                                            &free_rii,
    411                                            NULL);
    412     GNUNET_CONTAINER_multihashmap_destroy (in_map);
    413     in_map = NULL;
    414   }
    415   if (NULL != kycauth_map)
    416   {
    417     GNUNET_CONTAINER_multihashmap_iterate (kycauth_map,
    418                                            &free_kii,
    419                                            NULL);
    420     GNUNET_CONTAINER_multihashmap_destroy (kycauth_map);
    421     kycauth_map = NULL;
    422   }
    423   while (NULL != (wa = wa_head))
    424   {
    425     if (NULL != wa->chh)
    426     {
    427       TALER_BANK_credit_history_cancel (wa->chh);
    428       wa->chh = NULL;
    429     }
    430     GNUNET_CONTAINER_DLL_remove (wa_head,
    431                                  wa_tail,
    432                                  wa);
    433     GNUNET_free (wa->label_reserve_in_serial_id);
    434     GNUNET_free (wa->label_kycauth_in_serial_id);
    435     GNUNET_free (wa->label_wire_off_in);
    436     GNUNET_free (wa);
    437   }
    438   if (NULL != ctx)
    439   {
    440     GNUNET_CURL_fini (ctx);
    441     ctx = NULL;
    442   }
    443   if (NULL != rc)
    444   {
    445     GNUNET_CURL_gnunet_rc_destroy (rc);
    446     rc = NULL;
    447   }
    448   TALER_EXCHANGEDB_unload_accounts ();
    449   TALER_ARL_cfg = NULL;
    450 }
    451 
    452 
    453 /**
    454  * Start the database transactions and begin the audit.
    455  *
    456  * @return transaction status code
    457  */
    458 static enum GNUNET_DB_QueryStatus
    459 begin_transaction (void);
    460 
    461 
    462 /**
    463  * Rollback the current transaction, reset our state and try
    464  * again (we had a serialization error).
    465  */
    466 static void
    467 rollback_and_reset (void)
    468 {
    469   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    470               "Serialization issue, trying again\n");
    471   TALER_AUDITORDB_rollback (TALER_ARL_adb);
    472   for (unsigned int max_retries = 3; max_retries>0; max_retries--)
    473   {
    474     enum GNUNET_DB_QueryStatus qs;
    475 
    476     if (NULL != in_map)
    477     {
    478       GNUNET_CONTAINER_multihashmap_iterate (in_map,
    479                                              &free_rii,
    480                                              NULL);
    481       GNUNET_CONTAINER_multihashmap_destroy (in_map);
    482       in_map = NULL;
    483     }
    484     if (NULL != kycauth_map)
    485     {
    486       GNUNET_CONTAINER_multihashmap_iterate (kycauth_map,
    487                                              &free_kii,
    488                                              NULL);
    489       GNUNET_CONTAINER_multihashmap_destroy (kycauth_map);
    490       kycauth_map = NULL;
    491     }
    492     qs = begin_transaction ();
    493     if (GNUNET_DB_STATUS_HARD_ERROR == qs)
    494       break;
    495   }
    496   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    497               "Hard database error, terminating\n");
    498   GNUNET_SCHEDULER_shutdown ();
    499 }
    500 
    501 
    502 /**
    503  * Commit the transaction, checkpointing our progress in the auditor DB.
    504  *
    505  * @param qs transaction status so far
    506  */
    507 static void
    508 commit (enum GNUNET_DB_QueryStatus qs)
    509 {
    510   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    511               "Transaction logic ended with status %d\n",
    512               qs);
    513   TALER_EXCHANGEDB_rollback (TALER_ARL_edb);
    514   if (qs < 0)
    515     goto handle_db_error;
    516   qs = TALER_AUDITORDB_insert_balance (
    517     TALER_ARL_adb,
    518     TALER_ARL_SET_AB (total_wire_in),
    519     TALER_ARL_SET_AB (total_kycauth_in),
    520     TALER_ARL_SET_AB (total_kycauth_revenue),
    521     TALER_ARL_SET_AB (total_wire_credit_fees),
    522     TALER_ARL_SET_AB (total_bad_amount_in_plus),
    523     TALER_ARL_SET_AB (total_bad_amount_in_minus),
    524     TALER_ARL_SET_AB (total_misattribution_in),
    525     NULL);
    526   if (0 > qs)
    527     goto handle_db_error;
    528   for (struct WireAccount *wa = wa_head;
    529        NULL != wa;
    530        wa = wa->next)
    531   {
    532     qs = TALER_AUDITORDB_insert_auditor_progress (
    533       TALER_ARL_adb,
    534       wa->label_reserve_in_serial_id,
    535       wa->last_reserve_in_serial_id,
    536       wa->label_kycauth_in_serial_id,
    537       wa->last_kycauth_in_serial_id,
    538       wa->label_wire_off_in,
    539       wa->wire_off_in,
    540       NULL);
    541     if (0 > qs)
    542       goto handle_db_error;
    543     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    544                 "Transaction ends at %s=%llu for account `%s'\n",
    545                 wa->label_reserve_in_serial_id,
    546                 (unsigned long long) wa->last_reserve_in_serial_id,
    547                 wa->ai->section_name);
    548   }
    549   qs = TALER_AUDITORDB_commit (TALER_ARL_adb);
    550   if (0 > qs)
    551   {
    552     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    553     goto handle_db_error;
    554   }
    555   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    556               "Transaction concluded!\n");
    557   if (1 == test_mode)
    558     GNUNET_SCHEDULER_shutdown ();
    559   return;
    560 handle_db_error:
    561   rollback_and_reset ();
    562 }
    563 
    564 
    565 /**
    566  * Conclude the credit history check by logging entries that
    567  * were not found and freeing resources. Then move on to
    568  * processing debits.
    569  */
    570 static void
    571 conclude_credit_history (void)
    572 {
    573   if (NULL != in_map)
    574   {
    575     GNUNET_assert (0 ==
    576                    GNUNET_CONTAINER_multihashmap_size (in_map));
    577     GNUNET_CONTAINER_multihashmap_destroy (in_map);
    578     in_map = NULL;
    579   }
    580   if (NULL != kycauth_map)
    581   {
    582     GNUNET_assert (0 ==
    583                    GNUNET_CONTAINER_multihashmap_size (kycauth_map));
    584     GNUNET_CONTAINER_multihashmap_destroy (kycauth_map);
    585     kycauth_map = NULL;
    586   }
    587   commit (global_qs);
    588 }
    589 
    590 
    591 /**
    592  * Check if the given wire transfers are equivalent.
    593  *
    594  * @param credit amount that was received
    595  * @param credit2 2nd amount that was received
    596  * @param reserve_pub public key of the reserve (also the WTID)
    597  * @param reserve_pub2 2nd public key of the reserve (also the WTID)
    598  * @param sender_account_details payto://-URL of the sender's bank account
    599  * @param sender_account_details2 2nd payto://-URL of the sender's bank account
    600  * @param execution_date when did we receive the funds
    601  * @param execution_date2 2nd when did we receive the funds
    602  * @return #GNUNET_YES if so,
    603  *         #GNUNET_NO if not
    604  *         #GNUNET_SYSERR on internal error
    605  */
    606 static enum GNUNET_GenericReturnValue
    607 check_equality (const struct TALER_Amount *credit,
    608                 const struct TALER_Amount *credit2,
    609                 const struct TALER_ReservePublicKeyP *reserve_pub,
    610                 const struct TALER_ReservePublicKeyP *reserve_pub2,
    611                 const struct TALER_FullPayto sender_account_details,
    612                 const struct TALER_FullPayto sender_account_details2,
    613                 struct GNUNET_TIME_Timestamp execution_date,
    614                 struct GNUNET_TIME_Timestamp execution_date2)
    615 {
    616   if (0 != TALER_amount_cmp (credit,
    617                              credit2))
    618     return GNUNET_NO;
    619   if (0 != GNUNET_memcmp (reserve_pub,
    620                           reserve_pub2))
    621     return GNUNET_NO;
    622   {
    623     struct TALER_NormalizedPayto np;
    624     struct TALER_NormalizedPayto np2;
    625     bool fail;
    626 
    627     np = TALER_payto_normalize (sender_account_details);
    628     np2 = TALER_payto_normalize (sender_account_details2);
    629     fail = (0 != TALER_normalized_payto_cmp (np,
    630                                              np2));
    631     GNUNET_free (np.normalized_payto);
    632     GNUNET_free (np2.normalized_payto);
    633     if (fail)
    634       return GNUNET_NO;
    635   }
    636   if (GNUNET_TIME_timestamp_cmp (execution_date,
    637                                  !=,
    638                                  execution_date2))
    639     return GNUNET_NO;
    640   return GNUNET_YES;
    641 }
    642 
    643 
    644 /**
    645  * Check if two payto://-URLs designate the same bank account.
    646  *
    647  * @param a first account, may be NULL
    648  * @param b second account, may be NULL
    649  * @return true if they match, or if either side is unknown
    650  */
    651 static bool
    652 same_account (const struct TALER_FullPayto a,
    653               const struct TALER_FullPayto b)
    654 {
    655   struct TALER_NormalizedPayto na;
    656   struct TALER_NormalizedPayto nb;
    657   bool ret;
    658 
    659   /* The exchange only creates the wire_targets row for an account the
    660      first time it sees one, so it legitimately may not know the debited
    661      account of a KYCAUTH transfer.  Do not turn that into a finding. */
    662   if ( (NULL == a.full_payto) ||
    663        (NULL == b.full_payto) )
    664     return true;
    665   na = TALER_payto_normalize (a);
    666   nb = TALER_payto_normalize (b);
    667   ret = (0 == TALER_normalized_payto_cmp (na,
    668                                           nb));
    669   GNUNET_free (na.normalized_payto);
    670   GNUNET_free (nb.normalized_payto);
    671   return ret;
    672 }
    673 
    674 
    675 /**
    676  * Check if the two views of a KYCAUTH wire transfer agree in every
    677  * respect.
    678  *
    679  * @param credit amount of the first view
    680  * @param credit2 amount of the second view
    681  * @param account_pub account key of the first view
    682  * @param account_pub2 account key of the second view
    683  * @param sender_account_details debited account of the first view
    684  * @param sender_account_details2 debited account of the second view
    685  * @param execution_date execution time of the first view
    686  * @param execution_date2 execution time of the second view
    687  * @return true if the two agree
    688  */
    689 static bool
    690 check_kycauth_equality (
    691   const struct TALER_Amount *credit,
    692   const struct TALER_Amount *credit2,
    693   const union TALER_AccountPublicKeyP *account_pub,
    694   const union TALER_AccountPublicKeyP *account_pub2,
    695   const struct TALER_FullPayto sender_account_details,
    696   const struct TALER_FullPayto sender_account_details2,
    697   struct GNUNET_TIME_Timestamp execution_date,
    698   struct GNUNET_TIME_Timestamp execution_date2)
    699 {
    700   return (0 == TALER_amount_cmp (credit,
    701                                  credit2)) &&
    702          (0 == GNUNET_memcmp (account_pub,
    703                               account_pub2)) &&
    704          same_account (sender_account_details,
    705                        sender_account_details2) &&
    706          GNUNET_TIME_timestamp_cmp (execution_date,
    707                                     ==,
    708                                     execution_date2);
    709 }
    710 
    711 
    712 /**
    713  * Function called with details about incoming wire transfers that
    714  * established an account key for KYC authentication, as claimed by the
    715  * exchange DB.
    716  *
    717  * Such a transfer is not credited to a reserve and is never paid back, so
    718  * the money is simply revenue of the exchange operator.  We book it as
    719  * such: it is part of the balance the exchange's bank account is expected
    720  * to have, and the exchange may drain it together with its fee income.
    721  *
    722  * The row is then remembered in #kycauth_map, so that the walk over the
    723  * bank's credit history that follows can confirm the transfer actually
    724  * happened; whatever is left in that map afterwards is money the exchange
    725  * claims to have received and the bank knows nothing about.
    726  *
    727  * @param cls a `struct WireAccount` we are processing
    728  * @param rowid unique serial ID for the entry in our DB
    729  * @param account_pub public key the transfer associated with the account
    730  * @param credit amount that was received
    731  * @param sender_account_details payto://-URL of the sender's bank account,
    732  *        NULL if the exchange has no `wire_targets` row for it
    733  * @param wire_reference unique identifier for the wire transfer
    734  * @param execution_date when did we receive the funds
    735  * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
    736  */
    737 static enum GNUNET_GenericReturnValue
    738 kycauth_in_cb (void *cls,
    739                uint64_t rowid,
    740                const union TALER_AccountPublicKeyP *account_pub,
    741                const struct TALER_Amount *credit,
    742                const struct TALER_FullPayto sender_account_details,
    743                uint64_t wire_reference,
    744                struct GNUNET_TIME_Timestamp execution_date)
    745 {
    746   struct WireAccount *wa = cls;
    747   struct KycauthInInfo *kii;
    748   size_t slen;
    749 
    750   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    751               "Analyzing exchange KYCAUTH IN (%llu) at %s of %s\n",
    752               (unsigned long long) rowid,
    753               GNUNET_TIME_timestamp2s (execution_date),
    754               TALER_amount2s (credit));
    755   /* Advance the cursor up front: every path below has fully accounted for
    756      this row, and reading it a second time would count the revenue twice. */
    757   wa->last_kycauth_in_serial_id = rowid + 1;
    758   TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_kycauth_revenue),
    759                         &TALER_ARL_USE_AB (total_kycauth_revenue),
    760                         credit);
    761   TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_wire_in),
    762                         &TALER_ARL_USE_AB (total_wire_in),
    763                         credit);
    764   {
    765     enum GNUNET_DB_QueryStatus qs;
    766     struct TALER_AUDITORDB_KycauthInInconsistency dc;
    767 
    768     qs = TALER_AUDITORDB_get_kycauth_in_inconsistency (
    769       TALER_ARL_adb,
    770       wire_reference,
    771       &dc);
    772     switch (qs)
    773     {
    774     case GNUNET_DB_STATUS_HARD_ERROR:
    775     case GNUNET_DB_STATUS_SOFT_ERROR:
    776       global_qs = qs;
    777       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    778       return GNUNET_SYSERR;
    779     case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    780       break;
    781     case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    782       {
    783         bool healed = false;
    784 
    785         if (TALER_amount_is_zero (&dc.amount_exchange_expected))
    786         {
    787           /* An earlier run saw this transfer at the bank while the
    788              exchange had not booked it yet.  If the two now agree, that
    789              finding was a transient and we retract it. */
    790           healed = check_kycauth_equality (&dc.amount_wired,
    791                                            credit,
    792                                            &dc.account_pub,
    793                                            account_pub,
    794                                            dc.account,
    795                                            sender_account_details,
    796                                            GNUNET_TIME_absolute_to_timestamp (
    797                                              dc.timestamp),
    798                                            execution_date);
    799         }
    800         GNUNET_free (dc.account.full_payto);
    801         GNUNET_free (dc.diagnostic);
    802         if (healed)
    803         {
    804           qs = TALER_AUDITORDB_delete_kycauth_in_inconsistency (
    805             TALER_ARL_adb,
    806             dc.serial_id);
    807           if (qs < 0)
    808           {
    809             global_qs = qs;
    810             GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    811             return GNUNET_SYSERR;
    812           }
    813           /* We are retracting the finding, so the excess credit we booked
    814              when we made it has to go as well -- otherwise a transfer the
    815              exchange merely booked late inflates this balance forever. */
    816           TALER_ARL_amount_subtract (
    817             &TALER_ARL_USE_AB (total_bad_amount_in_plus),
    818             &TALER_ARL_USE_AB (total_bad_amount_in_plus),
    819             &dc.amount_wired);
    820           /* The bank side of this transfer was accounted for when the
    821              finding was made, so do not expect it again. */
    822           return GNUNET_OK;
    823         }
    824         break;
    825       }
    826     }
    827   }
    828   slen = (NULL == sender_account_details.full_payto)
    829     ? 0
    830     : strlen (sender_account_details.full_payto) + 1;
    831   kii = GNUNET_malloc (sizeof (struct KycauthInInfo) + slen);
    832   kii->rowid = rowid;
    833   kii->account_pub = *account_pub;
    834   kii->credit = *credit;
    835   kii->execution_date = execution_date;
    836   if (0 != slen)
    837   {
    838     kii->sender_account_details.full_payto = (char *) &kii[1];
    839     GNUNET_memcpy (&kii[1],
    840                    sender_account_details.full_payto,
    841                    slen);
    842   }
    843   GNUNET_CRYPTO_hash (&wire_reference,
    844                       sizeof (uint64_t),
    845                       &kii->row_off_hash);
    846   if (GNUNET_OK !=
    847       GNUNET_CONTAINER_multihashmap_put (
    848         kycauth_map,
    849         &kii->row_off_hash,
    850         kii,
    851         GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
    852   {
    853     struct TALER_AUDITORDB_RowInconsistency ri = {
    854       .row_id = rowid,
    855       .row_table = (char *) "kycauths_in",
    856       .diagnostic = (char *) "duplicate wire offset"
    857     };
    858     enum GNUNET_DB_QueryStatus qs;
    859 
    860     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    861                 "Duplicate wire offset\n");
    862     qs = TALER_AUDITORDB_insert_row_inconsistency (
    863       TALER_ARL_adb,
    864       &ri);
    865     GNUNET_free (kii);
    866     if (qs < 0)
    867     {
    868       global_qs = qs;
    869       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    870       return GNUNET_SYSERR;
    871     }
    872   }
    873   return GNUNET_OK;
    874 }
    875 
    876 
    877 /**
    878  * Function called with details about incoming wire transfers
    879  * as claimed by the exchange DB.
    880  *
    881  * @param cls a `struct WireAccount` we are processing
    882  * @param rowid unique serial ID for the entry in our DB
    883  * @param reserve_pub public key of the reserve (also the WTID)
    884  * @param credit amount that was received
    885  * @param sender_account_details payto://-URL of the sender's bank account
    886  * @param wire_reference unique identifier for the wire transfer
    887  * @param execution_date when did we receive the funds
    888  * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
    889  */
    890 static enum GNUNET_GenericReturnValue
    891 reserve_in_cb (void *cls,
    892                uint64_t rowid,
    893                const struct TALER_ReservePublicKeyP *reserve_pub,
    894                const struct TALER_Amount *credit,
    895                const struct TALER_FullPayto sender_account_details,
    896                uint64_t wire_reference,
    897                struct GNUNET_TIME_Timestamp execution_date)
    898 {
    899   struct WireAccount *wa = cls;
    900   struct ReserveInInfo *rii;
    901   size_t slen;
    902 
    903   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    904               "Analyzing exchange wire IN (%llu) at %s of %s with reserve_pub %s\n",
    905               (unsigned long long) rowid,
    906               GNUNET_TIME_timestamp2s (execution_date),
    907               TALER_amount2s (credit),
    908               TALER_B2S (reserve_pub));
    909   /* Advance the cursor up front: every path below has fully accounted for
    910      this row, and reading it a second time would count the credit twice. */
    911   wa->last_reserve_in_serial_id = rowid + 1;
    912   TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_wire_in),
    913                         &TALER_ARL_USE_AB (total_wire_in),
    914                         credit);
    915   {
    916     enum GNUNET_DB_QueryStatus qs;
    917     struct TALER_AUDITORDB_ReserveInInconsistency dc;
    918 
    919     qs = TALER_AUDITORDB_get_reserve_in_inconsistency (
    920       TALER_ARL_adb,
    921       wire_reference,
    922       &dc);
    923     switch (qs)
    924     {
    925     case GNUNET_DB_STATUS_HARD_ERROR:
    926     case GNUNET_DB_STATUS_SOFT_ERROR:
    927       global_qs = qs;
    928       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    929       return GNUNET_SYSERR;
    930     case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    931       break;
    932     case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    933       {
    934         enum GNUNET_GenericReturnValue ret = GNUNET_NO;
    935 
    936         if (TALER_amount_is_zero (&dc.amount_exchange_expected))
    937         {
    938           /* database entry indicates unmatched transaction */
    939           ret = check_equality (&dc.amount_wired,
    940                                 credit,
    941                                 &dc.reserve_pub,
    942                                 reserve_pub,
    943                                 dc.account,
    944                                 sender_account_details,
    945                                 GNUNET_TIME_absolute_to_timestamp (dc.timestamp)
    946                                 ,
    947                                 execution_date);
    948         }
    949         GNUNET_free (dc.account.full_payto);
    950         GNUNET_free (dc.diagnostic);
    951         if (GNUNET_SYSERR == ret)
    952           return GNUNET_SYSERR;
    953         if (GNUNET_YES == ret)
    954         {
    955           qs = TALER_AUDITORDB_delete_reserve_in_inconsistency (
    956             TALER_ARL_adb,
    957             dc.serial_id);
    958           if (qs < 0)
    959           {
    960             global_qs = qs;
    961             GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    962             return GNUNET_SYSERR;
    963           }
    964           /* We are retracting the finding, so the excess credit we booked
    965              when we made it has to go as well -- otherwise a transfer the
    966              exchange merely booked late inflates this balance forever. */
    967           TALER_ARL_amount_subtract (
    968             &TALER_ARL_USE_AB (total_bad_amount_in_plus),
    969             &TALER_ARL_USE_AB (total_bad_amount_in_plus),
    970             &dc.amount_wired);
    971           return GNUNET_OK;
    972         }
    973         break;
    974       }
    975     }
    976   }
    977   slen = strlen (sender_account_details.full_payto) + 1;
    978   rii = GNUNET_malloc (sizeof (struct ReserveInInfo) + slen);
    979   rii->rowid = rowid;
    980   rii->credit_details.type = TALER_BANK_CT_RESERVE;
    981   rii->credit_details.amount = *credit;
    982   rii->credit_details.execution_date = execution_date;
    983   rii->credit_details.details.reserve.reserve_pub = *reserve_pub;
    984   rii->credit_details.debit_account_uri.full_payto = (char *) &rii[1];
    985   GNUNET_memcpy (&rii[1],
    986                  sender_account_details.full_payto,
    987                  slen);
    988   GNUNET_CRYPTO_hash (&wire_reference,
    989                       sizeof (uint64_t),
    990                       &rii->row_off_hash);
    991   if (GNUNET_OK !=
    992       GNUNET_CONTAINER_multihashmap_put (in_map,
    993                                          &rii->row_off_hash,
    994                                          rii,
    995                                          GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
    996   {
    997     struct TALER_AUDITORDB_RowInconsistency ri = {
    998       .row_id = rowid,
    999       .row_table = (char *) "reserves_in",
   1000       .diagnostic = (char *) "duplicate wire offset"
   1001     };
   1002     enum GNUNET_DB_QueryStatus qs;
   1003 
   1004     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   1005                 "Duplicate wire offset\n");
   1006     qs = TALER_AUDITORDB_insert_row_inconsistency (
   1007       TALER_ARL_adb,
   1008       &ri);
   1009     GNUNET_free (rii);
   1010     if (qs < 0)
   1011     {
   1012       global_qs = qs;
   1013       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
   1014       return GNUNET_SYSERR;
   1015     }
   1016     return GNUNET_OK;
   1017   }
   1018   return GNUNET_OK;
   1019 }
   1020 
   1021 
   1022 /**
   1023  * Complain that we failed to match an entry from #in_map.
   1024  *
   1025  * @param cls a `struct WireAccount`
   1026  * @param key unused key
   1027  * @param value the `struct ReserveInInfo` to free
   1028  * @return #GNUNET_OK
   1029  */
   1030 static enum GNUNET_GenericReturnValue
   1031 complain_in_not_found (void *cls,
   1032                        const struct GNUNET_HashCode *key,
   1033                        void *value)
   1034 {
   1035   struct WireAccount *wa = cls;
   1036   struct ReserveInInfo *rii = value;
   1037   enum GNUNET_DB_QueryStatus qs;
   1038   struct TALER_AUDITORDB_ReserveInInconsistency riiDb = {
   1039     .bank_row_id = rii->rowid,
   1040     .diagnostic = (char *)
   1041                   "incoming wire transfer claimed by exchange not found",
   1042     .account = wa->ai->payto_uri,
   1043     .amount_exchange_expected = rii->credit_details.amount,
   1044     .amount_wired = zero,
   1045     .reserve_pub = rii->credit_details.details.reserve.reserve_pub,
   1046     .timestamp = rii->credit_details.execution_date.abs_time
   1047   };
   1048 
   1049   (void) key;
   1050   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   1051               "Incoming wire transfer #%llu claimed by exchange not found\n",
   1052               (unsigned long long) rii->rowid);
   1053   GNUNET_assert (TALER_BANK_CT_RESERVE ==
   1054                  rii->credit_details.type);
   1055   qs = TALER_AUDITORDB_insert_reserve_in_inconsistency (
   1056     TALER_ARL_adb,
   1057     &riiDb);
   1058   if (qs < 0)
   1059   {
   1060     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
   1061     global_qs = qs;
   1062     return GNUNET_SYSERR;
   1063   }
   1064   TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_in_minus),
   1065                         &TALER_ARL_USE_AB (total_bad_amount_in_minus),
   1066                         &rii->credit_details.amount);
   1067   return GNUNET_OK;
   1068 }
   1069 
   1070 
   1071 /**
   1072  * Complain that we failed to match an entry from #kycauth_map: the
   1073  * exchange booked a KYCAUTH transfer the bank never made.  Beyond the
   1074  * missing money, this means the exchange let an account authenticate
   1075  * without anyone ever having proven that they control it.
   1076  *
   1077  * @param cls a `struct WireAccount`
   1078  * @param key unused key
   1079  * @param value the `struct KycauthInInfo` we could not match
   1080  * @return #GNUNET_OK
   1081  */
   1082 static enum GNUNET_GenericReturnValue
   1083 complain_kycauth_in_not_found (void *cls,
   1084                                const struct GNUNET_HashCode *key,
   1085                                void *value)
   1086 {
   1087   struct WireAccount *wa = cls;
   1088   struct KycauthInInfo *kii = value;
   1089   enum GNUNET_DB_QueryStatus qs;
   1090   struct TALER_AUDITORDB_KycauthInInconsistency kiiDb = {
   1091     .bank_row_id = kii->rowid,
   1092     .diagnostic = (char *)
   1093                   "KYCAUTH wire transfer claimed by exchange not found",
   1094     .account = (NULL == kii->sender_account_details.full_payto)
   1095                ? wa->ai->payto_uri
   1096                : kii->sender_account_details,
   1097     .amount_exchange_expected = kii->credit,
   1098     .amount_wired = zero,
   1099     .account_pub = kii->account_pub,
   1100     .timestamp = kii->execution_date.abs_time
   1101   };
   1102 
   1103   (void) key;
   1104   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   1105               "KYCAUTH wire transfer #%llu claimed by exchange not found\n",
   1106               (unsigned long long) kii->rowid);
   1107   qs = TALER_AUDITORDB_insert_kycauth_in_inconsistency (
   1108     TALER_ARL_adb,
   1109     &kiiDb);
   1110   if (qs < 0)
   1111   {
   1112     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
   1113     global_qs = qs;
   1114     return GNUNET_SYSERR;
   1115   }
   1116   TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_in_minus),
   1117                         &TALER_ARL_USE_AB (total_bad_amount_in_minus),
   1118                         &kii->credit);
   1119   return GNUNET_OK;
   1120 }
   1121 
   1122 
   1123 /**
   1124  * Start processing the next wire account.
   1125  * Shuts down if we are done.
   1126  *
   1127  * @param cls `struct WireAccount` with a wire account list to process
   1128  */
   1129 static void
   1130 process_credits (void *cls);
   1131 
   1132 
   1133 /**
   1134  * We got all of the incoming transactions for @a wa,
   1135  * finish processing the account.
   1136  *
   1137  * @param[in,out] wa wire account to process
   1138  */
   1139 static void
   1140 conclude_account (struct WireAccount *wa)
   1141 {
   1142   GNUNET_assert (NULL == wa->chh);
   1143   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1144               "Reconciling CREDIT processing of account `%s'\n",
   1145               wa->ai->section_name);
   1146   if (NULL != in_map)
   1147   {
   1148     GNUNET_CONTAINER_multihashmap_iterate (in_map,
   1149                                            &complain_in_not_found,
   1150                                            wa);
   1151     /* clean up before 2nd phase */
   1152     GNUNET_CONTAINER_multihashmap_iterate (in_map,
   1153                                            &free_rii,
   1154                                            NULL);
   1155     if (global_qs < 0)
   1156     {
   1157       commit (global_qs);
   1158       return;
   1159     }
   1160   }
   1161   if (NULL != kycauth_map)
   1162   {
   1163     GNUNET_CONTAINER_multihashmap_iterate (kycauth_map,
   1164                                            &complain_kycauth_in_not_found,
   1165                                            wa);
   1166     /* clean up before 2nd phase */
   1167     GNUNET_CONTAINER_multihashmap_iterate (kycauth_map,
   1168                                            &free_kii,
   1169                                            NULL);
   1170     if (global_qs < 0)
   1171     {
   1172       commit (global_qs);
   1173       return;
   1174     }
   1175   }
   1176   process_credits (wa->next);
   1177 }
   1178 
   1179 
   1180 /**
   1181  * Analyze a KYCAUTH credit the bank reports against what the exchange
   1182  * booked in its `kycauths_in` table (collected in #kycauth_map).
   1183  *
   1184  * @param[in,out] wa account that received the transfer
   1185  * @param credit_details transfer details as reported by the bank
   1186  * @return true on success, false to stop loop at this point
   1187  */
   1188 static bool
   1189 analyze_kycauth_credit (
   1190   struct WireAccount *wa,
   1191   const struct TALER_BANK_CreditDetails *credit_details)
   1192 {
   1193   struct KycauthInInfo *kii;
   1194   struct GNUNET_HashCode key;
   1195 
   1196   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1197               "Analyzing bank KYCAUTH CREDIT #%llu at %s of %s\n",
   1198               (unsigned long long) credit_details->serial_id,
   1199               GNUNET_TIME_timestamp2s (credit_details->execution_date),
   1200               TALER_amount2s (&credit_details->amount));
   1201   TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_kycauth_in),
   1202                         &TALER_ARL_USE_AB (total_kycauth_in),
   1203                         &credit_details->amount);
   1204   /* the bank charges its credit fee on this transfer just like on
   1205      any other, so it must be part of the total either way */
   1206   TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_wire_credit_fees),
   1207                         &TALER_ARL_USE_AB (total_wire_credit_fees),
   1208                         &credit_details->credit_fee);
   1209   GNUNET_CRYPTO_hash (&credit_details->serial_id,
   1210                       sizeof (credit_details->serial_id),
   1211                       &key);
   1212   kii = GNUNET_CONTAINER_multihashmap_get (kycauth_map,
   1213                                            &key);
   1214   if (NULL == kii)
   1215   {
   1216     struct TALER_AUDITORDB_KycauthInInconsistency dc = {
   1217       .bank_row_id = credit_details->serial_id,
   1218       .amount_exchange_expected = zero,
   1219       .amount_wired = credit_details->amount,
   1220       .account_pub = credit_details->details.kycauth.account_pub,
   1221       .timestamp = credit_details->execution_date.abs_time,
   1222       .account = credit_details->debit_account_uri,
   1223       .diagnostic = (char *) "unknown to exchange"
   1224     };
   1225     enum GNUNET_DB_QueryStatus qs;
   1226 
   1227     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   1228                 "Failed to find KYCAUTH transfer at `%s' in exchange database.\n",
   1229                 GNUNET_TIME_timestamp2s (credit_details->execution_date));
   1230     qs = TALER_AUDITORDB_insert_kycauth_in_inconsistency (TALER_ARL_adb,
   1231                                                           &dc);
   1232     if (qs <= 0)
   1233     {
   1234       global_qs = qs;
   1235       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
   1236       return false;
   1237     }
   1238     TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_in_plus),
   1239                           &TALER_ARL_USE_AB (total_bad_amount_in_plus),
   1240                           &credit_details->amount);
   1241     /* We have fully processed this credit: the finding is on file, and
   1242        kycauth_in_cb() retracts it if the exchange books the transfer
   1243        later.  Looking at it again would only duplicate the finding. */
   1244     wa->wire_off_in = credit_details->serial_id;
   1245     return true;
   1246   }
   1247 
   1248   /* Update offset */
   1249   wa->wire_off_in = credit_details->serial_id;
   1250   if (0 != TALER_amount_cmp (&kii->credit,
   1251                              &credit_details->amount))
   1252   {
   1253     struct TALER_AUDITORDB_KycauthInInconsistency dc = {
   1254       .bank_row_id = credit_details->serial_id,
   1255       .amount_exchange_expected = kii->credit,
   1256       .amount_wired = credit_details->amount,
   1257       .account_pub = kii->account_pub,
   1258       .timestamp = kii->execution_date.abs_time,
   1259       .account = wa->ai->payto_uri,
   1260       .diagnostic = (char *) "wire amount does not match"
   1261     };
   1262     enum GNUNET_DB_QueryStatus qs;
   1263 
   1264     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   1265                 "KYCAUTH transfer amount differs\n");
   1266     qs = TALER_AUDITORDB_insert_kycauth_in_inconsistency (
   1267       TALER_ARL_adb,
   1268       &dc);
   1269     if (qs <= 0)
   1270     {
   1271       global_qs = qs;
   1272       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
   1273       return false;
   1274     }
   1275     if (0 < TALER_amount_cmp (&credit_details->amount,
   1276                               &kii->credit))
   1277     {
   1278       struct TALER_Amount delta;
   1279 
   1280       TALER_ARL_amount_subtract (&delta,
   1281                                  &credit_details->amount,
   1282                                  &kii->credit);
   1283       TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_in_plus),
   1284                             &TALER_ARL_USE_AB (total_bad_amount_in_plus),
   1285                             &delta);
   1286     }
   1287     else
   1288     {
   1289       struct TALER_Amount delta;
   1290 
   1291       TALER_ARL_amount_subtract (&delta,
   1292                                  &kii->credit,
   1293                                  &credit_details->amount);
   1294       TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_in_minus),
   1295                             &TALER_ARL_USE_AB (total_bad_amount_in_minus),
   1296                             &delta);
   1297     }
   1298   }
   1299   if (0 != GNUNET_memcmp (&kii->account_pub,
   1300                           &credit_details->details.kycauth.account_pub))
   1301   {
   1302     /* No money is missing, but the exchange associated a different key
   1303        with the account than the one the sender asked for: the wrong party
   1304        can now authenticate as the owner of that bank account. */
   1305     struct TALER_AUDITORDB_KycauthInInconsistency dc = {
   1306       .bank_row_id = credit_details->serial_id,
   1307       .amount_exchange_expected = kii->credit,
   1308       .amount_wired = credit_details->amount,
   1309       .account_pub = kii->account_pub,
   1310       .timestamp = kii->execution_date.abs_time,
   1311       .account = wa->ai->payto_uri,
   1312       .diagnostic = (char *) "account public key does not match"
   1313     };
   1314     enum GNUNET_DB_QueryStatus qs;
   1315 
   1316     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   1317                 "KYCAUTH account public key differs\n");
   1318     qs = TALER_AUDITORDB_insert_kycauth_in_inconsistency (
   1319       TALER_ARL_adb,
   1320       &dc);
   1321     if (qs <= 0)
   1322     {
   1323       global_qs = qs;
   1324       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
   1325       return false;
   1326     }
   1327   }
   1328   if (! same_account (kii->sender_account_details,
   1329                       credit_details->debit_account_uri))
   1330   {
   1331     /* Again no money is missing: the exchange authorised the wrong bank
   1332        account.  There is no reserve to close towards it either, so this
   1333        does not feed into #total_misattribution_in. */
   1334     struct TALER_AUDITORDB_KycauthInInconsistency dc = {
   1335       .bank_row_id = credit_details->serial_id,
   1336       .amount_exchange_expected = kii->credit,
   1337       .amount_wired = credit_details->amount,
   1338       .account_pub = kii->account_pub,
   1339       .timestamp = kii->execution_date.abs_time,
   1340       .account = credit_details->debit_account_uri,
   1341       .diagnostic = (char *) "debited account does not match"
   1342     };
   1343     enum GNUNET_DB_QueryStatus qs;
   1344 
   1345     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   1346                 "Origin bank account of KYCAUTH transfer differs\n");
   1347     qs = TALER_AUDITORDB_insert_kycauth_in_inconsistency (
   1348       TALER_ARL_adb,
   1349       &dc);
   1350     if (qs <= 0)
   1351     {
   1352       global_qs = qs;
   1353       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
   1354       return false;
   1355     }
   1356   }
   1357   if (GNUNET_TIME_timestamp_cmp (credit_details->execution_date,
   1358                                  !=,
   1359                                  kii->execution_date))
   1360   {
   1361     struct TALER_AUDITORDB_RowMinorInconsistencies rmi = {
   1362       .problem_row = kii->rowid,
   1363       .diagnostic = (char *) "execution date mismatch",
   1364       .row_table = (char *) "kycauths_in"
   1365     };
   1366     enum GNUNET_DB_QueryStatus qs;
   1367 
   1368     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   1369                 "Execution date of KYCAUTH transfer differs\n");
   1370     qs = TALER_AUDITORDB_insert_row_minor_inconsistencies (
   1371       TALER_ARL_adb,
   1372       &rmi);
   1373     if (qs < 0)
   1374     {
   1375       global_qs = qs;
   1376       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
   1377       return false;
   1378     }
   1379   }
   1380   GNUNET_assert (GNUNET_OK ==
   1381                  free_kii (NULL,
   1382                            &key,
   1383                            kii));
   1384   return true;
   1385 }
   1386 
   1387 
   1388 /**
   1389  * Analyze credit transaction @a details into @a wa.
   1390  *
   1391  * @param[in,out] wa account that received the transfer
   1392  * @param credit_details transfer details
   1393  * @return true on success, false to stop loop at this point
   1394  */
   1395 static bool
   1396 analyze_credit (
   1397   struct WireAccount *wa,
   1398   const struct TALER_BANK_CreditDetails *credit_details)
   1399 {
   1400   struct ReserveInInfo *rii;
   1401   struct GNUNET_HashCode key;
   1402 
   1403   switch (credit_details->type)
   1404   {
   1405   case TALER_BANK_CT_RESERVE:
   1406     break;
   1407   case TALER_BANK_CT_KYCAUTH:
   1408     return analyze_kycauth_credit (wa,
   1409                                    credit_details);
   1410   case TALER_BANK_CT_WAD:
   1411     GNUNET_break (0); /* FIXME: Wad not yet supported */
   1412     return false;
   1413   }
   1414   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1415               "Analyzing bank CREDIT #%llu at %s of %s with Reserve-pub %s\n",
   1416               (unsigned long long) credit_details->serial_id,
   1417               GNUNET_TIME_timestamp2s (credit_details->execution_date),
   1418               TALER_amount2s (&credit_details->amount),
   1419               TALER_B2S (&credit_details->details.reserve.reserve_pub));
   1420   TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_wire_credit_fees),
   1421                         &TALER_ARL_USE_AB (total_wire_credit_fees),
   1422                         &credit_details->credit_fee);
   1423   GNUNET_CRYPTO_hash (&credit_details->serial_id,
   1424                       sizeof (credit_details->serial_id),
   1425                       &key);
   1426   rii = GNUNET_CONTAINER_multihashmap_get (in_map,
   1427                                            &key);
   1428   if (NULL == rii)
   1429   {
   1430     struct TALER_AUDITORDB_ReserveInInconsistency dc = {
   1431       .bank_row_id = credit_details->serial_id,
   1432       .amount_exchange_expected = zero,
   1433       .amount_wired = credit_details->amount,
   1434       .reserve_pub = credit_details->details.reserve.reserve_pub,
   1435       .timestamp = credit_details->execution_date.abs_time,
   1436       .account = credit_details->debit_account_uri,
   1437       .diagnostic = (char *) "unknown to exchange"
   1438     };
   1439     enum GNUNET_DB_QueryStatus qs;
   1440 
   1441     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   1442                 "Failed to find wire transfer at `%s' in exchange database.\n",
   1443                 GNUNET_TIME_timestamp2s (credit_details->execution_date));
   1444     qs = TALER_AUDITORDB_insert_reserve_in_inconsistency (TALER_ARL_adb,
   1445                                                           &dc);
   1446     if (qs <= 0)
   1447     {
   1448       global_qs = qs;
   1449       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
   1450       return false;
   1451     }
   1452     TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_in_plus),
   1453                           &TALER_ARL_USE_AB (total_bad_amount_in_plus),
   1454                           &credit_details->amount);
   1455     /* We have fully processed this credit: the finding is on file, and
   1456        reserve_in_cb() retracts it if the exchange books the transfer
   1457        later.  Looking at it again would only duplicate the finding. */
   1458     wa->wire_off_in = credit_details->serial_id;
   1459     return true;
   1460   }
   1461 
   1462   /* Update offset */
   1463   wa->wire_off_in = credit_details->serial_id;
   1464   /* compare records with expected data */
   1465   if (0 != GNUNET_memcmp (&credit_details->details.reserve.reserve_pub,
   1466                           &rii->credit_details.details.reserve.reserve_pub))
   1467   {
   1468     struct TALER_AUDITORDB_ReserveInInconsistency riiDb = {
   1469       .bank_row_id = credit_details->serial_id,
   1470       .amount_exchange_expected = rii->credit_details.amount,
   1471       .amount_wired = zero,
   1472       .reserve_pub = rii->credit_details.details.reserve.reserve_pub,
   1473       .timestamp = rii->credit_details.execution_date.abs_time,
   1474       .account = wa->ai->payto_uri,
   1475       .diagnostic = (char *) "wire subject does not match"
   1476     };
   1477     enum GNUNET_DB_QueryStatus qs;
   1478 
   1479     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   1480                 "Reserve public key differs\n");
   1481     qs = TALER_AUDITORDB_insert_reserve_in_inconsistency (
   1482       TALER_ARL_adb,
   1483       &riiDb);
   1484     if (qs <= 0)
   1485     {
   1486       global_qs = qs;
   1487       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
   1488       return false;
   1489     }
   1490     TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_in_minus),
   1491                           &TALER_ARL_USE_AB (total_bad_amount_in_minus),
   1492                           &rii->credit_details.amount);
   1493     TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_in_plus),
   1494                           &TALER_ARL_USE_AB (total_bad_amount_in_plus),
   1495                           &credit_details->amount);
   1496     GNUNET_assert (GNUNET_OK ==
   1497                    free_rii (NULL,
   1498                              &key,
   1499                              rii));
   1500     return true;
   1501   }
   1502   if (0 != TALER_amount_cmp (&rii->credit_details.amount,
   1503                              &credit_details->amount))
   1504   {
   1505     struct TALER_AUDITORDB_ReserveInInconsistency riiDb = {
   1506       .diagnostic = (char *) "wire amount does not match",
   1507       .account = wa->ai->payto_uri,
   1508       .bank_row_id = credit_details->serial_id,
   1509       .amount_exchange_expected = rii->credit_details.amount,
   1510       .amount_wired = credit_details->amount,
   1511       .reserve_pub = rii->credit_details.details.reserve.reserve_pub,
   1512       .timestamp = rii->credit_details.execution_date.abs_time
   1513     };
   1514     enum GNUNET_DB_QueryStatus qs;
   1515 
   1516     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   1517                 "Wire transfer amount differs\n");
   1518     qs = TALER_AUDITORDB_insert_reserve_in_inconsistency (
   1519       TALER_ARL_adb,
   1520       &riiDb);
   1521     GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != qs);
   1522     if (qs < 0)
   1523     {
   1524       global_qs = qs;
   1525       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
   1526       return false;
   1527     }
   1528     if (0 < TALER_amount_cmp (&credit_details->amount,
   1529                               &rii->credit_details.amount))
   1530     {
   1531       /* details->amount > rii->details.amount: wire transfer was larger than it should have been */
   1532       struct TALER_Amount delta;
   1533 
   1534       TALER_ARL_amount_subtract (&delta,
   1535                                  &credit_details->amount,
   1536                                  &rii->credit_details.amount);
   1537       TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_in_plus),
   1538                             &TALER_ARL_USE_AB (total_bad_amount_in_plus),
   1539                             &delta);
   1540     }
   1541     else
   1542     {
   1543       /* rii->details.amount < details->amount: wire transfer was smaller than it should have been */
   1544       struct TALER_Amount delta;
   1545 
   1546       TALER_ARL_amount_subtract (&delta,
   1547                                  &rii->credit_details.amount,
   1548                                  &credit_details->amount);
   1549       TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_in_minus),
   1550                             &TALER_ARL_USE_AB (total_bad_amount_in_minus),
   1551                             &delta);
   1552     }
   1553   }
   1554 
   1555   {
   1556     struct TALER_NormalizedPayto np;
   1557     struct TALER_NormalizedPayto np2;
   1558 
   1559     np = TALER_payto_normalize (credit_details->debit_account_uri);
   1560     np2 = TALER_payto_normalize (rii->credit_details.debit_account_uri);
   1561     if (0 != TALER_normalized_payto_cmp (np,
   1562                                          np2))
   1563     {
   1564       struct TALER_AUDITORDB_MisattributionInInconsistency mii = {
   1565         .reserve_pub = rii->credit_details.details.reserve.reserve_pub,
   1566         .amount = rii->credit_details.amount,
   1567         .bank_row = credit_details->serial_id
   1568       };
   1569       enum GNUNET_DB_QueryStatus qs;
   1570 
   1571       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   1572                   "Origin bank account differs\n");
   1573       qs = TALER_AUDITORDB_insert_misattribution_in_inconsistency (
   1574         TALER_ARL_adb,
   1575         &mii);
   1576       GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != qs);
   1577       if (qs < 0)
   1578       {
   1579         global_qs = qs;
   1580         GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
   1581         GNUNET_free (np.normalized_payto);
   1582         GNUNET_free (np2.normalized_payto);
   1583         return false;
   1584       }
   1585       TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_misattribution_in),
   1586                             &TALER_ARL_USE_AB (total_misattribution_in),
   1587                             &rii->credit_details.amount);
   1588     }
   1589     GNUNET_free (np.normalized_payto);
   1590     GNUNET_free (np2.normalized_payto);
   1591   }
   1592   if (GNUNET_TIME_timestamp_cmp (credit_details->execution_date,
   1593                                  !=,
   1594                                  rii->credit_details.execution_date))
   1595   {
   1596     struct TALER_AUDITORDB_RowMinorInconsistencies rmi = {
   1597       .problem_row = rii->rowid,
   1598       .diagnostic = (char *) "execution date mismatch",
   1599       .row_table = (char *) "reserves_in"
   1600     };
   1601     enum GNUNET_DB_QueryStatus qs;
   1602 
   1603     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   1604                 "Execution date differs\n");
   1605     qs = TALER_AUDITORDB_insert_row_minor_inconsistencies (
   1606       TALER_ARL_adb,
   1607       &rmi);
   1608 
   1609     if (qs < 0)
   1610     {
   1611       global_qs = qs;
   1612       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
   1613       return false;
   1614     }
   1615   }
   1616   GNUNET_assert (GNUNET_OK ==
   1617                  free_rii (NULL,
   1618                            &key,
   1619                            rii));
   1620   return true;
   1621 }
   1622 
   1623 
   1624 /**
   1625  * This function is called for all transactions that
   1626  * are credited to the exchange's account (incoming
   1627  * transactions).
   1628  *
   1629  * @param cls `struct WireAccount` we are processing
   1630  * @param chr HTTP response returned by the bank
   1631  */
   1632 static void
   1633 history_credit_cb (void *cls,
   1634                    const struct TALER_BANK_CreditHistoryResponse *chr)
   1635 {
   1636   struct WireAccount *wa = cls;
   1637 
   1638   wa->chh = NULL;
   1639   switch (chr->http_status)
   1640   {
   1641   case MHD_HTTP_OK:
   1642     for (unsigned int i = 0; i < chr->details.ok.details_length; i++)
   1643     {
   1644       const struct TALER_BANK_CreditDetails *cd
   1645         = &chr->details.ok.details[i];
   1646 
   1647       if (! analyze_credit (wa,
   1648                             cd))
   1649       {
   1650         switch (global_qs)
   1651         {
   1652         case GNUNET_DB_STATUS_SOFT_ERROR:
   1653           rollback_and_reset ();
   1654           return;
   1655         case GNUNET_DB_STATUS_HARD_ERROR:
   1656           GNUNET_SCHEDULER_shutdown ();
   1657           return;
   1658         case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
   1659           /* perfectly fine */
   1660           break;
   1661         case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
   1662           /* perfectly fine */
   1663           break;
   1664         }
   1665         break;
   1666       }
   1667     }
   1668     conclude_account (wa);
   1669     return;
   1670   case MHD_HTTP_NO_CONTENT:
   1671     conclude_account (wa);
   1672     return;
   1673   case MHD_HTTP_NOT_FOUND:
   1674     if (ignore_account_404)
   1675     {
   1676       conclude_account (wa);
   1677       return;
   1678     }
   1679     break;
   1680   default:
   1681     break;
   1682   }
   1683   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1684               "Error fetching credit history of account %s: %u (%s)\n",
   1685               wa->ai->section_name,
   1686               chr->http_status,
   1687               TALER_ErrorCode_get_hint (chr->ec));
   1688   commit (GNUNET_DB_STATUS_HARD_ERROR);
   1689   global_ret = EXIT_FAILURE;
   1690   GNUNET_SCHEDULER_shutdown ();
   1691 }
   1692 
   1693 
   1694 /* ***************************** Setup logic ************************ */
   1695 
   1696 
   1697 /**
   1698  * Start processing the next wire account.
   1699  * Shuts down if we are done.
   1700  *
   1701  * @param cls `struct WireAccount` with a wire account list to process
   1702  */
   1703 static void
   1704 process_credits (void *cls)
   1705 {
   1706   struct WireAccount *wa = cls;
   1707   enum GNUNET_DB_QueryStatus qs;
   1708 
   1709   /* skip accounts where CREDIT is not enabled */
   1710   while ( (NULL != wa) &&
   1711           (GNUNET_NO == wa->ai->credit_enabled) )
   1712     wa = wa->next;
   1713   if (NULL == wa)
   1714   {
   1715     /* done with all accounts, conclude check */
   1716     conclude_credit_history ();
   1717     return;
   1718   }
   1719   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1720               "Analyzing exchange's wire IN table for account `%s'\n",
   1721               wa->ai->section_name);
   1722   qs = TALER_EXCHANGEDB_iterate_reserves_in_above_serial_id_by_account (
   1723     TALER_ARL_edb,
   1724     wa->ai->section_name,
   1725     wa->last_reserve_in_serial_id,
   1726     &reserve_in_cb,
   1727     wa);
   1728   if (0 > qs)
   1729   {
   1730     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
   1731     global_ret = EXIT_FAILURE;
   1732     GNUNET_SCHEDULER_shutdown ();
   1733     return;
   1734   }
   1735 
   1736   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1737               "Analyzing exchange's KYCAUTH IN table for account `%s'\n",
   1738               wa->ai->section_name);
   1739   qs = TALER_EXCHANGEDB_iterate_kycauth_in_above_serial_id_by_account (
   1740     TALER_ARL_edb,
   1741     wa->ai->section_name,
   1742     wa->last_kycauth_in_serial_id,
   1743     &kycauth_in_cb,
   1744     wa);
   1745   if (0 > qs)
   1746   {
   1747     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
   1748     global_ret = EXIT_FAILURE;
   1749     GNUNET_SCHEDULER_shutdown ();
   1750     return;
   1751   }
   1752 
   1753   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1754               "Starting bank CREDIT history of account `%s'\n",
   1755               wa->ai->section_name);
   1756   GNUNET_assert (NULL == wa->chh);
   1757   wa->chh = TALER_BANK_credit_history (ctx,
   1758                                        wa->ai->auth,
   1759                                        wa->wire_off_in,
   1760                                        MAX_PER_TRANSACTION,
   1761                                        GNUNET_TIME_UNIT_ZERO,
   1762                                        &history_credit_cb,
   1763                                        wa);
   1764   if (NULL == wa->chh)
   1765   {
   1766     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1767                 "Failed to obtain bank transaction history\n");
   1768     commit (GNUNET_DB_STATUS_HARD_ERROR);
   1769     global_ret = EXIT_FAILURE;
   1770     GNUNET_SCHEDULER_shutdown ();
   1771     return;
   1772   }
   1773 }
   1774 
   1775 
   1776 /**
   1777  * Begin audit of CREDITs to the exchange.
   1778  */
   1779 static void
   1780 begin_credit_audit (void)
   1781 {
   1782   GNUNET_assert (NULL == in_map);
   1783   in_map = GNUNET_CONTAINER_multihashmap_create (1024,
   1784                                                  GNUNET_YES);
   1785   GNUNET_assert (NULL == kycauth_map);
   1786   kycauth_map = GNUNET_CONTAINER_multihashmap_create (1024,
   1787                                                       GNUNET_YES);
   1788   /* now go over all bank accounts and check delta with in_map */
   1789   process_credits (wa_head);
   1790 }
   1791 
   1792 
   1793 static enum GNUNET_DB_QueryStatus
   1794 begin_transaction (void)
   1795 {
   1796   enum GNUNET_DB_QueryStatus qs;
   1797 
   1798   if (GNUNET_SYSERR ==
   1799       TALER_EXCHANGEDB_preflight (TALER_ARL_edb))
   1800   {
   1801     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1802                 "Failed to initialize exchange database connection.\n");
   1803     return GNUNET_DB_STATUS_HARD_ERROR;
   1804   }
   1805   if (GNUNET_SYSERR ==
   1806       TALER_AUDITORDB_preflight (TALER_ARL_adb))
   1807   {
   1808     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1809                 "Failed to initialize auditor database session.\n");
   1810     return GNUNET_DB_STATUS_HARD_ERROR;
   1811   }
   1812   global_qs = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
   1813   if (GNUNET_OK !=
   1814       TALER_AUDITORDB_start (TALER_ARL_adb,
   1815                              "auditor-wire-credit"))
   1816   {
   1817     GNUNET_break (0);
   1818     return GNUNET_DB_STATUS_HARD_ERROR;
   1819   }
   1820   if (GNUNET_OK !=
   1821       TALER_EXCHANGEDB_start_read_only (TALER_ARL_edb,
   1822                                         "wire credit auditor"))
   1823   {
   1824     GNUNET_break (0);
   1825     return GNUNET_DB_STATUS_HARD_ERROR;
   1826   }
   1827   qs = TALER_AUDITORDB_get_balance (
   1828     TALER_ARL_adb,
   1829     TALER_ARL_GET_AB (total_wire_in),
   1830     TALER_ARL_GET_AB (total_kycauth_in),
   1831     TALER_ARL_GET_AB (total_kycauth_revenue),
   1832     TALER_ARL_GET_AB (total_wire_credit_fees),
   1833     TALER_ARL_GET_AB (total_bad_amount_in_plus),
   1834     TALER_ARL_GET_AB (total_bad_amount_in_minus),
   1835     TALER_ARL_GET_AB (total_misattribution_in),
   1836     NULL);
   1837   switch (qs)
   1838   {
   1839   case GNUNET_DB_STATUS_HARD_ERROR:
   1840     GNUNET_break (0);
   1841     return qs;
   1842   case GNUNET_DB_STATUS_SOFT_ERROR:
   1843     GNUNET_break (0);
   1844     return qs;
   1845   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
   1846   case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
   1847     break;
   1848   }
   1849   for (struct WireAccount *wa = wa_head;
   1850        NULL != wa;
   1851        wa = wa->next)
   1852   {
   1853     /* we are called again by rollback_and_reset() after a serialization
   1854        failure, so the labels may already have been computed */
   1855     GNUNET_free (wa->label_reserve_in_serial_id);
   1856     GNUNET_free (wa->label_kycauth_in_serial_id);
   1857     GNUNET_free (wa->label_wire_off_in);
   1858     GNUNET_asprintf (&wa->label_reserve_in_serial_id,
   1859                      "wire-%s-%s",
   1860                      wa->ai->section_name,
   1861                      "reserve_in_serial_id");
   1862     GNUNET_asprintf (&wa->label_kycauth_in_serial_id,
   1863                      "wire-%s-%s",
   1864                      wa->ai->section_name,
   1865                      "kycauth_in_serial_id");
   1866     GNUNET_asprintf (&wa->label_wire_off_in,
   1867                      "wire-%s-%s",
   1868                      wa->ai->section_name,
   1869                      "wire_off_in");
   1870     qs = TALER_AUDITORDB_get_auditor_progress (
   1871       TALER_ARL_adb,
   1872       wa->label_reserve_in_serial_id,
   1873       &wa->last_reserve_in_serial_id,
   1874       wa->label_kycauth_in_serial_id,
   1875       &wa->last_kycauth_in_serial_id,
   1876       wa->label_wire_off_in,
   1877       &wa->wire_off_in,
   1878       NULL);
   1879     if (0 > qs)
   1880     {
   1881       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
   1882       return qs;
   1883     }
   1884     wa->start_reserve_in_serial_id = wa->last_reserve_in_serial_id;
   1885     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1886                 "Starting from reserve_in at %s=%llu for account `%s'\n",
   1887                 wa->label_reserve_in_serial_id,
   1888                 (unsigned long long) wa->start_reserve_in_serial_id,
   1889                 wa->ai->section_name);
   1890   }
   1891 
   1892   begin_credit_audit ();
   1893   return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
   1894 }
   1895 
   1896 
   1897 /**
   1898  * Function called with information about a wire account.  Adds the
   1899  * account to our list for processing (if it is enabled and we can
   1900  * load the plugin).
   1901  *
   1902  * @param cls closure, NULL
   1903  * @param ai account information
   1904  */
   1905 static void
   1906 process_account_cb (void *cls,
   1907                     const struct TALER_EXCHANGEDB_AccountInfo *ai)
   1908 {
   1909   struct WireAccount *wa;
   1910 
   1911   (void) cls;
   1912   if ((! ai->debit_enabled) &&
   1913       (! ai->credit_enabled))
   1914     return; /* not an active exchange account */
   1915   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1916               "Found exchange account `%s'\n",
   1917               ai->section_name);
   1918   wa = GNUNET_new (struct WireAccount);
   1919   wa->ai = ai;
   1920   GNUNET_CONTAINER_DLL_insert (wa_head,
   1921                                wa_tail,
   1922                                wa);
   1923 }
   1924 
   1925 
   1926 /**
   1927  * Function called on events received from Postgres.
   1928  *
   1929  * @param cls closure, NULL
   1930  * @param extra additional event data provided
   1931  * @param extra_size number of bytes in @a extra
   1932  */
   1933 static void
   1934 db_notify (void *cls,
   1935            const void *extra,
   1936            size_t extra_size)
   1937 {
   1938   (void) cls;
   1939   (void) extra;
   1940   (void) extra_size;
   1941 
   1942   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1943               "Received notification to wake wire helper\n");
   1944   /* If there are accounts we are still processing, abort
   1945      the HTTP requests so we can start afresh. */
   1946   for (struct WireAccount *wa = wa_head;
   1947        NULL != wa;
   1948        wa = wa->next)
   1949   {
   1950     if (NULL != wa->chh)
   1951     {
   1952       TALER_BANK_credit_history_cancel (wa->chh);
   1953       wa->chh = NULL;
   1954     }
   1955     conclude_account (wa);
   1956   }
   1957 
   1958   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
   1959       begin_transaction ())
   1960   {
   1961     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1962                 "Audit failed\n");
   1963     GNUNET_break (0);
   1964     global_ret = EXIT_FAILURE;
   1965     GNUNET_SCHEDULER_shutdown ();
   1966   }
   1967 }
   1968 
   1969 
   1970 /**
   1971  * Main function that will be run.
   1972  *
   1973  * @param cls closure
   1974  * @param args remaining command-line arguments
   1975  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
   1976  * @param c configuration
   1977  */
   1978 static void
   1979 run (void *cls,
   1980      char *const *args,
   1981      const char *cfgfile,
   1982      const struct GNUNET_CONFIGURATION_Handle *c)
   1983 {
   1984   (void) cls;
   1985   (void) args;
   1986   (void) cfgfile;
   1987   cfg = c;
   1988   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
   1989               "Launching wire-credit auditor\n");
   1990   if (GNUNET_OK !=
   1991       TALER_ARL_init (c))
   1992   {
   1993     global_ret = EXIT_FAILURE;
   1994     return;
   1995   }
   1996   if (GNUNET_OK !=
   1997       TALER_config_get_amount (TALER_ARL_cfg,
   1998                                "auditor",
   1999                                "TINY_AMOUNT",
   2000                                &tiny_amount))
   2001   {
   2002     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
   2003                                "auditor",
   2004                                "TINY_AMOUNT");
   2005     global_ret = EXIT_NOTCONFIGURED;
   2006     return;
   2007   }
   2008   GNUNET_assert (GNUNET_OK ==
   2009                  TALER_amount_set_zero (TALER_ARL_currency,
   2010                                         &zero));
   2011   GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
   2012                                  NULL);
   2013   ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
   2014                           &rc);
   2015   rc = GNUNET_CURL_gnunet_rc_create (ctx);
   2016   if (NULL == ctx)
   2017   {
   2018     GNUNET_break (0);
   2019     global_ret = EXIT_FAILURE;
   2020     return;
   2021   }
   2022   if (GNUNET_OK !=
   2023       TALER_EXCHANGEDB_load_accounts (TALER_ARL_cfg,
   2024                                       TALER_EXCHANGEDB_ALO_CREDIT
   2025                                       | TALER_EXCHANGEDB_ALO_AUTHDATA))
   2026   {
   2027     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2028                 "No bank accounts configured\n");
   2029     global_ret = EXIT_NOTCONFIGURED;
   2030     GNUNET_SCHEDULER_shutdown ();
   2031     return;
   2032   }
   2033   TALER_EXCHANGEDB_find_accounts (&process_account_cb,
   2034                                   NULL);
   2035 
   2036   if (0 == test_mode)
   2037   {
   2038     struct GNUNET_DB_EventHeaderP es = {
   2039       .size = htons (sizeof (es)),
   2040       .type = htons (TALER_DBEVENT_EXCHANGE_AUDITOR_WAKE_HELPER_WIRE)
   2041     };
   2042 
   2043     eh = TALER_AUDITORDB_event_listen (TALER_ARL_adb,
   2044                                        &es,
   2045                                        GNUNET_TIME_UNIT_FOREVER_REL,
   2046                                        &db_notify,
   2047                                        NULL);
   2048     GNUNET_assert (NULL != eh);
   2049   }
   2050   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
   2051       begin_transaction ())
   2052   {
   2053     GNUNET_break (0);
   2054     global_ret = EXIT_FAILURE;
   2055     GNUNET_SCHEDULER_shutdown ();
   2056     return;
   2057   }
   2058 }
   2059 
   2060 
   2061 /**
   2062  * The main function of the wire auditing tool. Checks that
   2063  * the exchange's records of wire transfers match that of
   2064  * the wire gateway.
   2065  *
   2066  * @param argc number of arguments from the command line
   2067  * @param argv command line arguments
   2068  * @return 0 ok, 1 on error
   2069  */
   2070 int
   2071 main (int argc,
   2072       char *const *argv)
   2073 {
   2074   const struct GNUNET_GETOPT_CommandLineOption options[] = {
   2075     GNUNET_GETOPT_option_flag ('i',
   2076                                "internal",
   2077                                "perform checks only applicable for exchange-internal audits",
   2078                                &internal_checks),
   2079     GNUNET_GETOPT_option_flag ('I',
   2080                                "ignore-not-found",
   2081                                "continue, even if the bank account of the exchange was not found",
   2082                                &ignore_account_404),
   2083     GNUNET_GETOPT_option_flag ('t',
   2084                                "test",
   2085                                "run in test mode and exit when idle",
   2086                                &test_mode),
   2087     GNUNET_GETOPT_option_timetravel ('T',
   2088                                      "timetravel"),
   2089     GNUNET_GETOPT_OPTION_END
   2090   };
   2091   enum GNUNET_GenericReturnValue ret;
   2092 
   2093   ret = GNUNET_PROGRAM_run (
   2094     TALER_AUDITOR_project_data (),
   2095     argc,
   2096     argv,
   2097     "taler-helper-auditor-wire-credit",
   2098     gettext_noop (
   2099       "Audit exchange database for consistency with the bank's wire transfers"),
   2100     options,
   2101     &run,
   2102     NULL);
   2103   if (GNUNET_SYSERR == ret)
   2104     return EXIT_INVALIDARGUMENT;
   2105   if (GNUNET_NO == ret)
   2106     return EXIT_SUCCESS;
   2107   return global_ret;
   2108 }
   2109 
   2110 
   2111 /* end of taler-helper-auditor-wire-credit.c */