LCOV - code coverage report
Current view: top level - auditor - taler-helper-auditor-wire-credit.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 34.5 % 634 219
Test Date: 2026-10-17 15:46:49 Functions: 58.3 % 24 14

            Line data    Source code
       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 #TAC_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 #TAC_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           16 : free_rii (void *cls,
     350              :           const struct GNUNET_HashCode *key,
     351              :           void *value)
     352              : {
     353           16 :   struct ReserveInInfo *rii = value;
     354              : 
     355              :   (void) cls;
     356           16 :   GNUNET_assert (GNUNET_YES ==
     357              :                  GNUNET_CONTAINER_multihashmap_remove (in_map,
     358              :                                                        key,
     359              :                                                        rii));
     360           16 :   GNUNET_free (rii);
     361           16 :   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            0 : free_kii (void *cls,
     375              :           const struct GNUNET_HashCode *key,
     376              :           void *value)
     377              : {
     378            0 :   struct KycauthInInfo *kii = value;
     379              : 
     380              :   (void) cls;
     381            0 :   GNUNET_assert (GNUNET_YES ==
     382              :                  GNUNET_CONTAINER_multihashmap_remove (kycauth_map,
     383              :                                                        key,
     384              :                                                        kii));
     385            0 :   GNUNET_free (kii);
     386            0 :   return GNUNET_OK;
     387              : }
     388              : 
     389              : 
     390              : /**
     391              :  * Task run on shutdown.
     392              :  *
     393              :  * @param cls NULL
     394              :  */
     395              : static void
     396            6 : do_shutdown (void *cls)
     397              : {
     398              :   struct WireAccount *wa;
     399              : 
     400              :   (void) cls;
     401            6 :   if (NULL != eh)
     402              :   {
     403            0 :     TALER_AUDITORDB_event_listen_cancel (eh);
     404            0 :     eh = NULL;
     405              :   }
     406            6 :   TALER_ARL_done ();
     407            6 :   if (NULL != in_map)
     408              :   {
     409            0 :     GNUNET_CONTAINER_multihashmap_iterate (in_map,
     410              :                                            &free_rii,
     411              :                                            NULL);
     412            0 :     GNUNET_CONTAINER_multihashmap_destroy (in_map);
     413            0 :     in_map = NULL;
     414              :   }
     415            6 :   if (NULL != kycauth_map)
     416              :   {
     417            0 :     GNUNET_CONTAINER_multihashmap_iterate (kycauth_map,
     418              :                                            &free_kii,
     419              :                                            NULL);
     420            0 :     GNUNET_CONTAINER_multihashmap_destroy (kycauth_map);
     421            0 :     kycauth_map = NULL;
     422              :   }
     423           12 :   while (NULL != (wa = wa_head))
     424              :   {
     425            6 :     if (NULL != wa->chh)
     426              :     {
     427            0 :       TALER_BANK_credit_history_cancel (wa->chh);
     428            0 :       wa->chh = NULL;
     429              :     }
     430            6 :     GNUNET_CONTAINER_DLL_remove (wa_head,
     431              :                                  wa_tail,
     432              :                                  wa);
     433            6 :     GNUNET_free (wa->label_reserve_in_serial_id);
     434            6 :     GNUNET_free (wa->label_kycauth_in_serial_id);
     435            6 :     GNUNET_free (wa->label_wire_off_in);
     436            6 :     GNUNET_free (wa);
     437              :   }
     438            6 :   if (NULL != ctx)
     439              :   {
     440            6 :     GNUNET_CURL_fini (ctx);
     441            6 :     ctx = NULL;
     442              :   }
     443            6 :   if (NULL != rc)
     444              :   {
     445            6 :     GNUNET_CURL_gnunet_rc_destroy (rc);
     446            6 :     rc = NULL;
     447              :   }
     448            6 :   TALER_EXCHANGEDB_unload_accounts ();
     449            6 :   TALER_ARL_cfg = NULL;
     450            6 : }
     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            0 : rollback_and_reset (void)
     468              : {
     469            0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     470              :               "Serialization issue, trying again\n");
     471            0 :   TALER_AUDITORDB_rollback (TALER_ARL_adb);
     472            0 :   for (unsigned int max_retries = 3; max_retries>0; max_retries--)
     473              :   {
     474              :     enum GNUNET_DB_QueryStatus qs;
     475              : 
     476            0 :     if (NULL != in_map)
     477              :     {
     478            0 :       GNUNET_CONTAINER_multihashmap_iterate (in_map,
     479              :                                              &free_rii,
     480              :                                              NULL);
     481            0 :       GNUNET_CONTAINER_multihashmap_destroy (in_map);
     482            0 :       in_map = NULL;
     483              :     }
     484            0 :     if (NULL != kycauth_map)
     485              :     {
     486            0 :       GNUNET_CONTAINER_multihashmap_iterate (kycauth_map,
     487              :                                              &free_kii,
     488              :                                              NULL);
     489            0 :       GNUNET_CONTAINER_multihashmap_destroy (kycauth_map);
     490            0 :       kycauth_map = NULL;
     491              :     }
     492            0 :     qs = begin_transaction ();
     493            0 :     if (GNUNET_DB_STATUS_HARD_ERROR == qs)
     494            0 :       break;
     495              :   }
     496            0 :   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     497              :               "Hard database error, terminating\n");
     498            0 :   GNUNET_SCHEDULER_shutdown ();
     499            0 : }
     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            6 : commit (enum GNUNET_DB_QueryStatus qs)
     509              : {
     510            6 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     511              :               "Transaction logic ended with status %d\n",
     512              :               qs);
     513            6 :   TALER_EXCHANGEDB_rollback (TALER_ARL_edb);
     514            6 :   if (qs < 0)
     515            0 :     goto handle_db_error;
     516            6 :   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            6 :   if (0 > qs)
     527            0 :     goto handle_db_error;
     528            6 :   for (struct WireAccount *wa = wa_head;
     529           12 :        NULL != wa;
     530            6 :        wa = wa->next)
     531              :   {
     532            6 :     qs = TALER_AUDITORDB_insert_auditor_progress (
     533              :       TALER_ARL_adb,
     534            6 :       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            6 :     if (0 > qs)
     542            0 :       goto handle_db_error;
     543            6 :     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            6 :   qs = TALER_AUDITORDB_commit (TALER_ARL_adb);
     550            6 :   if (0 > qs)
     551              :   {
     552            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     553            0 :     goto handle_db_error;
     554              :   }
     555            6 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     556              :               "Transaction concluded!\n");
     557            6 :   if (1 == test_mode)
     558            6 :     GNUNET_SCHEDULER_shutdown ();
     559            6 :   return;
     560            0 : handle_db_error:
     561            0 :   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            6 : conclude_credit_history (void)
     572              : {
     573            6 :   if (NULL != in_map)
     574              :   {
     575            6 :     GNUNET_assert (0 ==
     576              :                    GNUNET_CONTAINER_multihashmap_size (in_map));
     577            6 :     GNUNET_CONTAINER_multihashmap_destroy (in_map);
     578            6 :     in_map = NULL;
     579              :   }
     580            6 :   if (NULL != kycauth_map)
     581              :   {
     582            6 :     GNUNET_assert (0 ==
     583              :                    GNUNET_CONTAINER_multihashmap_size (kycauth_map));
     584            6 :     GNUNET_CONTAINER_multihashmap_destroy (kycauth_map);
     585            6 :     kycauth_map = NULL;
     586              :   }
     587            6 :   commit (global_qs);
     588            6 : }
     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            0 : 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            0 :   if (0 != TALER_amount_cmp (credit,
     617              :                              credit2))
     618            0 :     return GNUNET_NO;
     619            0 :   if (0 != GNUNET_memcmp (reserve_pub,
     620              :                           reserve_pub2))
     621            0 :     return GNUNET_NO;
     622              :   {
     623              :     struct TALER_NormalizedPayto np;
     624              :     struct TALER_NormalizedPayto np2;
     625              :     bool fail;
     626              : 
     627            0 :     np = TALER_payto_normalize (sender_account_details);
     628            0 :     np2 = TALER_payto_normalize (sender_account_details2);
     629            0 :     fail = (0 != TALER_normalized_payto_cmp (np,
     630              :                                              np2));
     631            0 :     GNUNET_free (np.normalized_payto);
     632            0 :     GNUNET_free (np2.normalized_payto);
     633            0 :     if (fail)
     634            0 :       return GNUNET_NO;
     635              :   }
     636            0 :   if (GNUNET_TIME_timestamp_cmp (execution_date,
     637              :                                  !=,
     638              :                                  execution_date2))
     639            0 :     return GNUNET_NO;
     640            0 :   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            0 : 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            0 :   if ( (NULL == a.full_payto) ||
     663            0 :        (NULL == b.full_payto) )
     664            0 :     return true;
     665            0 :   na = TALER_payto_normalize (a);
     666            0 :   nb = TALER_payto_normalize (b);
     667            0 :   ret = (0 == TALER_normalized_payto_cmp (na,
     668              :                                           nb));
     669            0 :   GNUNET_free (na.normalized_payto);
     670            0 :   GNUNET_free (nb.normalized_payto);
     671            0 :   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            0 : 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            0 :   return (0 == TALER_amount_cmp (credit,
     701            0 :                                  credit2)) &&
     702            0 :          (0 == GNUNET_memcmp (account_pub,
     703            0 :                               account_pub2)) &&
     704            0 :          same_account (sender_account_details,
     705            0 :                        sender_account_details2) &&
     706            0 :          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            0 : 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            0 :   struct WireAccount *wa = cls;
     747              :   struct KycauthInInfo *kii;
     748              :   size_t slen;
     749              : 
     750            0 :   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            0 :   wa->last_kycauth_in_serial_id = rowid + 1;
     758            0 :   TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_kycauth_revenue),
     759              :                         &TALER_ARL_USE_AB (total_kycauth_revenue),
     760              :                         credit);
     761            0 :   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            0 :     qs = TALER_AUDITORDB_get_kycauth_in_inconsistency (
     769              :       TALER_ARL_adb,
     770              :       wire_reference,
     771              :       &dc);
     772            0 :     switch (qs)
     773              :     {
     774            0 :     case GNUNET_DB_STATUS_HARD_ERROR:
     775              :     case GNUNET_DB_STATUS_SOFT_ERROR:
     776            0 :       global_qs = qs;
     777            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     778            0 :       return GNUNET_SYSERR;
     779            0 :     case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
     780            0 :       break;
     781            0 :     case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
     782              :       {
     783            0 :         bool healed = false;
     784              : 
     785            0 :         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            0 :           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            0 :         GNUNET_free (dc.account.full_payto);
     801            0 :         GNUNET_free (dc.diagnostic);
     802            0 :         if (healed)
     803              :         {
     804            0 :           qs = TALER_AUDITORDB_delete_kycauth_in_inconsistency (
     805              :             TALER_ARL_adb,
     806              :             dc.serial_id);
     807            0 :           if (qs < 0)
     808              :           {
     809            0 :             global_qs = qs;
     810            0 :             GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     811            0 :             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            0 :           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            0 :           return GNUNET_OK;
     823              :         }
     824            0 :         break;
     825              :       }
     826              :     }
     827              :   }
     828            0 :   slen = (NULL == sender_account_details.full_payto)
     829              :     ? 0
     830            0 :     : strlen (sender_account_details.full_payto) + 1;
     831            0 :   kii = GNUNET_malloc (sizeof (struct KycauthInInfo) + slen);
     832            0 :   kii->rowid = rowid;
     833            0 :   kii->account_pub = *account_pub;
     834            0 :   kii->credit = *credit;
     835            0 :   kii->execution_date = execution_date;
     836            0 :   if (0 != slen)
     837              :   {
     838            0 :     kii->sender_account_details.full_payto = (char *) &kii[1];
     839            0 :     GNUNET_memcpy (&kii[1],
     840              :                    sender_account_details.full_payto,
     841              :                    slen);
     842              :   }
     843            0 :   GNUNET_CRYPTO_hash (&wire_reference,
     844              :                       sizeof (uint64_t),
     845              :                       &kii->row_off_hash);
     846            0 :   if (GNUNET_OK !=
     847            0 :       GNUNET_CONTAINER_multihashmap_put (
     848              :         kycauth_map,
     849            0 :         &kii->row_off_hash,
     850              :         kii,
     851              :         GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
     852              :   {
     853            0 :     struct TALER_AUDITORDB_RowInconsistency ri = {
     854              :       .problem_row_id = rowid,
     855              :       .row_table = (char *) "kycauths_in",
     856              :       .diagnostic = (char *) "duplicate wire offset"
     857              :     };
     858              :     enum GNUNET_DB_QueryStatus qs;
     859              : 
     860            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     861              :                 "Duplicate wire offset\n");
     862            0 :     qs = TALER_AUDITORDB_insert_row_inconsistency (
     863              :       TALER_ARL_adb,
     864              :       &ri);
     865            0 :     GNUNET_free (kii);
     866            0 :     if (qs < 0)
     867              :     {
     868            0 :       global_qs = qs;
     869            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     870            0 :       return GNUNET_SYSERR;
     871              :     }
     872              :   }
     873            0 :   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           16 : 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           16 :   struct WireAccount *wa = cls;
     900              :   struct ReserveInInfo *rii;
     901              :   size_t slen;
     902              : 
     903           16 :   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           16 :   wa->last_reserve_in_serial_id = rowid + 1;
     912           16 :   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           16 :     qs = TALER_AUDITORDB_get_reserve_in_inconsistency (
     920              :       TALER_ARL_adb,
     921              :       wire_reference,
     922              :       &dc);
     923           16 :     switch (qs)
     924              :     {
     925            0 :     case GNUNET_DB_STATUS_HARD_ERROR:
     926              :     case GNUNET_DB_STATUS_SOFT_ERROR:
     927            0 :       global_qs = qs;
     928            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     929            0 :       return GNUNET_SYSERR;
     930           16 :     case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
     931           16 :       break;
     932            0 :     case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
     933              :       {
     934            0 :         enum GNUNET_GenericReturnValue ret = GNUNET_NO;
     935              : 
     936            0 :         if (TALER_amount_is_zero (&dc.amount_exchange_expected))
     937              :         {
     938              :           /* database entry indicates unmatched transaction */
     939            0 :           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            0 :         GNUNET_free (dc.account.full_payto);
     950            0 :         GNUNET_free (dc.diagnostic);
     951            0 :         if (GNUNET_SYSERR == ret)
     952            0 :           return GNUNET_SYSERR;
     953            0 :         if (GNUNET_YES == ret)
     954              :         {
     955            0 :           qs = TALER_AUDITORDB_delete_reserve_in_inconsistency (
     956              :             TALER_ARL_adb,
     957              :             dc.serial_id);
     958            0 :           if (qs < 0)
     959              :           {
     960            0 :             global_qs = qs;
     961            0 :             GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     962            0 :             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            0 :           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            0 :           return GNUNET_OK;
     972              :         }
     973            0 :         break;
     974              :       }
     975              :     }
     976              :   }
     977           16 :   slen = strlen (sender_account_details.full_payto) + 1;
     978           16 :   rii = GNUNET_malloc (sizeof (struct ReserveInInfo) + slen);
     979           16 :   rii->rowid = rowid;
     980           16 :   rii->credit_details.type = TALER_BANK_CT_RESERVE;
     981           16 :   rii->credit_details.amount = *credit;
     982           16 :   rii->credit_details.execution_date = execution_date;
     983           16 :   rii->credit_details.details.reserve.reserve_pub = *reserve_pub;
     984           16 :   rii->credit_details.debit_account_uri.full_payto = (char *) &rii[1];
     985           16 :   GNUNET_memcpy (&rii[1],
     986              :                  sender_account_details.full_payto,
     987              :                  slen);
     988           16 :   GNUNET_CRYPTO_hash (&wire_reference,
     989              :                       sizeof (uint64_t),
     990              :                       &rii->row_off_hash);
     991           16 :   if (GNUNET_OK !=
     992           16 :       GNUNET_CONTAINER_multihashmap_put (in_map,
     993           16 :                                          &rii->row_off_hash,
     994              :                                          rii,
     995              :                                          GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
     996              :   {
     997            0 :     struct TALER_AUDITORDB_RowInconsistency ri = {
     998              :       .problem_row_id = rowid,
     999              :       .row_table = (char *) "reserves_in",
    1000              :       .diagnostic = (char *) "duplicate wire offset"
    1001              :     };
    1002              :     enum GNUNET_DB_QueryStatus qs;
    1003              : 
    1004            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    1005              :                 "Duplicate wire offset\n");
    1006            0 :     qs = TALER_AUDITORDB_insert_row_inconsistency (
    1007              :       TALER_ARL_adb,
    1008              :       &ri);
    1009            0 :     GNUNET_free (rii);
    1010            0 :     if (qs < 0)
    1011              :     {
    1012            0 :       global_qs = qs;
    1013            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1014            0 :       return GNUNET_SYSERR;
    1015              :     }
    1016            0 :     return GNUNET_OK;
    1017              :   }
    1018           16 :   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            0 : complain_in_not_found (void *cls,
    1032              :                        const struct GNUNET_HashCode *key,
    1033              :                        void *value)
    1034              : {
    1035            0 :   struct WireAccount *wa = cls;
    1036            0 :   struct ReserveInInfo *rii = value;
    1037              :   enum GNUNET_DB_QueryStatus qs;
    1038            0 :   struct TALER_AUDITORDB_ReserveInInconsistency riiDb = {
    1039            0 :     .bank_row_id = rii->rowid,
    1040              :     .diagnostic = (char *)
    1041              :                   "incoming wire transfer claimed by exchange not found",
    1042            0 :     .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            0 :   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    1051              :               "Incoming wire transfer #%llu claimed by exchange not found\n",
    1052              :               (unsigned long long) rii->rowid);
    1053            0 :   GNUNET_assert (TALER_BANK_CT_RESERVE ==
    1054              :                  rii->credit_details.type);
    1055            0 :   qs = TALER_AUDITORDB_insert_reserve_in_inconsistency (
    1056              :     TALER_ARL_adb,
    1057              :     &riiDb);
    1058            0 :   if (qs < 0)
    1059              :   {
    1060            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1061            0 :     global_qs = qs;
    1062            0 :     return GNUNET_SYSERR;
    1063              :   }
    1064            0 :   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            0 :   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            0 : complain_kycauth_in_not_found (void *cls,
    1084              :                                const struct GNUNET_HashCode *key,
    1085              :                                void *value)
    1086              : {
    1087            0 :   struct WireAccount *wa = cls;
    1088            0 :   struct KycauthInInfo *kii = value;
    1089              :   enum GNUNET_DB_QueryStatus qs;
    1090            0 :   struct TALER_AUDITORDB_KycauthInInconsistency kiiDb = {
    1091            0 :     .bank_row_id = kii->rowid,
    1092              :     .diagnostic = (char *)
    1093              :                   "KYCAUTH wire transfer claimed by exchange not found",
    1094            0 :     .account = (NULL == kii->sender_account_details.full_payto)
    1095            0 :                ? wa->ai->payto_uri
    1096            0 :                : 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            0 :   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    1105              :               "KYCAUTH wire transfer #%llu claimed by exchange not found\n",
    1106              :               (unsigned long long) kii->rowid);
    1107            0 :   qs = TALER_AUDITORDB_insert_kycauth_in_inconsistency (
    1108              :     TALER_ARL_adb,
    1109              :     &kiiDb);
    1110            0 :   if (qs < 0)
    1111              :   {
    1112            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1113            0 :     global_qs = qs;
    1114            0 :     return GNUNET_SYSERR;
    1115              :   }
    1116            0 :   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            0 :   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            6 : conclude_account (struct WireAccount *wa)
    1141              : {
    1142            6 :   GNUNET_assert (NULL == wa->chh);
    1143            6 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1144              :               "Reconciling CREDIT processing of account `%s'\n",
    1145              :               wa->ai->section_name);
    1146            6 :   if (NULL != in_map)
    1147              :   {
    1148            6 :     GNUNET_CONTAINER_multihashmap_iterate (in_map,
    1149              :                                            &complain_in_not_found,
    1150              :                                            wa);
    1151              :     /* clean up before 2nd phase */
    1152            6 :     GNUNET_CONTAINER_multihashmap_iterate (in_map,
    1153              :                                            &free_rii,
    1154              :                                            NULL);
    1155            6 :     if (global_qs < 0)
    1156              :     {
    1157            0 :       commit (global_qs);
    1158            0 :       return;
    1159              :     }
    1160              :   }
    1161            6 :   if (NULL != kycauth_map)
    1162              :   {
    1163            6 :     GNUNET_CONTAINER_multihashmap_iterate (kycauth_map,
    1164              :                                            &complain_kycauth_in_not_found,
    1165              :                                            wa);
    1166              :     /* clean up before 2nd phase */
    1167            6 :     GNUNET_CONTAINER_multihashmap_iterate (kycauth_map,
    1168              :                                            &free_kii,
    1169              :                                            NULL);
    1170            6 :     if (global_qs < 0)
    1171              :     {
    1172            0 :       commit (global_qs);
    1173            0 :       return;
    1174              :     }
    1175              :   }
    1176            6 :   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            0 : 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            0 :   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            0 :   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            0 :   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            0 :   GNUNET_CRYPTO_hash (&credit_details->serial_id,
    1210              :                       sizeof (credit_details->serial_id),
    1211              :                       &key);
    1212            0 :   kii = GNUNET_CONTAINER_multihashmap_get (kycauth_map,
    1213              :                                            &key);
    1214            0 :   if (NULL == kii)
    1215              :   {
    1216            0 :     struct TALER_AUDITORDB_KycauthInInconsistency dc = {
    1217            0 :       .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            0 :     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            0 :     qs = TALER_AUDITORDB_insert_kycauth_in_inconsistency (TALER_ARL_adb,
    1231              :                                                           &dc);
    1232            0 :     if (qs <= 0)
    1233              :     {
    1234            0 :       global_qs = qs;
    1235            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1236            0 :       return false;
    1237              :     }
    1238            0 :     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            0 :     wa->wire_off_in = credit_details->serial_id;
    1245            0 :     return true;
    1246              :   }
    1247              : 
    1248              :   /* Update offset */
    1249            0 :   wa->wire_off_in = credit_details->serial_id;
    1250            0 :   if (0 != TALER_amount_cmp (&kii->credit,
    1251              :                              &credit_details->amount))
    1252              :   {
    1253            0 :     struct TALER_AUDITORDB_KycauthInInconsistency dc = {
    1254            0 :       .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            0 :       .account = wa->ai->payto_uri,
    1260              :       .diagnostic = (char *) "wire amount does not match"
    1261              :     };
    1262              :     enum GNUNET_DB_QueryStatus qs;
    1263              : 
    1264            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    1265              :                 "KYCAUTH transfer amount differs\n");
    1266            0 :     qs = TALER_AUDITORDB_insert_kycauth_in_inconsistency (
    1267              :       TALER_ARL_adb,
    1268              :       &dc);
    1269            0 :     if (qs <= 0)
    1270              :     {
    1271            0 :       global_qs = qs;
    1272            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1273            0 :       return false;
    1274              :     }
    1275            0 :     if (0 < TALER_amount_cmp (&credit_details->amount,
    1276            0 :                               &kii->credit))
    1277              :     {
    1278              :       struct TALER_Amount delta;
    1279              : 
    1280            0 :       TALER_ARL_amount_subtract (&delta,
    1281              :                                  &credit_details->amount,
    1282              :                                  &kii->credit);
    1283            0 :       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            0 :       TALER_ARL_amount_subtract (&delta,
    1292              :                                  &kii->credit,
    1293              :                                  &credit_details->amount);
    1294            0 :       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            0 :   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            0 :     struct TALER_AUDITORDB_KycauthInInconsistency dc = {
    1306            0 :       .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            0 :       .account = wa->ai->payto_uri,
    1312              :       .diagnostic = (char *) "account public key does not match"
    1313              :     };
    1314              :     enum GNUNET_DB_QueryStatus qs;
    1315              : 
    1316            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    1317              :                 "KYCAUTH account public key differs\n");
    1318            0 :     qs = TALER_AUDITORDB_insert_kycauth_in_inconsistency (
    1319              :       TALER_ARL_adb,
    1320              :       &dc);
    1321            0 :     if (qs <= 0)
    1322              :     {
    1323            0 :       global_qs = qs;
    1324            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1325            0 :       return false;
    1326              :     }
    1327              :   }
    1328            0 :   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            0 :     struct TALER_AUDITORDB_KycauthInInconsistency dc = {
    1335            0 :       .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            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    1346              :                 "Origin bank account of KYCAUTH transfer differs\n");
    1347            0 :     qs = TALER_AUDITORDB_insert_kycauth_in_inconsistency (
    1348              :       TALER_ARL_adb,
    1349              :       &dc);
    1350            0 :     if (qs <= 0)
    1351              :     {
    1352            0 :       global_qs = qs;
    1353            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1354            0 :       return false;
    1355              :     }
    1356              :   }
    1357            0 :   if (GNUNET_TIME_timestamp_cmp (credit_details->execution_date,
    1358              :                                  !=,
    1359              :                                  kii->execution_date))
    1360              :   {
    1361            0 :     struct TALER_AUDITORDB_RowMinorInconsistencies rmi = {
    1362            0 :       .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            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    1369              :                 "Execution date of KYCAUTH transfer differs\n");
    1370            0 :     qs = TALER_AUDITORDB_insert_row_minor_inconsistencies (
    1371              :       TALER_ARL_adb,
    1372              :       &rmi);
    1373            0 :     if (qs < 0)
    1374              :     {
    1375            0 :       global_qs = qs;
    1376            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1377            0 :       return false;
    1378              :     }
    1379              :   }
    1380            0 :   GNUNET_assert (GNUNET_OK ==
    1381              :                  free_kii (NULL,
    1382              :                            &key,
    1383              :                            kii));
    1384            0 :   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           16 : 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           16 :   switch (credit_details->type)
    1404              :   {
    1405           16 :   case TALER_BANK_CT_RESERVE:
    1406           16 :     break;
    1407            0 :   case TALER_BANK_CT_KYCAUTH:
    1408            0 :     return analyze_kycauth_credit (wa,
    1409              :                                    credit_details);
    1410            0 :   case TALER_BANK_CT_WAD:
    1411            0 :     GNUNET_break (0); /* FIXME: Wad not yet supported */
    1412            0 :     return false;
    1413              :   }
    1414           16 :   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           16 :   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           16 :   GNUNET_CRYPTO_hash (&credit_details->serial_id,
    1424              :                       sizeof (credit_details->serial_id),
    1425              :                       &key);
    1426           16 :   rii = GNUNET_CONTAINER_multihashmap_get (in_map,
    1427              :                                            &key);
    1428           16 :   if (NULL == rii)
    1429              :   {
    1430            0 :     struct TALER_AUDITORDB_ReserveInInconsistency dc = {
    1431            0 :       .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            0 :     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            0 :     qs = TALER_AUDITORDB_insert_reserve_in_inconsistency (TALER_ARL_adb,
    1445              :                                                           &dc);
    1446            0 :     if (qs <= 0)
    1447              :     {
    1448            0 :       global_qs = qs;
    1449            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1450            0 :       return false;
    1451              :     }
    1452            0 :     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            0 :     wa->wire_off_in = credit_details->serial_id;
    1459            0 :     return true;
    1460              :   }
    1461              : 
    1462              :   /* Update offset */
    1463           16 :   wa->wire_off_in = credit_details->serial_id;
    1464              :   /* compare records with expected data */
    1465           16 :   if (0 != GNUNET_memcmp (&credit_details->details.reserve.reserve_pub,
    1466              :                           &rii->credit_details.details.reserve.reserve_pub))
    1467              :   {
    1468            0 :     struct TALER_AUDITORDB_ReserveInInconsistency riiDb = {
    1469            0 :       .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            0 :       .account = wa->ai->payto_uri,
    1475              :       .diagnostic = (char *) "wire subject does not match"
    1476              :     };
    1477              :     enum GNUNET_DB_QueryStatus qs;
    1478              : 
    1479            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    1480              :                 "Reserve public key differs\n");
    1481            0 :     qs = TALER_AUDITORDB_insert_reserve_in_inconsistency (
    1482              :       TALER_ARL_adb,
    1483              :       &riiDb);
    1484            0 :     if (qs <= 0)
    1485              :     {
    1486            0 :       global_qs = qs;
    1487            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1488            0 :       return false;
    1489              :     }
    1490            0 :     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            0 :     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            0 :     GNUNET_assert (GNUNET_OK ==
    1497              :                    free_rii (NULL,
    1498              :                              &key,
    1499              :                              rii));
    1500            0 :     return true;
    1501              :   }
    1502           16 :   if (0 != TALER_amount_cmp (&rii->credit_details.amount,
    1503              :                              &credit_details->amount))
    1504              :   {
    1505            0 :     struct TALER_AUDITORDB_ReserveInInconsistency riiDb = {
    1506              :       .diagnostic = (char *) "wire amount does not match",
    1507            0 :       .account = wa->ai->payto_uri,
    1508            0 :       .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            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    1517              :                 "Wire transfer amount differs\n");
    1518            0 :     qs = TALER_AUDITORDB_insert_reserve_in_inconsistency (
    1519              :       TALER_ARL_adb,
    1520              :       &riiDb);
    1521            0 :     GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != qs);
    1522            0 :     if (qs < 0)
    1523              :     {
    1524            0 :       global_qs = qs;
    1525            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1526            0 :       return false;
    1527              :     }
    1528            0 :     if (0 < TALER_amount_cmp (&credit_details->amount,
    1529            0 :                               &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            0 :       TALER_ARL_amount_subtract (&delta,
    1535              :                                  &credit_details->amount,
    1536              :                                  &rii->credit_details.amount);
    1537            0 :       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            0 :       TALER_ARL_amount_subtract (&delta,
    1547              :                                  &rii->credit_details.amount,
    1548              :                                  &credit_details->amount);
    1549            0 :       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           16 :     np = TALER_payto_normalize (credit_details->debit_account_uri);
    1560           16 :     np2 = TALER_payto_normalize (rii->credit_details.debit_account_uri);
    1561           16 :     if (0 != TALER_normalized_payto_cmp (np,
    1562              :                                          np2))
    1563              :     {
    1564            0 :       struct TALER_AUDITORDB_MisattributionInInconsistency mii = {
    1565              :         .reserve_pub = rii->credit_details.details.reserve.reserve_pub,
    1566              :         .amount = rii->credit_details.amount,
    1567            0 :         .bank_row = credit_details->serial_id
    1568              :       };
    1569              :       enum GNUNET_DB_QueryStatus qs;
    1570              : 
    1571            0 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    1572              :                   "Origin bank account differs\n");
    1573            0 :       qs = TALER_AUDITORDB_insert_misattribution_in_inconsistency (
    1574              :         TALER_ARL_adb,
    1575              :         &mii);
    1576            0 :       GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != qs);
    1577            0 :       if (qs < 0)
    1578              :       {
    1579            0 :         global_qs = qs;
    1580            0 :         GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1581            0 :         GNUNET_free (np.normalized_payto);
    1582            0 :         GNUNET_free (np2.normalized_payto);
    1583            0 :         return false;
    1584              :       }
    1585            0 :       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           16 :     GNUNET_free (np.normalized_payto);
    1590           16 :     GNUNET_free (np2.normalized_payto);
    1591              :   }
    1592           16 :   if (GNUNET_TIME_timestamp_cmp (credit_details->execution_date,
    1593              :                                  !=,
    1594              :                                  rii->credit_details.execution_date))
    1595              :   {
    1596            0 :     struct TALER_AUDITORDB_RowMinorInconsistencies rmi = {
    1597            0 :       .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            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    1604              :                 "Execution date differs\n");
    1605            0 :     qs = TALER_AUDITORDB_insert_row_minor_inconsistencies (
    1606              :       TALER_ARL_adb,
    1607              :       &rmi);
    1608              : 
    1609            0 :     if (qs < 0)
    1610              :     {
    1611            0 :       global_qs = qs;
    1612            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1613            0 :       return false;
    1614              :     }
    1615              :   }
    1616           16 :   GNUNET_assert (GNUNET_OK ==
    1617              :                  free_rii (NULL,
    1618              :                            &key,
    1619              :                            rii));
    1620           16 :   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            6 : history_credit_cb (void *cls,
    1634              :                    const struct TALER_BANK_CreditHistoryResponse *chr)
    1635              : {
    1636            6 :   struct WireAccount *wa = cls;
    1637              : 
    1638            6 :   wa->chh = NULL;
    1639            6 :   switch (chr->http_status)
    1640              :   {
    1641            4 :   case MHD_HTTP_OK:
    1642           20 :     for (unsigned int i = 0; i < chr->details.ok.details_length; i++)
    1643              :     {
    1644           16 :       const struct TALER_BANK_CreditDetails *cd
    1645           16 :         = &chr->details.ok.details[i];
    1646              : 
    1647           16 :       if (! analyze_credit (wa,
    1648              :                             cd))
    1649              :       {
    1650            0 :         switch (global_qs)
    1651              :         {
    1652            0 :         case GNUNET_DB_STATUS_SOFT_ERROR:
    1653            0 :           rollback_and_reset ();
    1654            0 :           return;
    1655            0 :         case GNUNET_DB_STATUS_HARD_ERROR:
    1656            0 :           GNUNET_SCHEDULER_shutdown ();
    1657            0 :           return;
    1658            0 :         case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    1659              :           /* perfectly fine */
    1660            0 :           break;
    1661            0 :         case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    1662              :           /* perfectly fine */
    1663            0 :           break;
    1664              :         }
    1665            0 :         break;
    1666              :       }
    1667              :     }
    1668            4 :     conclude_account (wa);
    1669            4 :     return;
    1670            0 :   case MHD_HTTP_NO_CONTENT:
    1671            0 :     conclude_account (wa);
    1672            0 :     return;
    1673            2 :   case MHD_HTTP_NOT_FOUND:
    1674            2 :     if (ignore_account_404)
    1675              :     {
    1676            2 :       conclude_account (wa);
    1677            2 :       return;
    1678              :     }
    1679            0 :     break;
    1680            0 :   default:
    1681            0 :     break;
    1682              :   }
    1683            0 :   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            0 :   commit (GNUNET_DB_STATUS_HARD_ERROR);
    1689            0 :   global_ret = EXIT_FAILURE;
    1690            0 :   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           12 : process_credits (void *cls)
    1705              : {
    1706           12 :   struct WireAccount *wa = cls;
    1707              :   enum GNUNET_DB_QueryStatus qs;
    1708              : 
    1709              :   /* skip accounts where CREDIT is not enabled */
    1710           12 :   while ( (NULL != wa) &&
    1711            6 :           (GNUNET_NO == wa->ai->credit_enabled) )
    1712            0 :     wa = wa->next;
    1713           12 :   if (NULL == wa)
    1714              :   {
    1715              :     /* done with all accounts, conclude check */
    1716            6 :     conclude_credit_history ();
    1717            6 :     return;
    1718              :   }
    1719            6 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1720              :               "Analyzing exchange's wire IN table for account `%s'\n",
    1721              :               wa->ai->section_name);
    1722            6 :   qs = TALER_EXCHANGEDB_iterate_reserves_in_above_serial_id_by_account (
    1723              :     TALER_ARL_edb,
    1724            6 :     wa->ai->section_name,
    1725              :     wa->last_reserve_in_serial_id,
    1726              :     &reserve_in_cb,
    1727              :     wa);
    1728            6 :   if (0 > qs)
    1729              :   {
    1730            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1731            0 :     global_ret = EXIT_FAILURE;
    1732            0 :     GNUNET_SCHEDULER_shutdown ();
    1733            0 :     return;
    1734              :   }
    1735              : 
    1736            6 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1737              :               "Analyzing exchange's KYCAUTH IN table for account `%s'\n",
    1738              :               wa->ai->section_name);
    1739            6 :   qs = TALER_EXCHANGEDB_iterate_kycauth_in_above_serial_id_by_account (
    1740              :     TALER_ARL_edb,
    1741            6 :     wa->ai->section_name,
    1742              :     wa->last_kycauth_in_serial_id,
    1743              :     &kycauth_in_cb,
    1744              :     wa);
    1745            6 :   if (0 > qs)
    1746              :   {
    1747            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1748            0 :     global_ret = EXIT_FAILURE;
    1749            0 :     GNUNET_SCHEDULER_shutdown ();
    1750            0 :     return;
    1751              :   }
    1752              : 
    1753            6 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1754              :               "Starting bank CREDIT history of account `%s'\n",
    1755              :               wa->ai->section_name);
    1756            6 :   GNUNET_assert (NULL == wa->chh);
    1757           12 :   wa->chh = TALER_BANK_credit_history (ctx,
    1758            6 :                                        wa->ai->auth,
    1759              :                                        wa->wire_off_in,
    1760              :                                        MAX_PER_TRANSACTION,
    1761            6 :                                        GNUNET_TIME_UNIT_ZERO,
    1762              :                                        &history_credit_cb,
    1763              :                                        wa);
    1764            6 :   if (NULL == wa->chh)
    1765              :   {
    1766            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1767              :                 "Failed to obtain bank transaction history\n");
    1768            0 :     commit (GNUNET_DB_STATUS_HARD_ERROR);
    1769            0 :     global_ret = EXIT_FAILURE;
    1770            0 :     GNUNET_SCHEDULER_shutdown ();
    1771            0 :     return;
    1772              :   }
    1773              : }
    1774              : 
    1775              : 
    1776              : /**
    1777              :  * Begin audit of CREDITs to the exchange.
    1778              :  */
    1779              : static void
    1780            6 : begin_credit_audit (void)
    1781              : {
    1782            6 :   GNUNET_assert (NULL == in_map);
    1783            6 :   in_map = GNUNET_CONTAINER_multihashmap_create (1024,
    1784              :                                                  GNUNET_YES);
    1785            6 :   GNUNET_assert (NULL == kycauth_map);
    1786            6 :   kycauth_map = GNUNET_CONTAINER_multihashmap_create (1024,
    1787              :                                                       GNUNET_YES);
    1788              :   /* now go over all bank accounts and check delta with in_map */
    1789            6 :   process_credits (wa_head);
    1790            6 : }
    1791              : 
    1792              : 
    1793              : static enum GNUNET_DB_QueryStatus
    1794            6 : begin_transaction (void)
    1795              : {
    1796              :   enum GNUNET_DB_QueryStatus qs;
    1797              : 
    1798            6 :   if (GNUNET_SYSERR ==
    1799            6 :       TALER_EXCHANGEDB_preflight (TALER_ARL_edb))
    1800              :   {
    1801            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1802              :                 "Failed to initialize exchange database connection.\n");
    1803            0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    1804              :   }
    1805            6 :   if (GNUNET_SYSERR ==
    1806            6 :       TALER_AUDITORDB_preflight (TALER_ARL_adb))
    1807              :   {
    1808            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1809              :                 "Failed to initialize auditor database session.\n");
    1810            0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    1811              :   }
    1812            6 :   global_qs = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
    1813            6 :   if (GNUNET_OK !=
    1814            6 :       TALER_AUDITORDB_start (TALER_ARL_adb,
    1815              :                              "auditor-wire-credit"))
    1816              :   {
    1817            0 :     GNUNET_break (0);
    1818            0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    1819              :   }
    1820            6 :   if (GNUNET_OK !=
    1821            6 :       TALER_EXCHANGEDB_start_read_only (TALER_ARL_edb,
    1822              :                                         "wire credit auditor"))
    1823              :   {
    1824            0 :     GNUNET_break (0);
    1825            0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    1826              :   }
    1827            6 :   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            6 :   switch (qs)
    1838              :   {
    1839            0 :   case GNUNET_DB_STATUS_HARD_ERROR:
    1840            0 :     GNUNET_break (0);
    1841            0 :     return qs;
    1842            0 :   case GNUNET_DB_STATUS_SOFT_ERROR:
    1843            0 :     GNUNET_break (0);
    1844            0 :     return qs;
    1845            0 :   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    1846              :   case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    1847            0 :     break;
    1848              :   }
    1849            6 :   for (struct WireAccount *wa = wa_head;
    1850           12 :        NULL != wa;
    1851            6 :        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            6 :     GNUNET_free (wa->label_reserve_in_serial_id);
    1856            6 :     GNUNET_free (wa->label_kycauth_in_serial_id);
    1857            6 :     GNUNET_free (wa->label_wire_off_in);
    1858            6 :     GNUNET_asprintf (&wa->label_reserve_in_serial_id,
    1859              :                      "wire-%s-%s",
    1860            6 :                      wa->ai->section_name,
    1861              :                      "reserve_in_serial_id");
    1862            6 :     GNUNET_asprintf (&wa->label_kycauth_in_serial_id,
    1863              :                      "wire-%s-%s",
    1864            6 :                      wa->ai->section_name,
    1865              :                      "kycauth_in_serial_id");
    1866            6 :     GNUNET_asprintf (&wa->label_wire_off_in,
    1867              :                      "wire-%s-%s",
    1868            6 :                      wa->ai->section_name,
    1869              :                      "wire_off_in");
    1870            6 :     qs = TALER_AUDITORDB_get_auditor_progress (
    1871              :       TALER_ARL_adb,
    1872            6 :       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            6 :     if (0 > qs)
    1880              :     {
    1881            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1882            0 :       return qs;
    1883              :     }
    1884            6 :     wa->start_reserve_in_serial_id = wa->last_reserve_in_serial_id;
    1885            6 :     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            6 :   begin_credit_audit ();
    1893            6 :   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            6 : process_account_cb (void *cls,
    1907              :                     const struct TALER_EXCHANGEDB_AccountInfo *ai)
    1908              : {
    1909              :   struct WireAccount *wa;
    1910              : 
    1911              :   (void) cls;
    1912            6 :   if ((! ai->debit_enabled) &&
    1913            0 :       (! ai->credit_enabled))
    1914            0 :     return; /* not an active exchange account */
    1915            6 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1916              :               "Found exchange account `%s'\n",
    1917              :               ai->section_name);
    1918            6 :   wa = GNUNET_new (struct WireAccount);
    1919            6 :   wa->ai = ai;
    1920            6 :   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            0 : 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            0 :   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            0 :   for (struct WireAccount *wa = wa_head;
    1947            0 :        NULL != wa;
    1948            0 :        wa = wa->next)
    1949              :   {
    1950            0 :     if (NULL != wa->chh)
    1951              :     {
    1952            0 :       TALER_BANK_credit_history_cancel (wa->chh);
    1953            0 :       wa->chh = NULL;
    1954              :     }
    1955            0 :     conclude_account (wa);
    1956              :   }
    1957              : 
    1958            0 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
    1959            0 :       begin_transaction ())
    1960              :   {
    1961            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1962              :                 "Audit failed\n");
    1963            0 :     GNUNET_break (0);
    1964            0 :     global_ret = EXIT_FAILURE;
    1965            0 :     GNUNET_SCHEDULER_shutdown ();
    1966              :   }
    1967            0 : }
    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            6 : 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            6 :   cfg = c;
    1988            6 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1989              :               "Launching wire-credit auditor\n");
    1990            6 :   if (EXIT_SUCCESS !=
    1991            6 :       (global_ret = TALER_ARL_init (c)))
    1992              :   {
    1993            0 :     return;
    1994              :   }
    1995            6 :   if (GNUNET_OK !=
    1996            6 :       TALER_config_get_amount (TALER_ARL_cfg,
    1997              :                                "auditor",
    1998              :                                "TINY_AMOUNT",
    1999              :                                &tiny_amount))
    2000              :   {
    2001            0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    2002              :                                "auditor",
    2003              :                                "TINY_AMOUNT");
    2004            0 :     global_ret = EXIT_NOTCONFIGURED;
    2005            0 :     return;
    2006              :   }
    2007            6 :   GNUNET_assert (GNUNET_OK ==
    2008              :                  TALER_amount_set_zero (TALER_ARL_currency,
    2009              :                                         &zero));
    2010            6 :   GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
    2011              :                                  NULL);
    2012            6 :   ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
    2013              :                           &rc);
    2014            6 :   rc = GNUNET_CURL_gnunet_rc_create (ctx);
    2015            6 :   if (NULL == ctx)
    2016              :   {
    2017            0 :     GNUNET_break (0);
    2018            0 :     global_ret = EXIT_FAILURE;
    2019            0 :     return;
    2020              :   }
    2021            6 :   if (GNUNET_OK !=
    2022            6 :       TALER_EXCHANGEDB_load_accounts (TALER_ARL_cfg,
    2023              :                                       TALER_EXCHANGEDB_ALO_CREDIT
    2024              :                                       | TALER_EXCHANGEDB_ALO_AUTHDATA))
    2025              :   {
    2026            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    2027              :                 "No bank accounts configured\n");
    2028            0 :     global_ret = EXIT_NOTCONFIGURED;
    2029            0 :     GNUNET_SCHEDULER_shutdown ();
    2030            0 :     return;
    2031              :   }
    2032            6 :   TALER_EXCHANGEDB_find_accounts (&process_account_cb,
    2033              :                                   NULL);
    2034              : 
    2035            6 :   if (0 == test_mode)
    2036              :   {
    2037            0 :     struct GNUNET_DB_EventHeaderP es = {
    2038            0 :       .size = htons (sizeof (es)),
    2039            0 :       .type = htons (TALER_DBEVENT_EXCHANGE_AUDITOR_WAKE_HELPER_WIRE)
    2040              :     };
    2041              : 
    2042            0 :     eh = TALER_AUDITORDB_event_listen (TALER_ARL_adb,
    2043              :                                        &es,
    2044            0 :                                        GNUNET_TIME_UNIT_FOREVER_REL,
    2045              :                                        &db_notify,
    2046              :                                        NULL);
    2047            0 :     GNUNET_assert (NULL != eh);
    2048              :   }
    2049            6 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
    2050            6 :       begin_transaction ())
    2051              :   {
    2052            0 :     GNUNET_break (0);
    2053            0 :     global_ret = EXIT_FAILURE;
    2054            0 :     GNUNET_SCHEDULER_shutdown ();
    2055            0 :     return;
    2056              :   }
    2057              : }
    2058              : 
    2059              : 
    2060              : /**
    2061              :  * The main function of the wire auditing tool. Checks that
    2062              :  * the exchange's records of wire transfers match that of
    2063              :  * the wire gateway.
    2064              :  *
    2065              :  * @param argc number of arguments from the command line
    2066              :  * @param argv command line arguments
    2067              :  * @return 0 ok, 1 on error
    2068              :  */
    2069              : int
    2070            6 : main (int argc,
    2071              :       char *const *argv)
    2072              : {
    2073            6 :   const struct GNUNET_GETOPT_CommandLineOption options[] = {
    2074            6 :     GNUNET_GETOPT_option_flag ('i',
    2075              :                                "internal",
    2076              :                                "perform checks only applicable for exchange-internal audits",
    2077              :                                &internal_checks),
    2078            6 :     GNUNET_GETOPT_option_flag ('I',
    2079              :                                "ignore-not-found",
    2080              :                                "continue, even if the bank account of the exchange was not found",
    2081              :                                &ignore_account_404),
    2082            6 :     GNUNET_GETOPT_option_flag ('t',
    2083              :                                "test",
    2084              :                                "run in test mode and exit when idle",
    2085              :                                &test_mode),
    2086            6 :     GNUNET_GETOPT_option_timetravel ('T',
    2087              :                                      "timetravel"),
    2088              :     GNUNET_GETOPT_OPTION_END
    2089              :   };
    2090              :   enum GNUNET_GenericReturnValue ret;
    2091              : 
    2092            6 :   ret = GNUNET_PROGRAM_run (
    2093              :     TALER_AUDITOR_project_data (),
    2094              :     argc,
    2095              :     argv,
    2096              :     "taler-helper-auditor-wire-credit",
    2097              :     gettext_noop (
    2098              :       "Audit exchange database for consistency with the bank's wire transfers"),
    2099              :     options,
    2100              :     &run,
    2101              :     NULL);
    2102            6 :   if (GNUNET_SYSERR == ret)
    2103            0 :     return EXIT_NOTCONFIGURED;
    2104            6 :   if (GNUNET_NO == ret)
    2105            0 :     return EXIT_SUCCESS;
    2106            6 :   return global_ret;
    2107              : }
    2108              : 
    2109              : 
    2110              : /* end of taler-helper-auditor-wire-credit.c */
        

Generated by: LCOV version 2.0-1