LCOV - code coverage report
Current view: top level - auditor - taler-helper-auditor-wire-debit.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 364 585 62.2 %
Date: 2025-06-05 21:03:14 Functions: 22 24 91.7 %

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

Generated by: LCOV version 1.16