LCOV - code coverage report
Current view: top level - auditor - taler-helper-auditor-wire-debit.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 45.4 % 566 257
Test Date: 2026-10-17 15:46:49 Functions: 70.8 % 24 17

            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-debit.c
      18              :  * @brief audits that wire outgoing transfers match those from an exchange
      19              :  * database.
      20              :  * @author Christian Grothoff
      21              :  * @author Özgür Kesim
      22              :  *
      23              :  * - We check that the outgoing wire transfers match those
      24              :  *   given in the 'wire_out' and 'reserve_closures' tables;
      25              :  *   any outgoing transfer MUST have a prior justification,
      26              :  *   so if one is missing we flag it (and never remove it).
      27              :  * - We check that all wire transfers that should
      28              :  *   have been made, were actually made. If any were not made,
      29              :  *   we flag those, but may remove those flags if we later
      30              :  *   find that the wire transfers were made (wire transfers
      31              :  *   could be delayed due to AML/KYC or core-banking issues).
      32              :  */
      33              : #include "platform.h"
      34              : #include <gnunet/gnunet_util_lib.h>
      35              : #include <gnunet/gnunet_curl_lib.h>
      36              : #include "auditordb_lib.h"
      37              : /* WIRE_TRANSFER_OUT's callback typedef also lives in exchangedb_lib.h, so its
      38              :    closure override must be established before that header is included. */
      39              : struct WireAccount;
      40              : #define TALER_EXCHANGEDB_WIRE_TRANSFER_OUT_RESULT_CLOSURE struct WireAccount
      41              : #include "exchangedb_lib.h"
      42              : #include "taler/taler_json_lib.h"
      43              : #include "taler/taler_bank_service.h"
      44              : #include "taler/taler_signatures.h"
      45              : #include "report-lib.h"
      46              : #include "taler/taler_dbevents.h"
      47              : #include "auditor-database/delete_auditor_closure_lag.h"
      48              : #include "auditor-database/delete_wire_out_inconsistency_if_matching.h"
      49              : #include "auditor-database/event_listen.h"
      50              : #include "auditor-database/get_auditor_progress.h"
      51              : #include "auditor-database/get_balance.h"
      52              : #include "auditor-database/insert_auditor_closure_lags.h"
      53              : #include "auditor-database/insert_auditor_progress.h"
      54              : #include "auditor-database/insert_balance.h"
      55              : #include "auditor-database/insert_row_inconsistency.h"
      56              : #include "auditor-database/insert_row_minor_inconsistencies.h"
      57              : #include "auditor-database/insert_wire_format_inconsistency.h"
      58              : #include "auditor-database/insert_wire_out_inconsistency.h"
      59              : #include "auditor-database/preflight.h"
      60              : #include "auditor-database/start.h"
      61              : #include "exchange-database/get_profit_drain.h"
      62              : #include "exchange-database/preflight.h"
      63              : #include "exchange-database/rollback.h"
      64              : #include "exchange-database/iterate_reserve_closed_above_serial_id.h"
      65              : #include "exchange-database/iterate_wire_outs_above_serial_id_by_account.h"
      66              : #include "exchange-database/start_read_only.h"
      67              : 
      68              : 
      69              : /**
      70              :  * Maximum number of wire transfers we process per
      71              :  * (database) transaction.
      72              :  */
      73              : #define MAX_PER_TRANSACTION 1024
      74              : 
      75              : /**
      76              :  * How much do we allow the bank and the exchange to disagree about
      77              :  * timestamps? Should be sufficiently large to avoid bogus reports from deltas
      78              :  * created by imperfect clock synchronization and network delay.
      79              :  */
      80              : #define TIME_TOLERANCE GNUNET_TIME_relative_multiply ( \
      81              :           GNUNET_TIME_UNIT_MINUTES, \
      82              :           15)
      83              : 
      84              : 
      85              : /**
      86              :  * How long do we try to long-poll for bank wire transfers?
      87              :  */
      88              : #define MAX_LONGPOLL_DELAY GNUNET_TIME_relative_multiply ( \
      89              :           GNUNET_TIME_UNIT_HOURS, \
      90              :           1)
      91              : 
      92              : 
      93              : /**
      94              :  * How long do we wait between polling for bank wire transfers at the minimum?
      95              :  */
      96              : #define MIN_LONGPOLL_DELAY GNUNET_TIME_relative_multiply ( \
      97              :           GNUNET_TIME_UNIT_MINUTES, \
      98              :           5)
      99              : 
     100              : 
     101              : /**
     102              :  * Run in test mode. Exit when idle instead of
     103              :  * going to sleep and waiting for more work.
     104              :  */
     105              : static int test_mode;
     106              : 
     107              : 
     108              : /**
     109              :  * Information we keep for each supported account.
     110              :  */
     111              : struct WireAccount
     112              : {
     113              :   /**
     114              :    * Accounts are kept in a DLL.
     115              :    */
     116              :   struct WireAccount *next;
     117              : 
     118              :   /**
     119              :    * Plugins are kept in a DLL.
     120              :    */
     121              :   struct WireAccount *prev;
     122              : 
     123              :   /**
     124              :    * Account details.
     125              :    */
     126              :   const struct TALER_EXCHANGEDB_AccountInfo *ai;
     127              : 
     128              :   /**
     129              :    * Active wire request for the transaction history.
     130              :    */
     131              :   struct TALER_BANK_DebitHistoryHandle *dhh;
     132              : 
     133              :   /**
     134              :    * Task to trigger @e dhh long-polling.
     135              :    */
     136              :   struct GNUNET_SCHEDULER_Task *dhh_task;
     137              : 
     138              :   /**
     139              :    * Time when we expect the current @e dhh long-poll
     140              :    * to finish and we thus could begin another one.
     141              :    */
     142              :   struct GNUNET_TIME_Absolute dhh_next;
     143              : 
     144              :   /**
     145              :    * Progress point for this account.
     146              :    */
     147              :   uint64_t last_wire_out_serial_id;
     148              : 
     149              :   /**
     150              :    * Initial progress point for this account.
     151              :    */
     152              :   uint64_t start_wire_out_serial_id;
     153              : 
     154              :   /**
     155              :    * Where we are in the outbound transaction history.
     156              :    */
     157              :   uint64_t wire_off_out;
     158              : 
     159              :   /**
     160              :    * Label under which we store our pp's reserve_in_serial_id.
     161              :    */
     162              :   char *label_wire_out_serial_id;
     163              : 
     164              :   /**
     165              :    * Label under which we store our wire_off_out.
     166              :    */
     167              :   char *label_wire_off_out;
     168              : };
     169              : 
     170              : 
     171              : /**
     172              :  * Information we track for a reserve being closed.
     173              :  */
     174              : struct ReserveClosure
     175              : {
     176              :   /**
     177              :    * Row in the reserves_closed table for this action.
     178              :    */
     179              :   uint64_t rowid;
     180              : 
     181              :   /**
     182              :    * When was the reserve closed?
     183              :    */
     184              :   struct GNUNET_TIME_Timestamp execution_date;
     185              : 
     186              :   /**
     187              :    * Amount transferred (amount remaining minus fee).
     188              :    */
     189              :   struct TALER_Amount amount;
     190              : 
     191              :   /**
     192              :    * Target account where the money was sent.
     193              :    */
     194              :   struct TALER_FullPayto receiver_account;
     195              : 
     196              :   /**
     197              :    * Wire transfer subject used.
     198              :    */
     199              :   struct TALER_WireTransferIdentifierRawP wtid;
     200              : };
     201              : 
     202              : 
     203              : /**
     204              :  * Map from H(wtid,receiver_account) to `struct ReserveClosure` entries.
     205              :  */
     206              : static struct GNUNET_CONTAINER_MultiHashMap *reserve_closures;
     207              : 
     208              : /**
     209              :  * How often do we retry the transaction after a serialization
     210              :  * failure before we give up?
     211              :  */
     212              : #define MAX_RETRIES 3
     213              : 
     214              : /**
     215              :  * Return value from main().
     216              :  */
     217              : static int global_ret;
     218              : 
     219              : /**
     220              :  * Number of serialization failures we have retried since the last
     221              :  * successfully committed transaction.
     222              :  */
     223              : static unsigned int retries;
     224              : 
     225              : /**
     226              :  * State of the current database transaction with
     227              :  * the auditor DB.
     228              :  */
     229              : static enum GNUNET_DB_QueryStatus global_qs;
     230              : 
     231              : /**
     232              :  * Map with information about outgoing wire transfers.
     233              :  * Maps hashes of the wire subjects (in binary encoding)
     234              :  * to `struct ReserveOutInfo`s.
     235              :  */
     236              : static struct GNUNET_CONTAINER_MultiHashMap *out_map;
     237              : 
     238              : /**
     239              :  * Head of list of wire accounts we still need to look at.
     240              :  */
     241              : static struct WireAccount *wa_head;
     242              : 
     243              : /**
     244              :  * Tail of list of wire accounts we still need to look at.
     245              :  */
     246              : static struct WireAccount *wa_tail;
     247              : 
     248              : /**
     249              :  * Last reserve_out / wire_out serial IDs seen.
     250              :  */
     251              : static TALER_ARL_DEF_PP (wire_reserve_close_id);
     252              : 
     253              : /**
     254              :  * Total amount that was transferred too much from the exchange.
     255              :  */
     256              : static TALER_ARL_DEF_AB (total_bad_amount_out_plus);
     257              : 
     258              : /**
     259              :  * Total amount that was transferred too little from the exchange.
     260              :  */
     261              : static TALER_ARL_DEF_AB (total_bad_amount_out_minus);
     262              : 
     263              : /**
     264              :  * Total amount of reserve closures which the exchange did not transfer in time.
     265              :  */
     266              : static TALER_ARL_DEF_AB (total_closure_amount_lag);
     267              : 
     268              : /**
     269              :  * Total amount affected by duplicate wire transfer
     270              :  * subjects.
     271              :  */
     272              : static TALER_ARL_DEF_AB (wire_debit_duplicate_transfer_subject_total);
     273              : 
     274              : /**
     275              :  * Total amount debited to exchange accounts.
     276              :  */
     277              : static TALER_ARL_DEF_AB (total_wire_out);
     278              : 
     279              : /**
     280              :  * Total fees the bank charged the exchange for outgoing wire
     281              :  * transfers, on top of the amounts transferred.
     282              :  */
     283              : static TALER_ARL_DEF_AB (total_wire_debit_fees);
     284              : 
     285              : /**
     286              :  * Total amount of profits drained.
     287              :  */
     288              : static TALER_ARL_DEF_AB (total_drained);
     289              : 
     290              : /**
     291              :  * Amount of zero in our currency.
     292              :  */
     293              : static struct TALER_Amount zero;
     294              : 
     295              : /**
     296              :  * Handle to the context for interacting with the bank.
     297              :  */
     298              : static struct GNUNET_CURL_Context *ctx;
     299              : 
     300              : /**
     301              :  * Scheduler context for running the @e ctx.
     302              :  */
     303              : static struct GNUNET_CURL_RescheduleContext *rctx;
     304              : 
     305              : /**
     306              :  * Should we run checks that only work for exchange-internal audits?
     307              :  */
     308              : static int internal_checks;
     309              : 
     310              : /**
     311              :  * Should we ignore if the bank does not know our bank
     312              :  * account?
     313              :  */
     314              : static int ignore_account_404;
     315              : 
     316              : /**
     317              :  * Database event handler to wake us up again.
     318              :  */
     319              : static struct GNUNET_DB_EventHandler *eh;
     320              : 
     321              : /**
     322              :  * The auditors's configuration.
     323              :  */
     324              : static const struct GNUNET_CONFIGURATION_Handle *cfg;
     325              : 
     326              : 
     327              : /**
     328              :  * Entry in map with wire information we expect to obtain from the
     329              :  * #TALER_ARL_edb later.
     330              :  */
     331              : struct WireTransferOutInfo
     332              : {
     333              : 
     334              :   /**
     335              :    * Hash of the wire transfer subject.
     336              :    */
     337              :   struct GNUNET_HashCode subject_hash;
     338              : 
     339              :   /**
     340              :    * Expected details about the wire transfer.
     341              :    */
     342              :   struct TALER_BANK_DebitDetails details;
     343              : 
     344              : };
     345              : 
     346              : 
     347              : /**
     348              :  * Free entry in #out_map.
     349              :  *
     350              :  * @param cls NULL
     351              :  * @param key unused key
     352              :  * @param value the `struct WireTransferOutInfo` to free
     353              :  * @return #GNUNET_OK
     354              :  */
     355              : static enum GNUNET_GenericReturnValue
     356           30 : free_roi (void *cls,
     357              :           const struct GNUNET_HashCode *key,
     358              :           void *value)
     359              : {
     360           30 :   struct WireTransferOutInfo *roi = value;
     361              : 
     362              :   (void) cls;
     363           30 :   GNUNET_assert (GNUNET_YES ==
     364              :                  GNUNET_CONTAINER_multihashmap_remove (out_map,
     365              :                                                        key,
     366              :                                                        roi));
     367           30 :   GNUNET_free (roi);
     368           30 :   return GNUNET_OK;
     369              : }
     370              : 
     371              : 
     372              : /**
     373              :  * Free entry in #reserve_closures.
     374              :  *
     375              :  * @param cls NULL
     376              :  * @param key unused key
     377              :  * @param value the `struct ReserveClosure` to free
     378              :  * @return #GNUNET_OK
     379              :  */
     380              : static enum GNUNET_GenericReturnValue
     381            2 : free_rc (void *cls,
     382              :          const struct GNUNET_HashCode *key,
     383              :          void *value)
     384              : {
     385            2 :   struct ReserveClosure *rc = value;
     386              : 
     387              :   (void) cls;
     388            2 :   GNUNET_assert (GNUNET_YES ==
     389              :                  GNUNET_CONTAINER_multihashmap_remove (reserve_closures,
     390              :                                                        key,
     391              :                                                        rc));
     392            2 :   GNUNET_free (rc->receiver_account.full_payto);
     393            2 :   GNUNET_free (rc);
     394            2 :   return GNUNET_OK;
     395              : }
     396              : 
     397              : 
     398              : /**
     399              :  * Task run on shutdown.
     400              :  *
     401              :  * @param cls NULL
     402              :  */
     403              : static void
     404            6 : do_shutdown (void *cls)
     405              : {
     406              :   struct WireAccount *wa;
     407              : 
     408              :   (void) cls;
     409            6 :   if (NULL != eh)
     410              :   {
     411            0 :     TALER_AUDITORDB_event_listen_cancel (eh);
     412            0 :     eh = NULL;
     413              :   }
     414            6 :   TALER_ARL_done ();
     415            6 :   if (NULL != reserve_closures)
     416              :   {
     417            6 :     GNUNET_CONTAINER_multihashmap_iterate (reserve_closures,
     418              :                                            &free_rc,
     419              :                                            NULL);
     420            6 :     GNUNET_CONTAINER_multihashmap_destroy (reserve_closures);
     421            6 :     reserve_closures = NULL;
     422              :   }
     423            6 :   if (NULL != out_map)
     424              :   {
     425            6 :     GNUNET_CONTAINER_multihashmap_iterate (out_map,
     426              :                                            &free_roi,
     427              :                                            NULL);
     428            6 :     GNUNET_CONTAINER_multihashmap_destroy (out_map);
     429            6 :     out_map = NULL;
     430              :   }
     431           12 :   while (NULL != (wa = wa_head))
     432              :   {
     433            6 :     if (NULL != wa->dhh_task)
     434              :     {
     435            6 :       GNUNET_SCHEDULER_cancel (wa->dhh_task);
     436            6 :       wa->dhh_task = NULL;
     437              :     }
     438            6 :     if (NULL != wa->dhh)
     439              :     {
     440            0 :       TALER_BANK_debit_history_cancel (wa->dhh);
     441            0 :       wa->dhh = NULL;
     442              :     }
     443            6 :     GNUNET_CONTAINER_DLL_remove (wa_head,
     444              :                                  wa_tail,
     445              :                                  wa);
     446            6 :     GNUNET_free (wa->label_wire_out_serial_id);
     447            6 :     GNUNET_free (wa->label_wire_off_out);
     448            6 :     GNUNET_free (wa);
     449              :   }
     450            6 :   if (NULL != ctx)
     451              :   {
     452            6 :     GNUNET_CURL_fini (ctx);
     453            6 :     ctx = NULL;
     454              :   }
     455            6 :   if (NULL != rctx)
     456              :   {
     457            6 :     GNUNET_CURL_gnunet_rc_destroy (rctx);
     458            6 :     rctx = NULL;
     459              :   }
     460            6 :   TALER_EXCHANGEDB_unload_accounts ();
     461            6 :   TALER_ARL_cfg = NULL;
     462            6 : }
     463              : 
     464              : 
     465              : /**
     466              :  * Detect any entries in #reserve_closures that were not yet
     467              :  * observed on the wire transfer side and update the progress
     468              :  * point accordingly.
     469              :  *
     470              :  * @param cls NULL
     471              :  * @param key unused key
     472              :  * @param value the `struct ReserveClosure` to free
     473              :  * @return #GNUNET_OK
     474              :  */
     475              : static enum GNUNET_GenericReturnValue
     476            0 : check_pending_rc (void *cls,
     477              :                   const struct GNUNET_HashCode *key,
     478              :                   void *value)
     479              : {
     480            0 :   struct ReserveClosure *rc = value;
     481              : 
     482              :   (void) cls;
     483              :   (void) key;
     484            0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     485              :               "Missing wire transfer for closed reserve with balance %s\n",
     486              :               TALER_amount2s (&rc->amount));
     487            0 :   if (! TALER_amount_is_zero (&rc->amount))
     488              :   {
     489            0 :     struct TALER_AUDITORDB_ClosureLags cl = {
     490            0 :       .problem_row_id = rc->rowid,
     491              :       .account = rc->receiver_account,
     492              :       .amount = rc->amount,
     493              :       .deadline = rc->execution_date.abs_time,
     494              :       .wtid = rc->wtid
     495              :     };
     496              :     enum GNUNET_DB_QueryStatus qs;
     497              : 
     498            0 :     qs = TALER_AUDITORDB_insert_auditor_closure_lags (
     499              :       TALER_ARL_adb,
     500              :       &cl);
     501            0 :     if (qs < 0)
     502              :     {
     503            0 :       global_qs = qs;
     504            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     505            0 :       return GNUNET_SYSERR;
     506              :     }
     507            0 :     TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_closure_amount_lag),
     508              :                           &TALER_ARL_USE_AB (total_closure_amount_lag),
     509              :                           &rc->amount);
     510              :   }
     511            0 :   return GNUNET_OK;
     512              : }
     513              : 
     514              : 
     515              : /**
     516              :  * Compute the key under which a reserve closure for a given
     517              :  * @a receiver_account and @a wtid would be stored.
     518              :  *
     519              :  * @param receiver_account payto://-URI of the account
     520              :  * @param wtid wire transfer identifier used
     521              :  * @param[out] key set to the key
     522              :  */
     523              : static void
     524            4 : hash_rc (const struct TALER_FullPayto receiver_account,
     525              :          const struct TALER_WireTransferIdentifierRawP *wtid,
     526              :          struct GNUNET_HashCode *key)
     527            4 : {
     528              :   struct TALER_NormalizedPayto npto
     529            4 :     = TALER_payto_normalize (receiver_account);
     530            4 :   size_t slen = strlen (npto.normalized_payto);
     531            4 :   char buf[sizeof (struct TALER_WireTransferIdentifierRawP) + slen];
     532              : 
     533            4 :   GNUNET_memcpy (buf,
     534              :                  wtid,
     535              :                  sizeof (*wtid));
     536            4 :   GNUNET_memcpy (&buf[sizeof (*wtid)],
     537              :                  npto.normalized_payto,
     538              :                  slen);
     539            4 :   GNUNET_CRYPTO_hash (buf,
     540              :                       sizeof (buf),
     541              :                       key);
     542            4 :   GNUNET_free (npto.normalized_payto);
     543            4 : }
     544              : 
     545              : 
     546              : /**
     547              :  * Start the database transactions and begin the audit.
     548              :  *
     549              :  * @return transaction status code
     550              :  */
     551              : static enum GNUNET_DB_QueryStatus
     552              : begin_transaction (void);
     553              : 
     554              : 
     555              : /**
     556              :  * Commit the transaction, checkpointing our progress in the auditor DB.
     557              :  *
     558              :  * @param qs transaction status so far
     559              :  */
     560              : static void
     561            6 : commit (enum GNUNET_DB_QueryStatus qs)
     562              : {
     563            6 :   GNUNET_CONTAINER_multihashmap_iterate (reserve_closures,
     564              :                                          &check_pending_rc,
     565              :                                          NULL);
     566            6 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     567              :               "Transaction logic ended with status %d\n",
     568              :               qs);
     569            6 :   TALER_EXCHANGEDB_rollback (TALER_ARL_edb);
     570            6 :   qs = TALER_AUDITORDB_insert_balance (
     571              :     TALER_ARL_adb,
     572              :     TALER_ARL_SET_AB (total_drained),
     573              :     TALER_ARL_SET_AB (total_wire_out),
     574              :     TALER_ARL_SET_AB (total_bad_amount_out_plus),
     575              :     TALER_ARL_SET_AB (total_bad_amount_out_minus),
     576              :     TALER_ARL_SET_AB (total_closure_amount_lag),
     577              :     TALER_ARL_SET_AB (wire_debit_duplicate_transfer_subject_total),
     578              :     TALER_ARL_SET_AB (total_wire_out),
     579              :     TALER_ARL_SET_AB (total_wire_debit_fees),
     580              :     NULL);
     581            6 :   if (0 > qs)
     582            0 :     goto handle_db_error;
     583            6 :   for (struct WireAccount *wa = wa_head;
     584           12 :        NULL != wa;
     585            6 :        wa = wa->next)
     586              :   {
     587            6 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     588              :                 "Transaction of account %s ends at %llu/%llu\n",
     589              :                 wa->ai->section_name,
     590              :                 (unsigned long long) wa->last_wire_out_serial_id,
     591              :                 (unsigned long long) wa->wire_off_out);
     592            6 :     qs = TALER_AUDITORDB_insert_auditor_progress (
     593              :       TALER_ARL_adb,
     594            6 :       wa->label_wire_out_serial_id,
     595              :       wa->last_wire_out_serial_id,
     596              :       wa->label_wire_off_out,
     597              :       wa->wire_off_out,
     598              :       NULL);
     599            6 :     if (0 > qs)
     600            0 :       goto handle_db_error;
     601            6 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     602              :                 "Transaction ends at %s=%llu for account `%s'\n",
     603              :                 wa->label_wire_out_serial_id,
     604              :                 (unsigned long long) wa->last_wire_out_serial_id,
     605              :                 wa->ai->section_name);
     606              :   }
     607            6 :   qs = TALER_AUDITORDB_insert_auditor_progress (
     608              :     TALER_ARL_adb,
     609              :     TALER_ARL_SET_PP (wire_reserve_close_id),
     610              :     NULL);
     611            6 :   if (0 > qs)
     612            0 :     goto handle_db_error;
     613            6 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     614              :               "Concluded audit step at %llu\n",
     615              :               (unsigned long long) TALER_ARL_USE_PP (wire_reserve_close_id));
     616            6 :   qs = TALER_AUDITORDB_commit (TALER_ARL_adb);
     617            6 :   if (0 > qs)
     618            0 :     goto handle_db_error;
     619            6 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     620              :               "Transaction concluded!\n");
     621            6 :   retries = 0;
     622            6 :   if (1 == test_mode)
     623            6 :     GNUNET_SCHEDULER_shutdown ();
     624            6 :   return;
     625            0 : handle_db_error:
     626            0 :   TALER_AUDITORDB_rollback (TALER_ARL_adb);
     627            0 :   if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
     628              :   {
     629            0 :     if (MAX_RETRIES < ++retries)
     630              :     {
     631            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     632              :                   "Failed to commit the transaction after %u retries, terminating\n",
     633              :                   (unsigned int) MAX_RETRIES);
     634            0 :       global_ret = EXIT_FAILURE;
     635            0 :       GNUNET_SCHEDULER_shutdown ();
     636            0 :       return;
     637              :     }
     638            0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     639              :                 "Serialization issue, trying again\n");
     640              :     /* Any bank request still in flight belongs to the attempt we are
     641              :        abandoning.  #process_debits() would leave it alone and start
     642              :        nothing, so the restarted audit would never make progress. */
     643            0 :     for (struct WireAccount *wa = wa_head;
     644            0 :          NULL != wa;
     645            0 :          wa = wa->next)
     646              :     {
     647            0 :       if (NULL != wa->dhh)
     648              :       {
     649            0 :         TALER_BANK_debit_history_cancel (wa->dhh);
     650            0 :         wa->dhh = NULL;
     651              :       }
     652            0 :       if (NULL != wa->dhh_task)
     653              :       {
     654            0 :         GNUNET_SCHEDULER_cancel (wa->dhh_task);
     655            0 :         wa->dhh_task = NULL;
     656              :       }
     657              :     }
     658            0 :     if (GNUNET_DB_STATUS_HARD_ERROR != begin_transaction ())
     659            0 :       return; /* the retry continues asynchronously from here */
     660              :   }
     661            0 :   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     662              :               "Hard database error, terminating\n");
     663            0 :   global_ret = EXIT_FAILURE;
     664            0 :   GNUNET_SCHEDULER_shutdown ();
     665              : }
     666              : 
     667              : 
     668              : /**
     669              :  * Check that @a want is within #TIME_TOLERANCE of @a have.
     670              :  * Otherwise report an inconsistency in row @a rowid of @a table.
     671              :  *
     672              :  * @param table where is the inconsistency (if any)
     673              :  * @param rowid what is the row
     674              :  * @param want what is the expected time
     675              :  * @param have what is the time we got
     676              :  * @return true on success, false to abort
     677              :  */
     678              : static bool
     679           30 : check_time_difference (const char *table,
     680              :                        uint64_t rowid,
     681              :                        struct GNUNET_TIME_Timestamp want,
     682              :                        struct GNUNET_TIME_Timestamp have)
     683              : {
     684              :   struct GNUNET_TIME_Relative delta;
     685              :   char *details;
     686              : 
     687           30 :   if (GNUNET_TIME_timestamp_cmp (have, >, want))
     688           28 :     delta = GNUNET_TIME_absolute_get_difference (want.abs_time,
     689              :                                                  have.abs_time);
     690              :   else
     691            2 :     delta = GNUNET_TIME_absolute_get_difference (have.abs_time,
     692              :                                                  want.abs_time);
     693           30 :   if (GNUNET_TIME_relative_cmp (delta,
     694              :                                 <=,
     695              :                                 TIME_TOLERANCE))
     696           30 :     return true;
     697              : 
     698            0 :   GNUNET_asprintf (&details,
     699              :                    "execution date mismatch (%s)",
     700              :                    GNUNET_TIME_relative2s (delta,
     701              :                                            true));
     702              :   {
     703            0 :     struct TALER_AUDITORDB_RowMinorInconsistencies rmi = {
     704              :       .row_table = (char *) table,
     705              :       .problem_row = rowid,
     706              :       .diagnostic = details
     707              :     };
     708              :     enum GNUNET_DB_QueryStatus qs;
     709              : 
     710            0 :     qs = TALER_AUDITORDB_insert_row_minor_inconsistencies (
     711              :       TALER_ARL_adb,
     712              :       &rmi);
     713              : 
     714            0 :     if (qs < 0)
     715              :     {
     716            0 :       global_qs = qs;
     717            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     718            0 :       GNUNET_free (details);
     719            0 :       return false;
     720              :     }
     721              :   }
     722            0 :   GNUNET_free (details);
     723            0 :   return true;
     724              : }
     725              : 
     726              : 
     727              : /**
     728              :  * Closure for #check_rc_matches
     729              :  */
     730              : struct CheckMatchContext
     731              : {
     732              : 
     733              :   /**
     734              :    * Reserve operation looking for a match
     735              :    */
     736              :   const struct WireTransferOutInfo *roi;
     737              : 
     738              :   /**
     739              :    * Set to true if we found a match.
     740              :    */
     741              :   bool found;
     742              : };
     743              : 
     744              : 
     745              : /**
     746              :  * Check if any of the reserve closures match the given wire transfer.
     747              :  *
     748              :  * @param[in,out] cls a `struct CheckMatchContext`
     749              :  * @param key key of @a value in #reserve_closures
     750              :  * @param value a `struct ReserveClosure`
     751              :  */
     752              : static enum GNUNET_GenericReturnValue
     753            2 : check_rc_matches (void *cls,
     754              :                   const struct GNUNET_HashCode *key,
     755              :                   void *value)
     756              : {
     757            2 :   struct CheckMatchContext *cmx = cls;
     758            2 :   struct ReserveClosure *rc = value;
     759              : 
     760            2 :   if ((0 == GNUNET_memcmp (&cmx->roi->details.wtid,
     761            2 :                            &rc->wtid)) &&
     762            2 :       (0 == TALER_full_payto_cmp (rc->receiver_account,
     763            4 :                                   cmx->roi->details.credit_account_uri)) &&
     764            2 :       (0 == TALER_amount_cmp (&rc->amount,
     765            2 :                               &cmx->roi->details.amount)))
     766              :   {
     767            2 :     if (! check_time_difference ("reserves_closures",
     768              :                                  rc->rowid,
     769              :                                  rc->execution_date,
     770            2 :                                  cmx->roi->details.execution_date))
     771              :     {
     772            0 :       free_rc (NULL,
     773              :                key,
     774              :                rc);
     775            0 :       return GNUNET_SYSERR;
     776              :     }
     777            2 :     cmx->found = true;
     778            2 :     free_rc (NULL,
     779              :              key,
     780              :              rc);
     781            2 :     return GNUNET_NO;
     782              :   }
     783            0 :   return GNUNET_OK;
     784              : }
     785              : 
     786              : 
     787              : /**
     788              :  * Maximum required length for make_missing_diag().
     789              :  */
     790              : #define MAX_DIAG_LEN 128
     791              : 
     792              : /**
     793              :  * Make diagnostic string for missing wire transfer.
     794              :  *
     795              :  * @param[out] diag where to write the diagnostic string
     796              :  * @param wtid wire transfer ID to include
     797              :  */
     798              : static void
     799            0 : make_missing_diag (char diag[MAX_DIAG_LEN],
     800              :                    const struct TALER_WireTransferIdentifierRawP *wtid)
     801              : {
     802              :   char *wtid_s;
     803              : 
     804            0 :   wtid_s = GNUNET_STRINGS_data_to_string_alloc (wtid,
     805              :                                                 sizeof (*wtid));
     806            0 :   GNUNET_snprintf (diag,
     807              :                    MAX_DIAG_LEN,
     808              :                    "expected outgoing wire transfer %s missing",
     809              :                    wtid_s);
     810            0 :   GNUNET_free (wtid_s);
     811            0 : }
     812              : 
     813              : 
     814              : /**
     815              :  * Check if an existing report on a missing wire
     816              :  * out operation justified the @a roi. If so,
     817              :  * clear the existing report.
     818              :  *
     819              :  * @param roi reserve out operation to check
     820              :  * @return #GNUNET_YES if @a roi was justified by a previous report,
     821              :  *         #GNUNET_NO of @a roi was not justified by a previous missing report
     822              :  *         #GNUNET_SYSERR on database trouble
     823              :  */
     824              : static enum GNUNET_GenericReturnValue
     825            0 : check_reported_inconsistency (struct WireTransferOutInfo *roi)
     826              : {
     827              :   char diag[MAX_DIAG_LEN];
     828            0 :   struct TALER_AUDITORDB_WireOutInconsistency woi = {
     829            0 :     .wire_out_row_id = roi->details.serial_id,
     830              :     .destination_account = roi->details.credit_account_uri,
     831              :     .diagnostic = diag,
     832              :     .expected = roi->details.amount,
     833              :     .claimed = zero,
     834              :   };
     835              :   enum GNUNET_DB_QueryStatus qs;
     836              : 
     837            0 :   make_missing_diag (diag,
     838            0 :                      &roi->details.wtid);
     839            0 :   qs = TALER_AUDITORDB_delete_wire_out_inconsistency_if_matching (
     840              :     TALER_ARL_adb,
     841              :     &woi);
     842            0 :   if (qs < 0)
     843              :   {
     844            0 :     global_qs = qs;
     845            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     846            0 :     return GNUNET_SYSERR;
     847              :   }
     848            0 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
     849              :   {
     850            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     851              :                 "Deletion of wire out inconsistency %llu (%s, %s, %s) failed: not reported missing!\n",
     852              :                 (unsigned long long) roi->details.serial_id,
     853              :                 roi->details.credit_account_uri.full_payto,
     854              :                 diag,
     855              :                 TALER_amount2s (&roi->details.amount));
     856            0 :     return GNUNET_NO;
     857              :   }
     858            0 :   TALER_ARL_amount_subtract (&TALER_ARL_USE_AB (total_bad_amount_out_minus),
     859              :                              &TALER_ARL_USE_AB (total_bad_amount_out_minus),
     860              :                              &roi->details.amount);
     861            0 :   return GNUNET_YES;
     862              : }
     863              : 
     864              : 
     865              : /**
     866              :  * Check if a profit drain operation justified the @a roi
     867              :  *
     868              :  * @param roi reserve out operation to check
     869              :  * @return #GNUNET_YES if @a roi was justified by a profit drain,
     870              :  *         #GNUNET_NO of @a roi was not justified by a proft drain
     871              :  *         #GNUNET_SYSERR on database trouble
     872              :  */
     873              : static enum GNUNET_GenericReturnValue
     874            0 : check_profit_drain (struct WireTransferOutInfo *roi)
     875              : {
     876              :   enum GNUNET_DB_QueryStatus qs;
     877              :   uint64_t serial;
     878              :   char *account_section;
     879              :   struct TALER_FullPayto payto_uri;
     880              :   struct GNUNET_TIME_Timestamp request_timestamp;
     881              :   struct TALER_Amount amount;
     882              :   struct TALER_MasterSignatureP master_sig;
     883              : 
     884            0 :   qs = TALER_EXCHANGEDB_get_profit_drain (
     885              :     TALER_ARL_edb,
     886            0 :     &roi->details.wtid,
     887              :     &serial,
     888              :     &account_section,
     889              :     &payto_uri,
     890              :     &request_timestamp,
     891              :     &amount,
     892              :     &master_sig);
     893            0 :   switch (qs)
     894              :   {
     895            0 :   case GNUNET_DB_STATUS_HARD_ERROR:
     896            0 :     GNUNET_break (0);
     897            0 :     global_ret = EXIT_FAILURE;
     898            0 :     GNUNET_SCHEDULER_shutdown ();
     899            0 :     return GNUNET_SYSERR;
     900            0 :   case GNUNET_DB_STATUS_SOFT_ERROR:
     901              :     /* should fail on commit later ... */
     902            0 :     GNUNET_break (0);
     903            0 :     return GNUNET_SYSERR;
     904            0 :   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
     905              :     /* not a profit drain */
     906            0 :     return GNUNET_NO;
     907            0 :   case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
     908            0 :     break;
     909              :   }
     910            0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     911              :               "Profit drain of %s to %s found!\n",
     912              :               TALER_amount2s (&amount),
     913              :               payto_uri.full_payto);
     914            0 :   if (GNUNET_OK !=
     915            0 :       TALER_exchange_offline_profit_drain_verify (
     916            0 :         &roi->details.wtid,
     917              :         request_timestamp,
     918              :         &amount,
     919              :         account_section,
     920              :         payto_uri,
     921              :         &TALER_ARL_master_pub,
     922              :         &master_sig))
     923              :   {
     924            0 :     struct TALER_AUDITORDB_RowInconsistency ri = {
     925            0 :       .problem_row_id = roi->details.serial_id,
     926              :       .row_table = (char *) "profit_drains",
     927              :       .diagnostic = (char *) "invalid signature"
     928              :     };
     929              : 
     930            0 :     GNUNET_break (0);
     931            0 :     qs = TALER_AUDITORDB_insert_row_inconsistency (
     932              :       TALER_ARL_adb,
     933              :       &ri);
     934            0 :     GNUNET_free (payto_uri.full_payto);
     935            0 :     GNUNET_free (account_section);
     936            0 :     if (qs < 0)
     937              :     {
     938            0 :       global_qs = qs;
     939            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     940            0 :       return GNUNET_SYSERR;
     941              :     }
     942            0 :     return GNUNET_NO;
     943              :   }
     944            0 :   GNUNET_free (account_section);
     945              : 
     946              :   {
     947            0 :     if (0 !=
     948            0 :         TALER_full_payto_normalize_and_cmp (payto_uri,
     949              :                                             roi->details.credit_account_uri))
     950              :     {
     951            0 :       struct TALER_AUDITORDB_WireOutInconsistency woi = {
     952              :         .wire_out_row_id = serial,
     953              :         .destination_account = roi->details.credit_account_uri,
     954              :         .diagnostic = (char *) "profit drain wired to invalid account",
     955              :         .expected = roi->details.amount,
     956              :         .claimed = zero,
     957              :       };
     958              : 
     959            0 :       qs = TALER_AUDITORDB_insert_wire_out_inconsistency (
     960              :         TALER_ARL_adb,
     961              :         &woi);
     962            0 :       if (qs < 0)
     963              :       {
     964            0 :         global_qs = qs;
     965            0 :         GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     966            0 :         GNUNET_free (payto_uri.full_payto);
     967            0 :         return GNUNET_SYSERR;
     968              :       }
     969            0 :       TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_out_plus),
     970              :                             &TALER_ARL_USE_AB (total_bad_amount_out_plus),
     971              :                             &amount);
     972            0 :       GNUNET_free (payto_uri.full_payto);
     973            0 :       return GNUNET_YES; /* justified, kind-of */
     974              :     }
     975              :   }
     976            0 :   GNUNET_free (payto_uri.full_payto);
     977            0 :   if (0 !=
     978            0 :       TALER_amount_cmp (&amount,
     979            0 :                         &roi->details.amount))
     980              :   {
     981            0 :     struct TALER_AUDITORDB_WireOutInconsistency woi = {
     982            0 :       .wire_out_row_id = roi->details.serial_id,
     983              :       .destination_account = roi->details.credit_account_uri,
     984              :       .diagnostic = (char *) "incorrect amount drained to correct account",
     985              :       .expected = roi->details.amount,
     986              :       .claimed = amount,
     987              :     };
     988              : 
     989            0 :     qs = TALER_AUDITORDB_insert_wire_out_inconsistency (
     990              :       TALER_ARL_adb,
     991              :       &woi);
     992            0 :     if (qs < 0)
     993              :     {
     994            0 :       global_qs = qs;
     995            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     996            0 :       return GNUNET_SYSERR;
     997              :     }
     998            0 :     TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_out_minus),
     999              :                           &TALER_ARL_USE_AB (total_bad_amount_out_minus),
    1000              :                           &roi->details.amount);
    1001            0 :     TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_out_plus),
    1002              :                           &TALER_ARL_USE_AB (total_bad_amount_out_plus),
    1003              :                           &amount);
    1004            0 :     return GNUNET_YES; /* justified, kind-of */
    1005              :   }
    1006              :   /* profit drain was correct */
    1007            0 :   TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_drained),
    1008              :                         &TALER_ARL_USE_AB (total_drained),
    1009              :                         &amount);
    1010            0 :   return GNUNET_YES;
    1011              : }
    1012              : 
    1013              : 
    1014              : /**
    1015              :  * Check whether the given transfer was justified because it was
    1016              :  * actually previously reported as missing.
    1017              :  *
    1018              :  * @param roi the reserve out operation to check
    1019              :  * @return #GNUNET_OK on success, #GNUNET_NO if there was no
    1020              :  *   matching lag, #GNUNET_SYSERR on database trouble
    1021              :  */
    1022              : static enum GNUNET_GenericReturnValue
    1023            0 : check_closure_lag (const struct WireTransferOutInfo *roi)
    1024              : {
    1025              :   enum GNUNET_DB_QueryStatus qs;
    1026              : 
    1027            0 :   qs = TALER_AUDITORDB_delete_auditor_closure_lag (
    1028              :     TALER_ARL_adb,
    1029              :     &roi->details.amount,
    1030              :     &roi->details.wtid,
    1031              :     roi->details.credit_account_uri);
    1032            0 :   if (qs < 0)
    1033              :   {
    1034            0 :     global_qs = qs;
    1035            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1036            0 :     return GNUNET_SYSERR;
    1037              :   }
    1038            0 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    1039            0 :     return GNUNET_NO;
    1040            0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1041              :               "Cleared closure lag: found justification\n");
    1042            0 :   TALER_ARL_amount_subtract (&TALER_ARL_USE_AB (total_closure_amount_lag),
    1043              :                              &TALER_ARL_USE_AB (total_closure_amount_lag),
    1044              :                              &roi->details.amount);
    1045            0 :   return GNUNET_YES; /* found! */
    1046              : }
    1047              : 
    1048              : 
    1049              : /**
    1050              :  * Check whether the given transfer was justified by a reserve closure or
    1051              :  * profit drain. If not, complain that we failed to match an entry from
    1052              :  * #out_map.  This means a wire transfer was made without proper
    1053              :  * justification.
    1054              :  *
    1055              :  * @param cls a `struct WireAccount`
    1056              :  * @param key unused key
    1057              :  * @param value the `struct WireTransferOutInfo` to report
    1058              :  * @return #GNUNET_OK on success
    1059              :  */
    1060              : static enum GNUNET_GenericReturnValue
    1061            2 : complain_out_not_found (void *cls,
    1062              :                         const struct GNUNET_HashCode *key,
    1063              :                         void *value)
    1064              : {
    1065              :   // struct WireAccount *wa = cls;
    1066            2 :   struct WireTransferOutInfo *roi = value;
    1067              :   struct GNUNET_HashCode rkey;
    1068            2 :   struct CheckMatchContext cmx = {
    1069              :     .roi = roi,
    1070              :     .found = false
    1071              :   };
    1072              :   enum GNUNET_GenericReturnValue ret;
    1073              : 
    1074              :   (void) cls;
    1075              :   (void) key;
    1076            2 :   hash_rc (roi->details.credit_account_uri,
    1077            2 :            &roi->details.wtid,
    1078              :            &rkey);
    1079            2 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1080              :               "Checking for reserve closure %s benefiting %s\n",
    1081              :               GNUNET_h2s (&rkey),
    1082              :               roi->details.credit_account_uri.full_payto);
    1083            2 :   GNUNET_CONTAINER_multihashmap_get_multiple (reserve_closures,
    1084              :                                               &rkey,
    1085              :                                               &check_rc_matches,
    1086              :                                               &cmx);
    1087            2 :   if (cmx.found)
    1088            2 :     return GNUNET_OK;
    1089            0 :   ret = check_reported_inconsistency (roi);
    1090            0 :   if (GNUNET_NO != ret)
    1091            0 :     return ret;
    1092            0 :   ret = check_profit_drain (roi);
    1093            0 :   if (GNUNET_NO != ret)
    1094            0 :     return ret;
    1095            0 :   ret = check_closure_lag (roi);
    1096            0 :   if (GNUNET_NO != ret)
    1097            0 :     return ret;
    1098              : 
    1099              :   {
    1100            0 :     struct TALER_AUDITORDB_WireOutInconsistency woi = {
    1101              :       .destination_account = roi->details.credit_account_uri,
    1102              :       .diagnostic = (char *) "missing justification for outgoing wire transfer",
    1103            0 :       .wire_out_row_id = roi->details.serial_id,
    1104              :       .expected = zero,
    1105              :       .claimed = roi->details.amount
    1106              :     };
    1107              :     enum GNUNET_DB_QueryStatus qs;
    1108              : 
    1109            0 :     qs = TALER_AUDITORDB_insert_wire_out_inconsistency (
    1110              :       TALER_ARL_adb,
    1111              :       &woi);
    1112            0 :     if (qs < 0)
    1113              :     {
    1114            0 :       global_qs = qs;
    1115            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1116            0 :       return GNUNET_SYSERR;
    1117              :     }
    1118              :   }
    1119            0 :   TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_out_plus),
    1120              :                         &TALER_ARL_USE_AB (total_bad_amount_out_plus),
    1121              :                         &roi->details.amount);
    1122            0 :   return GNUNET_OK;
    1123              : }
    1124              : 
    1125              : 
    1126              : /**
    1127              :  * Function called with details about outgoing wire transfers
    1128              :  * as claimed by the exchange DB.
    1129              :  *
    1130              :  * @param wa a `struct WireAccount`
    1131              :  * @param rowid unique serial ID in wire_out table
    1132              :  * @param date timestamp of the transfer (roughly)
    1133              :  * @param wtid wire transfer subject
    1134              :  * @param payto_uri wire transfer details of the receiver
    1135              :  * @param amount amount that was wired
    1136              :  * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
    1137              :  */
    1138              : static enum GNUNET_GenericReturnValue
    1139           28 : wire_out_cb (
    1140              :   struct WireAccount *wa,
    1141              :   uint64_t rowid,
    1142              :   struct GNUNET_TIME_Timestamp date,
    1143              :   const struct TALER_WireTransferIdentifierRawP *wtid,
    1144              :   const struct TALER_FullPayto payto_uri,
    1145              :   const struct TALER_Amount *amount)
    1146              : {
    1147              :   struct GNUNET_HashCode key;
    1148              :   struct WireTransferOutInfo *roi;
    1149              : 
    1150           28 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1151              :               "Exchange wire OUT #%llu at %s of %s with WTID %s\n",
    1152              :               (unsigned long long) rowid,
    1153              :               GNUNET_TIME_timestamp2s (date),
    1154              :               TALER_amount2s (amount),
    1155              :               TALER_B2S (wtid));
    1156           28 :   wa->last_wire_out_serial_id = rowid + 1;
    1157           28 :   TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_wire_out),
    1158              :                         &TALER_ARL_USE_AB (total_wire_out),
    1159              :                         amount);
    1160           28 :   GNUNET_CRYPTO_hash (wtid,
    1161              :                       sizeof (*wtid),
    1162              :                       &key);
    1163           28 :   roi = GNUNET_CONTAINER_multihashmap_get (out_map,
    1164              :                                            &key);
    1165           28 :   if (NULL == roi)
    1166              :   {
    1167              :     /* Wire transfer was not made (yet) at all (but would have been
    1168              :        justified), so the entire amount is missing / still to be done.  This
    1169              :        is moderately harmless, it might just be that the
    1170              :        taler-exchange-transfer tool or bank has not yet fully caught up with
    1171              :        the transfers it should do.
    1172              :        May be cleared later by check_reported_inconsistency() */
    1173              :     char diag[MAX_DIAG_LEN];
    1174            0 :     struct TALER_AUDITORDB_WireOutInconsistency woi = {
    1175              :       .destination_account = payto_uri,
    1176              :       .diagnostic = diag,
    1177              :       .wire_out_row_id = rowid,
    1178              :       .expected = *amount,
    1179              :       .claimed = zero,
    1180              :     };
    1181              :     enum GNUNET_DB_QueryStatus qs;
    1182              : 
    1183            0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1184              :                 "Wire out for row %llu still missing\n",
    1185              :                 (unsigned long long) rowid);
    1186            0 :     make_missing_diag (diag,
    1187              :                        wtid);
    1188            0 :     qs = TALER_AUDITORDB_insert_wire_out_inconsistency (
    1189              :       TALER_ARL_adb,
    1190              :       &woi);
    1191            0 :     if (qs < 0)
    1192              :     {
    1193            0 :       global_qs = qs;
    1194            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1195            0 :       return GNUNET_SYSERR;
    1196              :     }
    1197            0 :     TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_out_minus),
    1198              :                           &TALER_ARL_USE_AB (total_bad_amount_out_minus),
    1199              :                           amount);
    1200            0 :     return GNUNET_OK;
    1201              :   }
    1202              : 
    1203           28 :   if (0 != TALER_full_payto_normalize_and_cmp (payto_uri,
    1204              :                                                roi->details.credit_account_uri))
    1205              :   {
    1206              :     /* Destination bank account is wrong in actual wire transfer, so
    1207              :        we should count the wire transfer as entirely spurious, and
    1208              :        additionally consider the justified wire transfer as missing. */
    1209            0 :     struct TALER_AUDITORDB_WireOutInconsistency woi = {
    1210              :       .wire_out_row_id = rowid,
    1211              :       .destination_account = payto_uri,
    1212              :       .diagnostic = (char *) "receiver account mismatch",
    1213              :       .expected = *amount,
    1214              :       .claimed = roi->details.amount,
    1215              :     };
    1216              :     enum GNUNET_DB_QueryStatus qs;
    1217              : 
    1218            0 :     qs = TALER_AUDITORDB_insert_wire_out_inconsistency (
    1219              :       TALER_ARL_adb,
    1220              :       &woi);
    1221            0 :     if (qs < 0)
    1222              :     {
    1223            0 :       global_qs = qs;
    1224            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1225            0 :       return GNUNET_SYSERR;
    1226              :     }
    1227            0 :     TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_out_plus),
    1228              :                           &TALER_ARL_USE_AB (total_bad_amount_out_plus),
    1229              :                           &roi->details.amount);
    1230            0 :     TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_out_minus),
    1231              :                           &TALER_ARL_USE_AB (total_bad_amount_out_minus),
    1232              :                           amount);
    1233            0 :     GNUNET_assert (GNUNET_OK ==
    1234              :                    free_roi (NULL,
    1235              :                              &key,
    1236              :                              roi));
    1237            0 :     return GNUNET_OK;
    1238              :   }
    1239              : 
    1240           28 :   if (0 != TALER_amount_cmp (&roi->details.amount,
    1241              :                              amount))
    1242              :   {
    1243            0 :     struct TALER_AUDITORDB_WireOutInconsistency woi = {
    1244              :       .destination_account = payto_uri,
    1245              :       .diagnostic = (char *) "wire amount does not match",
    1246              :       .wire_out_row_id = rowid,
    1247              :       .expected = *amount,
    1248              :       .claimed = roi->details.amount,
    1249              :     };
    1250              :     enum GNUNET_DB_QueryStatus qs;
    1251              : 
    1252            0 :     qs = TALER_AUDITORDB_insert_wire_out_inconsistency (
    1253              :       TALER_ARL_adb,
    1254              :       &woi);
    1255            0 :     if (qs < 0)
    1256              :     {
    1257            0 :       global_qs = qs;
    1258            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1259            0 :       return GNUNET_SYSERR;
    1260              :     }
    1261            0 :     if (0 < TALER_amount_cmp (amount,
    1262            0 :                               &roi->details.amount))
    1263              :     {
    1264              :       /* amount > roi->details.amount: wire transfer was smaller than it should have been */
    1265              :       struct TALER_Amount delta;
    1266              : 
    1267            0 :       TALER_ARL_amount_subtract (&delta,
    1268              :                                  amount,
    1269              :                                  &roi->details.amount);
    1270            0 :       TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_out_minus),
    1271              :                             &TALER_ARL_USE_AB (total_bad_amount_out_minus),
    1272              :                             &delta);
    1273              :     }
    1274              :     else
    1275              :     {
    1276              :       /* roi->details.amount < amount: wire transfer was larger than it should have been */
    1277              :       struct TALER_Amount delta;
    1278              : 
    1279            0 :       TALER_ARL_amount_subtract (&delta,
    1280              :                                  &roi->details.amount,
    1281              :                                  amount);
    1282            0 :       TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_bad_amount_out_plus),
    1283              :                             &TALER_ARL_USE_AB (total_bad_amount_out_plus),
    1284              :                             &delta);
    1285              :     }
    1286            0 :     GNUNET_assert (GNUNET_OK ==
    1287              :                    free_roi (NULL,
    1288              :                              &key,
    1289              :                              roi));
    1290            0 :     return GNUNET_OK;
    1291              :   }
    1292              : 
    1293              :   {
    1294              :     enum GNUNET_GenericReturnValue ret;
    1295              : 
    1296           28 :     if (! check_time_difference ("wire_out",
    1297              :                                  rowid,
    1298              :                                  date,
    1299              :                                  roi->details.execution_date))
    1300              :     {
    1301              :       /* We had a database error, fail */
    1302            0 :       ret = GNUNET_SYSERR;
    1303              :     }
    1304              :     else
    1305              :     {
    1306           28 :       ret = GNUNET_OK;
    1307              :     }
    1308           28 :     GNUNET_assert (GNUNET_OK ==
    1309              :                    free_roi (NULL,
    1310              :                              &key,
    1311              :                              roi));
    1312           28 :     return ret;
    1313              :   }
    1314              : }
    1315              : 
    1316              : 
    1317              : /**
    1318              :  * Main function for processing 'debit' data.  We start by going over
    1319              :  * the DEBIT transactions this time, and then verify that all of them are
    1320              :  * justified by reserve closures, profit drains or regular outgoing
    1321              :  * wire transfers from aggregated deposits.
    1322              :  *
    1323              :  * @param[in,out] wa wire account list to process
    1324              :  */
    1325              : static void
    1326              : process_debits (struct WireAccount *wa);
    1327              : 
    1328              : 
    1329              : /**
    1330              :  * Go over the "wire_out" table of the exchange and
    1331              :  * verify that all wire outs are in that table.
    1332              :  *
    1333              :  * @param[in,out] wa wire account we are processing
    1334              :  */
    1335              : static void
    1336            6 : check_exchange_wire_out (struct WireAccount *wa)
    1337              : {
    1338              :   enum GNUNET_DB_QueryStatus qs;
    1339              : 
    1340            6 :   GNUNET_assert (NULL == wa->dhh);
    1341            6 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1342              :               "Analyzing exchange's wire OUT table for account `%s'\n",
    1343              :               wa->ai->section_name);
    1344            6 :   qs = TALER_EXCHANGEDB_iterate_wire_outs_above_serial_id_by_account (
    1345              :     TALER_ARL_edb,
    1346            6 :     wa->ai->section_name,
    1347              :     wa->last_wire_out_serial_id,
    1348              :     &wire_out_cb,
    1349              :     wa);
    1350            6 :   if (0 > qs)
    1351              :   {
    1352            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1353            0 :     global_ret = EXIT_FAILURE;
    1354            0 :     GNUNET_SCHEDULER_shutdown ();
    1355            0 :     return;
    1356              :   }
    1357            6 :   GNUNET_CONTAINER_multihashmap_iterate (out_map,
    1358              :                                          &complain_out_not_found,
    1359              :                                          wa);
    1360              :   /* clean up */
    1361            6 :   GNUNET_CONTAINER_multihashmap_iterate (out_map,
    1362              :                                          &free_roi,
    1363              :                                          NULL);
    1364            6 :   process_debits (wa->next);
    1365              : }
    1366              : 
    1367              : 
    1368              : /**
    1369              :  * This function is called for all transactions that
    1370              :  * are debited from the exchange's account (outgoing
    1371              :  * transactions).
    1372              :  *
    1373              :  * @param cls `struct WireAccount` with current wire account to process
    1374              :  * @param dhr HTTP response details
    1375              :  */
    1376              : static void
    1377              : history_debit_cb (
    1378              :   void *cls,
    1379              :   const struct TALER_BANK_DebitHistoryResponse *dhr);
    1380              : 
    1381              : 
    1382              : /**
    1383              :  * Task scheduled to begin long-polling on the
    1384              :  * bank transfer.
    1385              :  *
    1386              :  * @param cls a `struct WireAccount *`
    1387              :  */
    1388              : static void
    1389            0 : dh_long_poll (void *cls)
    1390              : {
    1391            0 :   struct WireAccount *wa = cls;
    1392              : 
    1393            0 :   wa->dhh_task = NULL;
    1394              :   wa->dhh_next
    1395            0 :     = GNUNET_TIME_relative_to_absolute (MIN_LONGPOLL_DELAY);
    1396            0 :   GNUNET_assert (NULL == wa->dhh);
    1397            0 :   wa->dhh = TALER_BANK_debit_history (
    1398              :     ctx,
    1399            0 :     wa->ai->auth,
    1400              :     wa->wire_off_out,
    1401              :     MAX_PER_TRANSACTION,
    1402              :     MAX_LONGPOLL_DELAY,
    1403              :     &history_debit_cb,
    1404              :     wa);
    1405            0 :   if (NULL == wa->dhh)
    1406              :   {
    1407            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1408              :                 "Failed to start long-polling for bank transaction history for `%s'\n",
    1409              :                 wa->ai->section_name);
    1410            0 :     global_ret = EXIT_FAILURE;
    1411            0 :     GNUNET_SCHEDULER_shutdown ();
    1412            0 :     return;
    1413              :   }
    1414              : }
    1415              : 
    1416              : 
    1417              : static void
    1418            6 : history_debit_cb (
    1419              :   void *cls,
    1420              :   const struct TALER_BANK_DebitHistoryResponse *dhr)
    1421              : {
    1422            6 :   struct WireAccount *wa = cls;
    1423              :   struct WireTransferOutInfo *roi;
    1424              :   size_t slen;
    1425              : 
    1426            6 :   wa->dhh = NULL;
    1427            6 :   if ( (MHD_HTTP_OK == dhr->http_status) &&
    1428            2 :        (0 != dhr->details.ok.details_length) )
    1429              :   {
    1430              :     /* As we got results, we go again *immediately* */
    1431            2 :     wa->dhh_next = GNUNET_TIME_UNIT_ZERO_ABS;
    1432              :   }
    1433            6 :   GNUNET_assert (NULL == wa->dhh_task);
    1434              :   wa->dhh_task
    1435            6 :     = GNUNET_SCHEDULER_add_at (wa->dhh_next,
    1436              :                                &dh_long_poll,
    1437              :                                wa);
    1438            6 :   switch (dhr->http_status)
    1439              :   {
    1440            2 :   case MHD_HTTP_OK:
    1441           32 :     for (unsigned int i = 0; i < dhr->details.ok.details_length; i++)
    1442              :     {
    1443           30 :       const struct TALER_BANK_DebitDetails *dd
    1444           30 :         = &dhr->details.ok.details[i];
    1445              : 
    1446           30 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1447              :                   "Analyzing bank DEBIT #%llu at %s of %s with WTID %s\n",
    1448              :                   (unsigned long long) dd->serial_id,
    1449              :                   GNUNET_TIME_timestamp2s (dd->execution_date),
    1450              :                   TALER_amount2s (&dd->amount),
    1451              :                   TALER_B2S (&dd->wtid));
    1452           30 :       wa->wire_off_out = dd->serial_id;
    1453              :       /* dd->amount is what was transferred, and what the exchange's
    1454              :          justification (wire_out, reserve closure, profit drain) must
    1455              :          match; the bank's fee was debited on top of it. */
    1456           30 :       TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_wire_debit_fees),
    1457              :                             &TALER_ARL_USE_AB (total_wire_debit_fees),
    1458              :                             &dd->debit_fee);
    1459           30 :       slen = strlen (dd->credit_account_uri.full_payto) + 1;
    1460           30 :       roi = GNUNET_malloc (sizeof (struct WireTransferOutInfo)
    1461              :                            + slen);
    1462           30 :       GNUNET_CRYPTO_hash (&dd->wtid,
    1463              :                           sizeof (dd->wtid),
    1464              :                           &roi->subject_hash);
    1465           30 :       roi->details = *dd;
    1466              :       roi->details.credit_account_uri.full_payto
    1467           30 :         = (char *) &roi[1];
    1468           30 :       GNUNET_memcpy (&roi[1],
    1469              :                      dd->credit_account_uri.full_payto,
    1470              :                      slen);
    1471           30 :       if (GNUNET_OK !=
    1472           30 :           GNUNET_CONTAINER_multihashmap_put (out_map,
    1473           30 :                                              &roi->subject_hash,
    1474              :                                              roi,
    1475              :                                              GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
    1476              :       {
    1477            0 :         struct TALER_AUDITORDB_WireFormatInconsistency wfi = {
    1478              :           .amount = dd->amount,
    1479            0 :           .wire_offset = dd->serial_id,
    1480              :           .diagnostic = (char *) "duplicate outgoing wire transfer subject"
    1481              :         };
    1482              :         enum GNUNET_DB_QueryStatus qs;
    1483              : 
    1484            0 :         qs = TALER_AUDITORDB_insert_wire_format_inconsistency (
    1485              :           TALER_ARL_adb,
    1486              :           &wfi);
    1487            0 :         if (qs < 0)
    1488              :         {
    1489            0 :           global_qs = qs;
    1490            0 :           GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1491            0 :           commit (qs);
    1492            0 :           return;
    1493              :         }
    1494            0 :         TALER_ARL_amount_add (&TALER_ARL_USE_AB (
    1495              :                                 wire_debit_duplicate_transfer_subject_total),
    1496              :                               &TALER_ARL_USE_AB (
    1497              :                                 wire_debit_duplicate_transfer_subject_total),
    1498              :                               &dd->amount);
    1499              :       }
    1500              :     }
    1501            2 :     check_exchange_wire_out (wa);
    1502            2 :     return;
    1503            2 :   case MHD_HTTP_NO_CONTENT:
    1504            2 :     check_exchange_wire_out (wa);
    1505            2 :     return;
    1506            2 :   case MHD_HTTP_NOT_FOUND:
    1507            2 :     if (ignore_account_404)
    1508              :     {
    1509            2 :       check_exchange_wire_out (wa);
    1510            2 :       return;
    1511              :     }
    1512            0 :     break;
    1513            0 :   default:
    1514            0 :     break;
    1515              :   }
    1516            0 :   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1517              :               "Error fetching debit history of account %s: %u/%u!\n",
    1518              :               wa->ai->section_name,
    1519              :               dhr->http_status,
    1520              :               (unsigned int) dhr->ec);
    1521            0 :   commit (GNUNET_DB_STATUS_HARD_ERROR);
    1522            0 :   global_ret = EXIT_FAILURE;
    1523            0 :   GNUNET_SCHEDULER_shutdown ();
    1524            0 :   return;
    1525              : }
    1526              : 
    1527              : 
    1528              : static void
    1529           12 : process_debits (struct WireAccount *wa)
    1530              : {
    1531              :   /* skip accounts where DEBIT is not enabled */
    1532           12 :   while ( (NULL != wa) &&
    1533            6 :           (! wa->ai->debit_enabled) )
    1534            0 :     wa = wa->next;
    1535           12 :   if (NULL == wa)
    1536              :   {
    1537              :     /* end of iteration */
    1538            6 :     commit (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT);
    1539            6 :     return;
    1540              :   }
    1541            6 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1542              :               "Checking bank DEBIT records of account `%s'\n",
    1543              :               wa->ai->section_name);
    1544            6 :   if ( (NULL == wa->dhh) &&
    1545            6 :        (NULL == wa->dhh_task) )
    1546              :   {
    1547           12 :     wa->dhh = TALER_BANK_debit_history (
    1548              :       ctx,
    1549            6 :       wa->ai->auth,
    1550              :       wa->wire_off_out,
    1551              :       MAX_PER_TRANSACTION,
    1552            6 :       GNUNET_TIME_UNIT_ZERO,
    1553              :       &history_debit_cb,
    1554              :       wa);
    1555            6 :     if (NULL == wa->dhh)
    1556              :     {
    1557            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1558              :                   "Failed to obtain bank transaction history for `%s'\n",
    1559              :                   wa->ai->section_name);
    1560            0 :       commit (GNUNET_DB_STATUS_HARD_ERROR);
    1561            0 :       global_ret = EXIT_FAILURE;
    1562            0 :       GNUNET_SCHEDULER_shutdown ();
    1563            0 :       return;
    1564              :     }
    1565              :   }
    1566              : }
    1567              : 
    1568              : 
    1569              : /**
    1570              :  * Function called about reserve closing operations the aggregator triggered.
    1571              :  *
    1572              :  * @param cls closure; NULL
    1573              :  * @param rowid row identifier used to uniquely identify the reserve closing operation
    1574              :  * @param execution_date when did we execute the close operation
    1575              :  * @param amount_with_fee how much did we debit the reserve
    1576              :  * @param closing_fee how much did we charge for closing the reserve
    1577              :  * @param reserve_pub public key of the reserve
    1578              :  * @param receiver_account where did we send the funds, in payto://-format
    1579              :  * @param wtid identifier used for the wire transfer
    1580              :  * @param close_request_row which close request triggered the operation?
    1581              :  *         0 if it was a timeout (not used)
    1582              :  * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
    1583              :  */
    1584              : static enum GNUNET_GenericReturnValue
    1585            2 : reserve_closed_cb (
    1586              :   void *cls,
    1587              :   uint64_t rowid,
    1588              :   struct GNUNET_TIME_Timestamp execution_date,
    1589              :   const struct TALER_Amount *amount_with_fee,
    1590              :   const struct TALER_Amount *closing_fee,
    1591              :   const struct TALER_ReservePublicKeyP *reserve_pub,
    1592              :   const struct TALER_FullPayto receiver_account,
    1593              :   const struct TALER_WireTransferIdentifierRawP *wtid,
    1594              :   uint64_t close_request_row)
    1595              : {
    1596              :   struct ReserveClosure *rc;
    1597              :   struct GNUNET_HashCode key;
    1598              : 
    1599              :   (void) cls;
    1600              :   (void) close_request_row;
    1601            2 :   GNUNET_assert (TALER_ARL_USE_PP (wire_reserve_close_id) <= rowid);
    1602            2 :   TALER_ARL_USE_PP (wire_reserve_close_id) = rowid + 1;
    1603            2 :   rc = GNUNET_new (struct ReserveClosure);
    1604            2 :   if (TALER_ARL_SR_INVALID_NEGATIVE ==
    1605            2 :       TALER_ARL_amount_subtract_neg (&rc->amount,
    1606              :                                      amount_with_fee,
    1607              :                                      closing_fee))
    1608              :   {
    1609            0 :     struct TALER_AUDITORDB_RowInconsistency ri = {
    1610              :       .problem_row_id = rowid,
    1611              :       .row_table
    1612              :         = (char *) "reserves_closures",
    1613              :       .diagnostic
    1614              :         = (char *) "closing fee above reserve balance (and closed anyway)"
    1615              :     };
    1616              :     enum GNUNET_DB_QueryStatus qs;
    1617              : 
    1618            0 :     qs = TALER_AUDITORDB_insert_row_inconsistency (
    1619              :       TALER_ARL_adb,
    1620              :       &ri);
    1621            0 :     if (qs < 0)
    1622              :     {
    1623            0 :       global_qs = qs;
    1624            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1625            0 :       return GNUNET_OK;
    1626              :     }
    1627            0 :     GNUNET_free (rc);
    1628            0 :     return GNUNET_OK;
    1629              :   }
    1630              :   rc->receiver_account.full_payto
    1631            2 :     = GNUNET_strdup (receiver_account.full_payto);
    1632            2 :   rc->wtid = *wtid;
    1633            2 :   rc->execution_date = execution_date;
    1634            2 :   rc->rowid = rowid;
    1635            2 :   hash_rc (rc->receiver_account,
    1636              :            wtid,
    1637              :            &key);
    1638            2 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1639              :               "Discovered reserve closure %llu (%s) over %s benefiting %s\n",
    1640              :               (unsigned long long) rowid,
    1641              :               GNUNET_h2s (&key),
    1642              :               TALER_amount2s (amount_with_fee),
    1643              :               receiver_account.full_payto);
    1644            2 :   (void) GNUNET_CONTAINER_multihashmap_put (
    1645              :     reserve_closures,
    1646              :     &key,
    1647              :     rc,
    1648              :     GNUNET_CONTAINER_MULTIHASHMAPOPTION_MULTIPLE);
    1649            2 :   return GNUNET_OK;
    1650              : }
    1651              : 
    1652              : 
    1653              : /**
    1654              :  * Start the database transactions and begin the audit.
    1655              :  *
    1656              :  * @return transaction status code
    1657              :  */
    1658              : static enum GNUNET_DB_QueryStatus
    1659            6 : begin_transaction (void)
    1660              : {
    1661              :   enum GNUNET_DB_QueryStatus qs;
    1662              : 
    1663            6 :   if (GNUNET_SYSERR ==
    1664            6 :       TALER_EXCHANGEDB_preflight (TALER_ARL_edb))
    1665              :   {
    1666            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1667              :                 "Failed to initialize exchange database connection.\n");
    1668            0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    1669              :   }
    1670            6 :   if (GNUNET_SYSERR ==
    1671            6 :       TALER_AUDITORDB_preflight (TALER_ARL_adb))
    1672              :   {
    1673            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1674              :                 "Failed to initialize auditor database session.\n");
    1675            0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    1676              :   }
    1677            6 :   global_qs = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
    1678            6 :   if (GNUNET_OK !=
    1679            6 :       TALER_AUDITORDB_start (TALER_ARL_adb,
    1680              :                              "auditor-wire-debit"))
    1681              :   {
    1682            0 :     GNUNET_break (0);
    1683            0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    1684              :   }
    1685            6 :   if (GNUNET_OK !=
    1686            6 :       TALER_EXCHANGEDB_start_read_only (TALER_ARL_edb,
    1687              :                                         "wire debit auditor"))
    1688              :   {
    1689            0 :     GNUNET_break (0);
    1690            0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    1691              :   }
    1692            6 :   qs = TALER_AUDITORDB_get_balance (
    1693              :     TALER_ARL_adb,
    1694              :     TALER_ARL_GET_AB (total_drained),
    1695              :     TALER_ARL_GET_AB (total_wire_out),
    1696              :     TALER_ARL_GET_AB (total_bad_amount_out_plus),
    1697              :     TALER_ARL_GET_AB (total_bad_amount_out_minus),
    1698              :     TALER_ARL_GET_AB (total_closure_amount_lag),
    1699              :     TALER_ARL_GET_AB (wire_debit_duplicate_transfer_subject_total),
    1700              :     TALER_ARL_GET_AB (total_wire_out),
    1701              :     TALER_ARL_GET_AB (total_wire_debit_fees),
    1702              :     NULL);
    1703            6 :   switch (qs)
    1704              :   {
    1705            0 :   case GNUNET_DB_STATUS_HARD_ERROR:
    1706            0 :     GNUNET_break (0);
    1707            0 :     return qs;
    1708            0 :   case GNUNET_DB_STATUS_SOFT_ERROR:
    1709            0 :     GNUNET_break (0);
    1710            0 :     return qs;
    1711            0 :   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    1712              :   case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    1713            0 :     break;
    1714              :   }
    1715            6 :   for (struct WireAccount *wa = wa_head;
    1716           12 :        NULL != wa;
    1717            6 :        wa = wa->next)
    1718              :   {
    1719              :     /* we are called again after a serialization failure, so the labels
    1720              :        may already have been computed */
    1721            6 :     GNUNET_free (wa->label_wire_out_serial_id);
    1722            6 :     GNUNET_free (wa->label_wire_off_out);
    1723            6 :     GNUNET_asprintf (&wa->label_wire_out_serial_id,
    1724              :                      "wire-%s-%s",
    1725            6 :                      wa->ai->section_name,
    1726              :                      "wire_out_serial_id");
    1727            6 :     GNUNET_asprintf (&wa->label_wire_off_out,
    1728              :                      "wire-%s-%s",
    1729            6 :                      wa->ai->section_name,
    1730              :                      "wire_off_out");
    1731            6 :     qs = TALER_AUDITORDB_get_auditor_progress (
    1732              :       TALER_ARL_adb,
    1733            6 :       wa->label_wire_out_serial_id,
    1734              :       &wa->last_wire_out_serial_id,
    1735              :       wa->label_wire_off_out,
    1736              :       &wa->wire_off_out,
    1737              :       NULL);
    1738            6 :     if (0 > qs)
    1739              :     {
    1740            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1741            0 :       return qs;
    1742              :     }
    1743            6 :     GNUNET_assert (2 == qs);
    1744            6 :     wa->start_wire_out_serial_id = wa->last_wire_out_serial_id;
    1745            6 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1746              :                 "Resuming account %s debit audit at %llu/%llu\n",
    1747              :                 wa->ai->section_name,
    1748              :                 (unsigned long long) wa->last_wire_out_serial_id,
    1749              :                 (unsigned long long) wa->wire_off_out);
    1750              :   }
    1751            6 :   qs = TALER_AUDITORDB_get_auditor_progress (
    1752              :     TALER_ARL_adb,
    1753              :     TALER_ARL_GET_PP (wire_reserve_close_id),
    1754              :     NULL);
    1755            6 :   if (0 > qs)
    1756              :   {
    1757            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1758            0 :     return qs;
    1759              :   }
    1760            6 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    1761              :   {
    1762            0 :     GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
    1763              :                 "First analysis of with wire auditor, starting audit from scratch\n");
    1764              :   }
    1765              :   else
    1766              :   {
    1767            6 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1768              :                 "Resuming wire debit audit at %llu\n",
    1769              :                 (unsigned long long) TALER_ARL_USE_PP (wire_reserve_close_id));
    1770              :   }
    1771            6 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1772              :               "Iterating over reserve closures from %llu\n",
    1773              :               (unsigned long long) TALER_ARL_USE_PP (wire_reserve_close_id));
    1774            6 :   qs = TALER_EXCHANGEDB_iterate_reserve_closed_above_serial_id (
    1775              :     TALER_ARL_edb,
    1776              :     TALER_ARL_USE_PP (wire_reserve_close_id),
    1777              :     &reserve_closed_cb,
    1778              :     NULL);
    1779            6 :   if (0 > qs)
    1780              :   {
    1781            0 :     GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
    1782            0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    1783              :   }
    1784            6 :   process_debits (wa_head);
    1785            6 :   return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
    1786              : }
    1787              : 
    1788              : 
    1789              : /**
    1790              :  * Function called with information about a wire account.  Adds the
    1791              :  * account to our list for processing (if it is enabled and we can
    1792              :  * load the plugin).
    1793              :  *
    1794              :  * @param cls closure, NULL
    1795              :  * @param ai account information
    1796              :  */
    1797              : static void
    1798            6 : process_account_cb (void *cls,
    1799              :                     const struct TALER_EXCHANGEDB_AccountInfo *ai)
    1800              : {
    1801              :   struct WireAccount *wa;
    1802              : 
    1803              :   (void) cls;
    1804            6 :   if ( (! ai->debit_enabled) &&
    1805            0 :        (! ai->credit_enabled) )
    1806            0 :     return; /* not an active exchange account */
    1807            6 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1808              :               "Found exchange account `%s'\n",
    1809              :               ai->section_name);
    1810            6 :   wa = GNUNET_new (struct WireAccount);
    1811            6 :   wa->ai = ai;
    1812            6 :   GNUNET_CONTAINER_DLL_insert (wa_head,
    1813              :                                wa_tail,
    1814              :                                wa);
    1815              : }
    1816              : 
    1817              : 
    1818              : /**
    1819              :  * Function called on events received from Postgres.
    1820              :  *
    1821              :  * @param cls closure, NULL
    1822              :  * @param extra additional event data provided
    1823              :  * @param extra_size number of bytes in @a extra
    1824              :  */
    1825              : static void
    1826            0 : db_notify (void *cls,
    1827              :            const void *extra,
    1828              :            size_t extra_size)
    1829              : {
    1830              :   (void) cls;
    1831              :   (void) extra;
    1832              :   (void) extra_size;
    1833              : 
    1834            0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1835              :               "Received notification to wake wire helper\n");
    1836              :   /* If there are accounts we are still processing, abort
    1837              :      the HTTP requests so we can start afresh. */
    1838            0 :   for (struct WireAccount *wa = wa_head;
    1839            0 :        NULL != wa;
    1840            0 :        wa = wa->next)
    1841              :   {
    1842            0 :     if (NULL != wa->dhh)
    1843              :     {
    1844            0 :       TALER_BANK_debit_history_cancel (wa->dhh);
    1845            0 :       wa->dhh = NULL;
    1846              :     }
    1847            0 :     check_exchange_wire_out (wa);
    1848              :   }
    1849              : 
    1850            0 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
    1851            0 :       begin_transaction ())
    1852              :   {
    1853            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1854              :                 "Audit failed\n");
    1855            0 :     GNUNET_break (0);
    1856            0 :     global_ret = EXIT_FAILURE;
    1857            0 :     GNUNET_SCHEDULER_shutdown ();
    1858            0 :     return;
    1859              :   }
    1860              : }
    1861              : 
    1862              : 
    1863              : /**
    1864              :  * Main function that will be run.
    1865              :  *
    1866              :  * @param cls closure
    1867              :  * @param args remaining command-line arguments
    1868              :  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
    1869              :  * @param c configuration
    1870              :  */
    1871              : static void
    1872            6 : run (void *cls,
    1873              :      char *const *args,
    1874              :      const char *cfgfile,
    1875              :      const struct GNUNET_CONFIGURATION_Handle *c)
    1876              : {
    1877              :   (void) cls;
    1878              :   (void) args;
    1879              :   (void) cfgfile;
    1880            6 :   cfg = c;
    1881            6 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1882              :               "Launching wire debit auditor\n");
    1883            6 :   if (EXIT_SUCCESS !=
    1884            6 :       (global_ret = TALER_ARL_init (c)))
    1885              :   {
    1886            0 :     return;
    1887              :   }
    1888              : 
    1889              :   reserve_closures
    1890            6 :     = GNUNET_CONTAINER_multihashmap_create (1024,
    1891              :                                             GNUNET_NO);
    1892            6 :   GNUNET_assert (GNUNET_OK ==
    1893              :                  TALER_amount_set_zero (TALER_ARL_currency,
    1894              :                                         &zero));
    1895            6 :   GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
    1896              :                                  NULL);
    1897            6 :   ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
    1898              :                           &rctx);
    1899            6 :   rctx = GNUNET_CURL_gnunet_rc_create (ctx);
    1900            6 :   if (NULL == ctx)
    1901              :   {
    1902            0 :     GNUNET_break (0);
    1903            0 :     global_ret = EXIT_FAILURE;
    1904            0 :     return;
    1905              :   }
    1906            6 :   reserve_closures = GNUNET_CONTAINER_multihashmap_create (1024,
    1907              :                                                            GNUNET_NO);
    1908            6 :   out_map = GNUNET_CONTAINER_multihashmap_create (1024,
    1909              :                                                   true);
    1910            6 :   if (GNUNET_OK !=
    1911            6 :       TALER_EXCHANGEDB_load_accounts (TALER_ARL_cfg,
    1912              :                                       TALER_EXCHANGEDB_ALO_DEBIT
    1913              :                                       | TALER_EXCHANGEDB_ALO_CREDIT
    1914              :                                       | TALER_EXCHANGEDB_ALO_AUTHDATA))
    1915              :   {
    1916            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1917              :                 "No bank accounts configured\n");
    1918            0 :     global_ret = EXIT_NOTCONFIGURED;
    1919            0 :     GNUNET_SCHEDULER_shutdown ();
    1920            0 :     return;
    1921              :   }
    1922            6 :   TALER_EXCHANGEDB_find_accounts (&process_account_cb,
    1923              :                                   NULL);
    1924              : 
    1925            6 :   if (0 == test_mode)
    1926              :   {
    1927            0 :     struct GNUNET_DB_EventHeaderP es = {
    1928            0 :       .size = htons (sizeof (es)),
    1929            0 :       .type = htons (TALER_DBEVENT_EXCHANGE_AUDITOR_WAKE_HELPER_WIRE)
    1930              :     };
    1931              : 
    1932            0 :     eh = TALER_AUDITORDB_event_listen (TALER_ARL_adb,
    1933              :                                        &es,
    1934            0 :                                        GNUNET_TIME_UNIT_FOREVER_REL,
    1935              :                                        &db_notify,
    1936              :                                        NULL);
    1937            0 :     GNUNET_assert (NULL != eh);
    1938              :   }
    1939            6 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
    1940            6 :       begin_transaction ())
    1941              :   {
    1942            0 :     GNUNET_break (0);
    1943            0 :     global_ret = EXIT_FAILURE;
    1944            0 :     GNUNET_SCHEDULER_shutdown ();
    1945            0 :     return;
    1946              :   }
    1947              : }
    1948              : 
    1949              : 
    1950              : /**
    1951              :  * The main function of the wire auditing tool. Checks that
    1952              :  * the exchange's records of wire transfers match that of
    1953              :  * the wire gateway.
    1954              :  *
    1955              :  * @param argc number of arguments from the command line
    1956              :  * @param argv command line arguments
    1957              :  * @return 0 ok, 1 on error
    1958              :  */
    1959              : int
    1960            6 : main (int argc,
    1961              :       char *const *argv)
    1962              : {
    1963            6 :   const struct GNUNET_GETOPT_CommandLineOption options[] = {
    1964            6 :     GNUNET_GETOPT_option_flag ('i',
    1965              :                                "internal",
    1966              :                                "perform checks only applicable for exchange-internal audits",
    1967              :                                &internal_checks),
    1968            6 :     GNUNET_GETOPT_option_flag ('I',
    1969              :                                "ignore-not-found",
    1970              :                                "continue, even if the bank account of the exchange was not found",
    1971              :                                &ignore_account_404),
    1972            6 :     GNUNET_GETOPT_option_flag ('t',
    1973              :                                "test",
    1974              :                                "run in test mode and exit when idle",
    1975              :                                &test_mode),
    1976            6 :     GNUNET_GETOPT_option_timetravel ('T',
    1977              :                                      "timetravel"),
    1978              :     GNUNET_GETOPT_OPTION_END
    1979              :   };
    1980              :   enum GNUNET_GenericReturnValue ret;
    1981              : 
    1982            6 :   ret = GNUNET_PROGRAM_run (
    1983              :     TALER_AUDITOR_project_data (),
    1984              :     argc,
    1985              :     argv,
    1986              :     "taler-helper-auditor-wire-debit",
    1987              :     gettext_noop (
    1988              :       "Audit exchange database for consistency with the bank's outgoing wire transfers"),
    1989              :     options,
    1990              :     &run,
    1991              :     NULL);
    1992            6 :   if (GNUNET_SYSERR == ret)
    1993            0 :     return EXIT_NOTCONFIGURED;
    1994            6 :   if (GNUNET_NO == ret)
    1995            0 :     return EXIT_SUCCESS;
    1996            6 :   return global_ret;
    1997              : }
    1998              : 
    1999              : 
    2000              : /* end of taler-helper-auditor-wire-debit.c */
        

Generated by: LCOV version 2.0-1