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


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