LCOV - code coverage report
Current view: top level - auditor - taler-helper-auditor-wire-credit.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 249 463 53.8 %
Date: 2025-06-05 21:03:14 Functions: 14 18 77.8 %

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

Generated by: LCOV version 1.16