LCOV - code coverage report
Current view: top level - auditor - taler-helper-auditor-transfer.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 34.1 % 370 126
Test Date: 2026-11-03 22:15:25 Functions: 73.3 % 15 11

            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-transfer.c
      18              :  * @brief audits that deposits past due date are
      19              :  *    aggregated and have a matching wire transfer
      20              :  * database.
      21              :  *
      22              :  * Three checks, each with its own cursor over the exchange's tables:
      23              :  *
      24              :  * 1) every batch deposit must eventually be aggregated (`total_amount_lag`),
      25              :  * 2) nothing may be aggregated before there is a deposit to justify it
      26              :  *    (`total_early_aggregation`),
      27              :  * 3) every aggregation must eventually turn into a wire transfer.
      28              :  *
      29              :  * The third is what keeps "the exchange paid the merchant" apart from "the
      30              :  * exchange decided what it owes and then sat on the money": an
      31              :  * `aggregation_tracking` row is not proof of payment, a `wire_out` row is.
      32              :  * What is still owed is split by the reason the exchange gave for not paying
      33              :  * -- `total_aml_hold` for an open KYC requirement, `total_small_aggregate` for
      34              :  * an aggregate that does not yet cover its wire fee, and `total_transfer_lag`
      35              :  * for a transfer the exchange never gave any reason for -- and each held
      36              :  * transfer is listed in `auditor_aml_holds`.
      37              :  *
      38              :  * @author Christian Grothoff
      39              :  */
      40              : #include "platform.h"
      41              : #include <gnunet/gnunet_util_lib.h>
      42              : #include <gnunet/gnunet_curl_lib.h>
      43              : #include "auditordb_lib.h"
      44              : #include "exchangedb_lib.h"
      45              : #include "taler/taler_json_lib.h"
      46              : #include "report-lib.h"
      47              : #include "taler/taler_dbevents.h"
      48              : #include "auditor-database/delete_aml_hold.h"
      49              : #include "auditor-database/delete_early_aggregation.h"
      50              : #include "auditor-database/delete_pending_deposit.h"
      51              : #include "auditor-database/event_listen.h"
      52              : #include "auditor-database/get_auditor_progress.h"
      53              : #include "auditor-database/get_balance.h"
      54              : #include "auditor-database/insert_aml_hold.h"
      55              : #include "auditor-database/insert_amount_arithmetic_inconsistency.h"
      56              : #include "auditor-database/insert_auditor_progress.h"
      57              : #include "auditor-database/insert_balance.h"
      58              : #include "auditor-database/insert_early_aggregation.h"
      59              : #include "auditor-database/insert_pending_deposit.h"
      60              : #include "auditor-database/insert_row_inconsistency.h"
      61              : #include "auditor-database/preflight.h"
      62              : #include "auditor-database/start.h"
      63              : #include "auditor-database/update_aml_hold.h"
      64              : #include "exchange-database/get_aggregation_deferral_by_wtid.h"
      65              : #include "exchange-database/get_aggregation_transient_by_wtid.h"
      66              : #include "exchange-database/get_open_legitimization_measure.h"
      67              : #include "exchange-database/get_pending_aggregation.h"
      68              : #include "exchange-database/preflight.h"
      69              : #include "exchange-database/rollback.h"
      70              : struct AggregationContext;
      71              : #define TALER_EXCHANGEDB_AGGREGATION_RESULT_CLOSURE struct AggregationContext
      72              : #include "exchange-database/iterate_aggregations_above_serial_id.h"
      73              : struct ImportMissingWireContext;
      74              : #define TALER_EXCHANGEDB_WIRE_MISSING_RESULT_CLOSURE struct \
      75              :         ImportMissingWireContext
      76              : #include "exchange-database/iterate_batch_deposits_missing_wire.h"
      77              : struct HoldContext;
      78              : #define TALER_EXCHANGEDB_AGGREGATION_WTID_RESULT_CLOSURE struct HoldContext
      79              : #include "exchange-database/iterate_aggregation_wtids_above_serial_id.h"
      80              : #define TALER_AUDITORDB_AML_HOLD_RESULT_CLOSURE struct HoldContext
      81              : #include "auditor-database/iterate_aml_holds.h"
      82              : #include "exchange-database/start_read_only.h"
      83              : 
      84              : 
      85              : /**
      86              :  * Run in test mode. Exit when idle instead of
      87              :  * going to sleep and waiting for more work.
      88              :  */
      89              : static int test_mode;
      90              : 
      91              : /**
      92              :  * Return value from main().
      93              :  */
      94              : static int global_ret;
      95              : 
      96              : /**
      97              :  * Last reserve_out / wire_out serial IDs seen.
      98              :  */
      99              : static TALER_ARL_DEF_PP (wire_batch_deposit_id);
     100              : static TALER_ARL_DEF_PP (wire_aggregation_id);
     101              : 
     102              : /**
     103              :  * Row of `aggregation_tracking` up to which we have looked for wire transfers
     104              :  * the exchange aggregated but did not execute.
     105              :  */
     106              : static TALER_ARL_DEF_PP (wire_hold_aggregation_id);
     107              : 
     108              : /**
     109              :  * Total amount which the exchange did not aggregate/transfer in time.
     110              :  */
     111              : static TALER_ARL_DEF_AB (total_amount_lag);
     112              : 
     113              : /**
     114              :  * Total amount which the exchange did aggregate/transfer too early.
     115              :  */
     116              : static TALER_ARL_DEF_AB (total_early_aggregation);
     117              : 
     118              : /**
     119              :  * Total amount the exchange aggregated but did not wire because it says a
     120              :  * legitimization requirement against the recipient is still open.  Money the
     121              :  * exchange is withholding for legal reasons.
     122              :  */
     123              : static TALER_ARL_DEF_AB (total_aml_hold);
     124              : 
     125              : /**
     126              :  * Total amount the exchange aggregated but did not wire because what it has
     127              :  * collected for the account so far does not cover the wire fee.  Money that is
     128              :  * waiting for the next deposit rather than for anyone's decision, and thus not
     129              :  * an irregularity at all -- it is kept apart so that it does not inflate the
     130              :  * two balances that are.
     131              :  */
     132              : static TALER_ARL_DEF_AB (total_small_aggregate);
     133              : 
     134              : /**
     135              :  * Total amount the exchange aggregated but did not wire and gave no reason
     136              :  * for.  Money the exchange simply has not paid out.
     137              :  */
     138              : static TALER_ARL_DEF_AB (total_transfer_lag);
     139              : 
     140              : /**
     141              :  * Should we run checks that only work for exchange-internal audits?
     142              :  */
     143              : static int internal_checks;
     144              : 
     145              : /**
     146              :  * Database event handler to wake us up again.
     147              :  */
     148              : static struct GNUNET_DB_EventHandler *eh;
     149              : 
     150              : /**
     151              :  * The auditors's configuration.
     152              :  */
     153              : static const struct GNUNET_CONFIGURATION_Handle *cfg;
     154              : 
     155              : 
     156              : /**
     157              :  * Task run on shutdown.
     158              :  *
     159              :  * @param cls NULL
     160              :  */
     161              : static void
     162            6 : do_shutdown (void *cls)
     163              : {
     164              :   (void) cls;
     165            6 :   if (NULL != eh)
     166              :   {
     167            0 :     TALER_AUDITORDB_event_listen_cancel (eh);
     168            0 :     eh = NULL;
     169              :   }
     170            6 :   TALER_ARL_done ();
     171            6 :   TALER_EXCHANGEDB_unload_accounts ();
     172            6 :   TALER_ARL_cfg = NULL;
     173            6 : }
     174              : 
     175              : 
     176              : /**
     177              :  * Closure for import_wire_missing_cb().
     178              :  */
     179              : struct ImportMissingWireContext
     180              : {
     181              :   /**
     182              :    * Set to maximum row ID encountered.
     183              :    */
     184              :   uint64_t max_batch_deposit_uuid;
     185              : 
     186              :   /**
     187              :    * Set to database errors in callback.
     188              :    */
     189              :   enum GNUNET_DB_QueryStatus err;
     190              : };
     191              : 
     192              : 
     193              : /**
     194              :  * Function called on deposits that need to be checked for their
     195              :  * wire transfer.
     196              :  *
     197              :  * @param wc closure, points to a `struct ImportMissingWireContext`
     198              :  * @param batch_deposit_serial_id serial of the entry in the batch deposits table
     199              :  * @param total_amount value of the missing deposits, including fee
     200              :  * @param wire_target_h_payto where should the funds be wired
     201              :  * @param deadline what was the earliest requested wire transfer deadline
     202              :  */
     203              : static void
     204           34 : import_wire_missing_cb (
     205              :   struct ImportMissingWireContext *wc,
     206              :   uint64_t batch_deposit_serial_id,
     207              :   const struct TALER_Amount *total_amount,
     208              :   const struct TALER_FullPaytoHashP *wire_target_h_payto,
     209              :   struct GNUNET_TIME_Timestamp deadline)
     210              : {
     211              :   enum GNUNET_DB_QueryStatus qs;
     212              : 
     213           34 :   if (wc->err < 0)
     214            0 :     return; /* already failed */
     215           34 :   GNUNET_assert (batch_deposit_serial_id >= wc->max_batch_deposit_uuid);
     216           34 :   wc->max_batch_deposit_uuid = batch_deposit_serial_id + 1;
     217           34 :   qs = TALER_AUDITORDB_delete_early_aggregation (
     218              :     TALER_ARL_adb,
     219              :     batch_deposit_serial_id);
     220           34 :   switch (qs)
     221              :   {
     222            0 :   case GNUNET_DB_STATUS_SOFT_ERROR:
     223            0 :     GNUNET_break (0);
     224            0 :     wc->err = qs;
     225            0 :     return;
     226            0 :   case GNUNET_DB_STATUS_HARD_ERROR:
     227            0 :     GNUNET_break (0);
     228            0 :     wc->err = qs;
     229            0 :     return;
     230           34 :   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
     231           34 :     qs = TALER_AUDITORDB_insert_pending_deposit (
     232              :       TALER_ARL_adb,
     233              :       batch_deposit_serial_id,
     234              :       wire_target_h_payto,
     235              :       total_amount,
     236              :       deadline);
     237           34 :     if (0 > qs)
     238              :     {
     239            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     240            0 :       wc->err = qs;
     241            0 :       return;
     242              :     }
     243           34 :     if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
     244           34 :       TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_amount_lag),
     245              :                             &TALER_ARL_USE_AB (total_amount_lag),
     246              :                             total_amount);
     247           34 :     break;
     248            0 :   case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
     249            0 :     TALER_ARL_amount_subtract (&TALER_ARL_USE_AB (total_early_aggregation),
     250              :                                &TALER_ARL_USE_AB (total_early_aggregation),
     251              :                                total_amount);
     252            0 :     break;
     253            0 :   default:
     254            0 :     GNUNET_assert (0);
     255              :   }
     256              : }
     257              : 
     258              : 
     259              : /**
     260              :  * Checks for wire transfers that should have happened.
     261              :  *
     262              :  * @return transaction status
     263              :  */
     264              : static enum GNUNET_DB_QueryStatus
     265            6 : check_for_required_transfers (void)
     266              : {
     267              :   enum GNUNET_DB_QueryStatus qs;
     268            6 :   struct ImportMissingWireContext wc = {
     269              :     .max_batch_deposit_uuid = TALER_ARL_USE_PP (wire_batch_deposit_id),
     270              :     .err = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT
     271              :   };
     272              : 
     273            6 :   qs = TALER_EXCHANGEDB_iterate_batch_deposits_missing_wire (
     274              :     TALER_ARL_edb,
     275              :     TALER_ARL_USE_PP (wire_batch_deposit_id),
     276              :     &import_wire_missing_cb,
     277              :     &wc);
     278            6 :   if (0 > qs)
     279              :   {
     280            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     281            0 :     return qs;
     282              :   }
     283            6 :   if (0 > wc.err)
     284              :   {
     285            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == wc.err);
     286            0 :     return wc.err;
     287              :   }
     288            6 :   TALER_ARL_USE_PP (wire_batch_deposit_id) = wc.max_batch_deposit_uuid;
     289            6 :   return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
     290              : }
     291              : 
     292              : 
     293              : /**
     294              :  * Closure for #clear_finished_transfer_cb().
     295              :  */
     296              : struct AggregationContext
     297              : {
     298              :   /**
     299              :    * Set to maximum row ID encountered.
     300              :    */
     301              :   uint64_t max_aggregation_serial;
     302              : 
     303              :   /**
     304              :    * Set to database errors in callback.
     305              :    */
     306              :   enum GNUNET_DB_QueryStatus err;
     307              : };
     308              : 
     309              : 
     310              : /**
     311              :  * Function called on aggregations that were done for
     312              :  * a (batch) deposit.
     313              :  *
     314              :  * @param ac closure
     315              :  * @param amount affected amount
     316              :  * @param tracking_serial_id where in the table are we
     317              :  * @param batch_deposit_serial_id which batch deposit was aggregated
     318              :  */
     319              : static void
     320           28 : clear_finished_transfer_cb (
     321              :   struct AggregationContext *ac,
     322              :   const struct TALER_Amount *amount,
     323              :   uint64_t tracking_serial_id,
     324              :   uint64_t batch_deposit_serial_id)
     325              : {
     326              :   enum GNUNET_DB_QueryStatus qs;
     327              : 
     328           28 :   if (0 > ac->err)
     329            0 :     return; /* already failed */
     330           28 :   GNUNET_assert (ac->max_aggregation_serial <= tracking_serial_id);
     331           28 :   ac->max_aggregation_serial = tracking_serial_id + 1;
     332           28 :   qs = TALER_AUDITORDB_delete_pending_deposit (
     333              :     TALER_ARL_adb,
     334              :     batch_deposit_serial_id);
     335           28 :   switch (qs)
     336              :   {
     337            0 :   case GNUNET_DB_STATUS_SOFT_ERROR:
     338            0 :     GNUNET_break (0);
     339            0 :     ac->err = qs;
     340            0 :     return;
     341            0 :   case GNUNET_DB_STATUS_HARD_ERROR:
     342            0 :     GNUNET_break (0);
     343            0 :     ac->err = qs;
     344            0 :     return;
     345            0 :   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
     346            0 :     qs = TALER_AUDITORDB_insert_early_aggregation (
     347              :       TALER_ARL_adb,
     348              :       batch_deposit_serial_id,
     349              :       tracking_serial_id,
     350              :       amount);
     351            0 :     if (0 > qs)
     352              :     {
     353            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     354            0 :       ac->err = qs;
     355            0 :       return;
     356              :     }
     357            0 :     if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
     358            0 :       TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_early_aggregation),
     359              :                             &TALER_ARL_USE_AB (total_early_aggregation),
     360              :                             amount);
     361            0 :     break;
     362           28 :   case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
     363           28 :     TALER_ARL_amount_subtract (&TALER_ARL_USE_AB (total_amount_lag),
     364              :                                &TALER_ARL_USE_AB (total_amount_lag),
     365              :                                amount);
     366           28 :     break;
     367            0 :   default:
     368            0 :     GNUNET_assert (0);
     369              :   }
     370              : }
     371              : 
     372              : 
     373              : /**
     374              :  * Checks that all wire transfers that should have happened
     375              :  * (based on deposits) have indeed happened.
     376              :  *
     377              :  * @return transaction status
     378              :  */
     379              : static enum GNUNET_DB_QueryStatus
     380            6 : check_for_completed_transfers (void)
     381              : {
     382            6 :   struct AggregationContext ac = {
     383              :     .max_aggregation_serial = TALER_ARL_USE_PP (wire_aggregation_id),
     384              :     .err = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT
     385              :   };
     386              :   enum GNUNET_DB_QueryStatus qs;
     387              : 
     388            6 :   qs = TALER_EXCHANGEDB_iterate_aggregations_above_serial_id (
     389              :     TALER_ARL_edb,
     390              :     TALER_ARL_USE_PP (wire_aggregation_id),
     391              :     &clear_finished_transfer_cb,
     392              :     &ac);
     393            6 :   if (0 > qs)
     394              :   {
     395            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     396            0 :     return qs;
     397              :   }
     398            6 :   if (0 > ac.err)
     399              :   {
     400            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == ac.err);
     401            0 :     return ac.err;
     402              :   }
     403            6 :   TALER_ARL_USE_PP (wire_aggregation_id) = ac.max_aggregation_serial;
     404            6 :   return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
     405              : }
     406              : 
     407              : 
     408              : /**
     409              :  * Closure for #note_held_transfer_cb() and #evaluate_hold_cb().
     410              :  */
     411              : struct HoldContext
     412              : {
     413              :   /**
     414              :    * Wire transfers already evaluated in this round, so that the three
     415              :    * `aggregation_tracking` rows of one transfer cost one evaluation, and so
     416              :    * that the pass over the holds already on file does not redo them.
     417              :    */
     418              :   struct GNUNET_CONTAINER_MultiHashMap *seen;
     419              : 
     420              :   /**
     421              :    * Sum of the holds the exchange attributes to an open KYC requirement.
     422              :    */
     423              :   struct TALER_Amount aml_hold;
     424              : 
     425              :   /**
     426              :    * Sum of the holds the exchange attributes to the aggregate being too small
     427              :    * to be worth its wire fee.
     428              :    */
     429              :   struct TALER_Amount small_aggregate;
     430              : 
     431              :   /**
     432              :    * Sum of the holds the exchange gave no reason for.
     433              :    */
     434              :   struct TALER_Amount transfer_lag;
     435              : 
     436              :   /**
     437              :    * Set to maximum row ID encountered.
     438              :    */
     439              :   uint64_t max_aggregation_serial;
     440              : 
     441              :   /**
     442              :    * Set to database errors in the callback.
     443              :    */
     444              :   enum GNUNET_DB_QueryStatus err;
     445              : };
     446              : 
     447              : 
     448              : /**
     449              :  * Report a row inconsistency.
     450              :  *
     451              :  * @param[in,out] hc our state, to flag database failures in
     452              :  * @param table name of the exchange table the bad row is in
     453              :  * @param rowid row that is bad, 0 if the finding is not about one row
     454              :  * @param diagnostic what is wrong with it
     455              :  */
     456              : static void
     457            0 : report_row (struct HoldContext *hc,
     458              :             const char *table,
     459              :             uint64_t rowid,
     460              :             const char *diagnostic)
     461              : {
     462            0 :   struct TALER_AUDITORDB_RowInconsistency ri = {
     463              :     .problem_row_id = rowid,
     464              :     .row_table = (char *) table,
     465              :     .diagnostic = (char *) diagnostic
     466              :   };
     467              :   enum GNUNET_DB_QueryStatus qs;
     468              : 
     469            0 :   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     470              :               "Row %llu of `%s' is bad: %s\n",
     471              :               (unsigned long long) rowid,
     472              :               table,
     473              :               diagnostic);
     474            0 :   qs = TALER_AUDITORDB_insert_row_inconsistency (TALER_ARL_adb,
     475              :                                                  &ri);
     476            0 :   if (0 > qs)
     477              :   {
     478            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     479            0 :     hc->err = qs;
     480              :   }
     481            0 : }
     482              : 
     483              : 
     484              : /**
     485              :  * Work out whether the exchange still owes the wire transfer @a wtid, how much
     486              :  * it owes and what it says is keeping it from paying; then record the result
     487              :  * and count it towards the running totals.
     488              :  *
     489              :  * The classification is the exchange's own, read out of `aggregation_deferrals`
     490              :  * -- append-only, and therefore the one statement of its reasons that survives
     491              :  * replication to an external auditor.  Taking it at face value is deliberate:
     492              :  * whether a hold is *justified* is a question for the people reading
     493              :  * `/monitoring/aml-holds`, not for the auditor.  What the auditor does check is
     494              :  * that the claim is consistent with everything else it can see: the amount
     495              :  * against its own recomputation, a KYC claim against the legitimization tables,
     496              :  * and under `-i` the whole thing against the live `aggregation_transient`.  A
     497              :  * disagreement is reported rather than resolved in either side's favour.
     498              :  *
     499              :  * A transfer with no `aggregation_deferrals` row at all is a transfer the
     500              :  * exchange decided on, did not make, and never explained; that is
     501              :  * `total_transfer_lag`.
     502              :  *
     503              :  * @param[in,out] hc our state
     504              :  * @param wtid wire transfer to examine
     505              :  */
     506              : static void
     507            0 : evaluate_hold (struct HoldContext *hc,
     508              :                const struct TALER_WireTransferIdentifierRawP *wtid)
     509              : {
     510              :   struct TALER_FullPaytoHashP h_payto;
     511            0 :   struct TALER_FullPayto payto = { NULL };
     512              :   struct TALER_NormalizedPaytoHashP h_normalized_payto;
     513              :   struct TALER_Amount deposited;
     514              :   struct TALER_Amount refunded;
     515              :   struct TALER_Amount deposit_fee;
     516              :   struct TALER_Amount deductions;
     517              :   struct TALER_Amount amount;
     518              :   struct TALER_Amount claimed;
     519              :   struct GNUNET_TIME_Timestamp deferral_time;
     520              :   struct GNUNET_TIME_Absolute measure_start;
     521            0 :   enum TALER_EXCHANGEDB_DeferralReason reason = TALER_EXCHANGEDB_DR_NONE;
     522            0 :   uint64_t claimed_measure = 0;
     523            0 :   uint64_t measure = 0;
     524              :   bool have_claim;
     525              :   bool open_measure;
     526              :   enum GNUNET_DB_QueryStatus qs;
     527              : 
     528            0 :   qs = TALER_EXCHANGEDB_get_pending_aggregation (TALER_ARL_edb,
     529              :                                                  wtid,
     530              :                                                  &h_payto,
     531              :                                                  &payto,
     532              :                                                  &deposited,
     533              :                                                  &refunded,
     534              :                                                  &deposit_fee);
     535            0 :   if (0 > qs)
     536              :   {
     537            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     538            0 :     hc->err = qs;
     539            0 :     return;
     540              :   }
     541            0 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
     542              :   {
     543              :     /* The `wire_out' row has appeared: the money left the exchange, so the
     544              :        hold ends and stops counting towards either total.  This is also the
     545              :        only place a hold is ever cleared -- there is no separate pass over
     546              :        `wire_out`, because a transfer that was executed simply stops being a
     547              :        pending aggregation. */
     548            0 :     qs = TALER_AUDITORDB_delete_aml_hold (TALER_ARL_adb,
     549              :                                           wtid);
     550            0 :     if (0 > qs)
     551              :     {
     552            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     553            0 :       hc->err = qs;
     554              :     }
     555            0 :     return;
     556              :   }
     557              : 
     558              :   /* What the exchange still owes, computed the way `exchange_do_aggregate'
     559              :      computes it, so that it is comparable with what the exchange itself
     560              :      recorded. */
     561            0 :   TALER_ARL_amount_add (&deductions,
     562              :                         &refunded,
     563              :                         &deposit_fee);
     564            0 :   if (TALER_ARL_SR_INVALID_NEGATIVE ==
     565            0 :       TALER_ARL_amount_subtract_neg (&amount,
     566              :                                      &deposited,
     567              :                                      &deductions))
     568              :   {
     569              :     char *diag;
     570              : 
     571            0 :     GNUNET_asprintf (&diag,
     572              :                      "refunds and deposit fees exceed the deposits aggregated"
     573              :                      " into wire transfer %s",
     574              :                      TALER_B2S (wtid));
     575            0 :     report_row (hc,
     576              :                 "aggregation_tracking",
     577              :                 0,
     578              :                 diag);
     579            0 :     GNUNET_free (diag);
     580            0 :     GNUNET_assert (GNUNET_OK ==
     581              :                    TALER_amount_set_zero (TALER_ARL_currency,
     582              :                                           &amount));
     583              :   }
     584              : 
     585              :   /* What the exchange says about it.  Absent for a transfer it never
     586              :      explained, and for one deferred by a version of the exchange from before
     587              :      `aggregation_deferrals' existed. */
     588            0 :   qs = TALER_EXCHANGEDB_get_aggregation_deferral_by_wtid (TALER_ARL_edb,
     589              :                                                           wtid,
     590              :                                                           &claimed,
     591              :                                                           &reason,
     592              :                                                           &claimed_measure,
     593              :                                                           &deferral_time);
     594            0 :   if (0 > qs)
     595              :   {
     596            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     597            0 :     hc->err = qs;
     598            0 :     GNUNET_free (payto.full_payto);
     599            0 :     return;
     600              :   }
     601            0 :   have_claim = (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs);
     602            0 :   if (! have_claim)
     603              :   {
     604            0 :     reason = TALER_EXCHANGEDB_DR_NONE;
     605            0 :     claimed_measure = 0;
     606              :   }
     607              : 
     608              :   /* `legitimization_measures' is reached through the account's *normalized*
     609              :      payto hash, and `wire_targets.h_normalized_payto' is not replicated, so
     610              :      the normalization has to happen here rather than in the query. */
     611            0 :   TALER_full_payto_normalize_and_hash (payto,
     612              :                                        &h_normalized_payto);
     613            0 :   qs = TALER_EXCHANGEDB_get_open_legitimization_measure (
     614              :     TALER_ARL_edb,
     615              :     &h_normalized_payto,
     616              :     &measure,
     617              :     &measure_start);
     618            0 :   if (0 > qs)
     619              :   {
     620            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     621            0 :     hc->err = qs;
     622            0 :     GNUNET_free (payto.full_payto);
     623            0 :     return;
     624              :   }
     625            0 :   open_measure = (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs);
     626            0 :   if (! open_measure)
     627            0 :     measure = 0;
     628            0 :   switch (reason)
     629              :   {
     630            0 :   case TALER_EXCHANGEDB_DR_NONE:
     631            0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     632              :                 "Exchange holds %s for %s and says nothing about why\n",
     633              :                 TALER_amount2s (&amount),
     634              :                 payto.full_payto);
     635            0 :     break;
     636            0 :   case TALER_EXCHANGEDB_DR_AMOUNT_TOO_SMALL:
     637            0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     638              :                 "Exchange holds %s for %s, too little to cover the wire fee\n",
     639              :                 TALER_amount2s (&amount),
     640              :                 payto.full_payto);
     641            0 :     break;
     642            0 :   case TALER_EXCHANGEDB_DR_KYC:
     643            0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     644              :                 "Exchange holds %s for %s awaiting KYC measure %llu\n",
     645              :                 TALER_amount2s (&amount),
     646              :                 payto.full_payto,
     647              :                 (unsigned long long) claimed_measure);
     648            0 :     break;
     649              :   }
     650              : 
     651            0 :   if (have_claim &&
     652            0 :       (0 != TALER_amount_cmp (&claimed,
     653              :                               &amount)) )
     654              :   {
     655            0 :     struct TALER_AUDITORDB_AmountArithmeticInconsistency aai = {
     656              :       .problem_row_id = 0,
     657              :       .operation = (char *) "aggregation deferral",
     658              :       .exchange_amount = claimed,
     659              :       .auditor_amount = amount,
     660              :       /* claiming to hold less than it owes understates the exchange's
     661              :          liabilities, which is the direction that favours the exchange */
     662            0 :       .profitable = (0 > TALER_amount_cmp (&claimed,
     663              :                                            &amount))
     664              :                     ? 1
     665              :                     : -1
     666              :     };
     667              : 
     668            0 :     qs = TALER_AUDITORDB_insert_amount_arithmetic_inconsistency (
     669              :       TALER_ARL_adb,
     670              :       &aai);
     671            0 :     if (0 > qs)
     672              :     {
     673            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     674            0 :       hc->err = qs;
     675              :     }
     676              :   }
     677              : 
     678            0 :   if ( (TALER_EXCHANGEDB_DR_KYC == reason) &&
     679            0 :        (! open_measure) )
     680              :   {
     681              :     char *diag;
     682              : 
     683              :     /* The one claim the auditor can check on its own: a legitimization
     684              :        requirement is a row in `legitimization_measures' that no
     685              :        `legitimization_processes' row carried through to a decision, and that
     686              :        is replicated.  Claiming KYC without one is how an exchange would dress
     687              :        up simply keeping the money. */
     688            0 :     GNUNET_asprintf (&diag,
     689              :                      "exchange withholds wire transfer %s for legitimization"
     690              :                      " measure %llu, which is not open",
     691              :                      TALER_B2S (wtid),
     692              :                      (unsigned long long) claimed_measure);
     693            0 :     report_row (hc,
     694              :                 "aggregation_deferrals",
     695              :                 claimed_measure,
     696              :                 diag);
     697            0 :     GNUNET_free (diag);
     698              :   }
     699              : 
     700            0 :   if (internal_checks)
     701              :   {
     702              :     struct TALER_Amount transient;
     703              :     uint64_t transient_measure;
     704              : 
     705              :     /* `aggregation_transient' is the live version of what the deferral row
     706              :        claims: updated in place, deleted on payout, and unreplicable.  On an
     707              :        internal audit we can hold the two against each other, which is what
     708              :        catches a `aggregation_deferrals' row that was written once and then
     709              :        left behind by an aggregate that has moved on. */
     710            0 :     qs = TALER_EXCHANGEDB_get_aggregation_transient_by_wtid (
     711              :       TALER_ARL_edb,
     712              :       &h_payto,
     713              :       wtid,
     714              :       &transient,
     715              :       &transient_measure);
     716            0 :     if (0 > qs)
     717              :     {
     718            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     719            0 :       hc->err = qs;
     720            0 :       GNUNET_free (payto.full_payto);
     721            0 :       return;
     722              :     }
     723            0 :     if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
     724              :     {
     725              :       char *diag;
     726              : 
     727              :       /* The exchange marked the deposits as aggregated, did not transfer the
     728              :          money, and is not tracking that it owes it either. */
     729            0 :       GNUNET_asprintf (&diag,
     730              :                        "wire transfer %s was aggregated but is neither in"
     731              :                        " wire_out nor in aggregation_transient",
     732              :                        TALER_B2S (wtid));
     733            0 :       report_row (hc,
     734              :                   "aggregation_transient",
     735              :                   0,
     736              :                   diag);
     737            0 :       GNUNET_free (diag);
     738              :     }
     739              :     else
     740              :     {
     741            0 :       if (0 !=
     742            0 :           TALER_amount_cmp (&transient,
     743              :                             &amount))
     744              :       {
     745            0 :         struct TALER_AUDITORDB_AmountArithmeticInconsistency aai = {
     746              :           .problem_row_id = 0,
     747              :           .operation = (char *) "aggregation transient",
     748              :           .exchange_amount = transient,
     749              :           .auditor_amount = amount,
     750              :           /* claiming to hold less than it owes understates the exchange's
     751              :              liabilities, which is the direction that favours the exchange */
     752            0 :           .profitable = (0 > TALER_amount_cmp (&transient,
     753              :                                                &amount))
     754              :                         ? 1
     755              :                         : -1
     756              :         };
     757              : 
     758            0 :         qs = TALER_AUDITORDB_insert_amount_arithmetic_inconsistency (
     759              :           TALER_ARL_adb,
     760              :           &aai);
     761            0 :         if (0 > qs)
     762              :         {
     763            0 :           GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     764            0 :           hc->err = qs;
     765              :         }
     766              :       }
     767            0 :       if ( (TALER_EXCHANGEDB_DR_KYC == reason) !=
     768              :            (0 != transient_measure) )
     769              :       {
     770              :         char *diag;
     771              : 
     772            0 :         GNUNET_asprintf (&diag,
     773              :                          "exchange is tracking wire transfer %s as %s but its"
     774              :                          " last aggregation_deferrals row says %s",
     775              :                          TALER_B2S (wtid),
     776            0 :                          (0 != transient_measure)
     777              :                          ? "awaiting legitimization"
     778              :                          : "not awaiting legitimization",
     779            0 :                          (TALER_EXCHANGEDB_DR_KYC == reason)
     780              :                          ? "it is"
     781              :                          : "it is not");
     782            0 :         report_row (hc,
     783              :                     "aggregation_transient",
     784              :                     transient_measure,
     785              :                     diag);
     786            0 :         GNUNET_free (diag);
     787              :       }
     788              :     }
     789              :   }
     790              : 
     791            0 :   qs = TALER_AUDITORDB_insert_aml_hold (TALER_ARL_adb,
     792              :                                         wtid,
     793              :                                         &h_payto,
     794              :                                         payto,
     795              :                                         &amount,
     796              :                                         (uint32_t) reason,
     797              :                                         claimed_measure);
     798            0 :   GNUNET_free (payto.full_payto);
     799            0 :   if (0 > qs)
     800              :   {
     801            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     802            0 :     hc->err = qs;
     803            0 :     return;
     804              :   }
     805              :   /* Insert-then-update, not "insert if new": the amount grows as further
     806              :      deposits join the transfer, and the reason the exchange gives for holding
     807              :      it can change long after the hold began. */
     808            0 :   qs = TALER_AUDITORDB_update_aml_hold (TALER_ARL_adb,
     809              :                                         wtid,
     810              :                                         &amount,
     811              :                                         (uint32_t) reason,
     812              :                                         claimed_measure);
     813            0 :   if (0 > qs)
     814              :   {
     815            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     816            0 :     hc->err = qs;
     817            0 :     return;
     818              :   }
     819              :   {
     820              :     struct TALER_Amount *bucket;
     821              : 
     822            0 :     switch (reason)
     823              :     {
     824            0 :     case TALER_EXCHANGEDB_DR_AMOUNT_TOO_SMALL:
     825            0 :       bucket = &hc->small_aggregate;
     826            0 :       break;
     827            0 :     case TALER_EXCHANGEDB_DR_KYC:
     828            0 :       bucket = &hc->aml_hold;
     829            0 :       break;
     830            0 :     case TALER_EXCHANGEDB_DR_NONE:
     831              :     default:
     832            0 :       bucket = &hc->transfer_lag;
     833            0 :       break;
     834              :     }
     835            0 :     TALER_ARL_amount_add (bucket,
     836              :                           bucket,
     837              :                           &amount);
     838              :   }
     839              : }
     840              : 
     841              : 
     842              : /**
     843              :  * Function called on each row of the aggregation tracking table.
     844              :  *
     845              :  * @param hc closure
     846              :  * @param rowid row of the entry in the aggregation tracking table
     847              :  * @param wtid wire transfer the deposit was aggregated into
     848              :  * @param wire_target_h_payto account the wire transfer should go to
     849              :  * @param pending false if the exchange really did make the transfer
     850              :  * @return #GNUNET_OK to continue to iterate
     851              :  */
     852              : static enum GNUNET_GenericReturnValue
     853           28 : note_held_transfer_cb (
     854              :   struct HoldContext *hc,
     855              :   uint64_t rowid,
     856              :   const struct TALER_WireTransferIdentifierRawP *wtid,
     857              :   const struct TALER_FullPaytoHashP *wire_target_h_payto,
     858              :   bool pending)
     859              : {
     860              :   struct GNUNET_HashCode key;
     861              : 
     862              :   (void) wire_target_h_payto;
     863           28 :   if (0 > hc->err)
     864            0 :     return GNUNET_SYSERR; /* already failed */
     865           28 :   GNUNET_assert (hc->max_aggregation_serial <= rowid);
     866           28 :   hc->max_aggregation_serial = rowid + 1;
     867           28 :   if (! pending)
     868              :   {
     869              :     /* Aggregated and paid, the ordinary case.  It cannot have a hold on file
     870              :        either: a wtid is only ever wired once, so the rows we are walking here
     871              :        are the ones that created it, and a hold from an earlier run is caught
     872              :        by the pass over the holds instead. */
     873           28 :     return GNUNET_OK;
     874              :   }
     875            0 :   GNUNET_CRYPTO_hash (wtid,
     876              :                       sizeof (*wtid),
     877              :                       &key);
     878            0 :   if (GNUNET_OK !=
     879            0 :       GNUNET_CONTAINER_multihashmap_put (
     880              :         hc->seen,
     881              :         &key,
     882            0 :         hc->seen,
     883              :         GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
     884            0 :     return GNUNET_OK; /* another row of the same transfer, already evaluated */
     885            0 :   evaluate_hold (hc,
     886              :                  wtid);
     887            0 :   if (0 > hc->err)
     888            0 :     return GNUNET_SYSERR;
     889            0 :   return GNUNET_OK;
     890              : }
     891              : 
     892              : 
     893              : /**
     894              :  * Function called on each hold we already had on file.
     895              :  *
     896              :  * @param hc closure
     897              :  * @param ah the hold
     898              :  * @return #GNUNET_OK to continue to iterate
     899              :  */
     900              : static enum GNUNET_GenericReturnValue
     901            0 : evaluate_hold_cb (struct HoldContext *hc,
     902              :                   const struct TALER_AUDITORDB_AmlHold *ah)
     903              : {
     904              :   struct GNUNET_HashCode key;
     905              : 
     906            0 :   if (0 > hc->err)
     907            0 :     return GNUNET_SYSERR; /* already failed */
     908            0 :   GNUNET_CRYPTO_hash (&ah->wtid,
     909              :                       sizeof (ah->wtid),
     910              :                       &key);
     911            0 :   if (GNUNET_YES ==
     912            0 :       GNUNET_CONTAINER_multihashmap_contains (hc->seen,
     913              :                                               &key))
     914            0 :     return GNUNET_OK; /* fresh rows arrived for it, already re-evaluated */
     915            0 :   evaluate_hold (hc,
     916              :                  &ah->wtid);
     917            0 :   if (0 > hc->err)
     918            0 :     return GNUNET_SYSERR;
     919            0 :   return GNUNET_OK;
     920              : }
     921              : 
     922              : 
     923              : /**
     924              :  * Checks which wire transfers the exchange aggregated but did not execute, and
     925              :  * what it says is keeping it from making them.
     926              :  *
     927              :  * Unlike the two checks above, the three balances this maintains are current
     928              :  * state and not a running tally: they are recomputed from the holds on file on
     929              :  * every run.  That is what keeps them convergent, because a hold changes in
     930              :  * three independent ways -- it grows as deposits are added to the transfer, it
     931              :  * is reclassified when the exchange states a different reason, and it ends when
     932              :  * the transfer is finally made -- and only the first of those is announced by
     933              :  * a new row that a cursor could walk.
     934              :  *
     935              :  * @return transaction status
     936              :  */
     937              : static enum GNUNET_DB_QueryStatus
     938            6 : check_for_held_transfers (void)
     939              : {
     940            6 :   struct HoldContext hc = {
     941              :     .max_aggregation_serial = TALER_ARL_USE_PP (wire_hold_aggregation_id),
     942              :     .err = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT
     943              :   };
     944              :   enum GNUNET_DB_QueryStatus qs;
     945              : 
     946            6 :   GNUNET_assert (GNUNET_OK ==
     947              :                  TALER_amount_set_zero (TALER_ARL_currency,
     948              :                                         &hc.aml_hold));
     949            6 :   GNUNET_assert (GNUNET_OK ==
     950              :                  TALER_amount_set_zero (TALER_ARL_currency,
     951              :                                         &hc.small_aggregate));
     952            6 :   GNUNET_assert (GNUNET_OK ==
     953              :                  TALER_amount_set_zero (TALER_ARL_currency,
     954              :                                         &hc.transfer_lag));
     955            6 :   hc.seen = GNUNET_CONTAINER_multihashmap_create (128,
     956              :                                                   GNUNET_NO);
     957              :   /* First the aggregations we have not seen before, then the holds we already
     958              :      knew about; the map keeps the two from doing each other's work. */
     959            6 :   qs = TALER_EXCHANGEDB_iterate_aggregation_wtids_above_serial_id (
     960              :     TALER_ARL_edb,
     961              :     TALER_ARL_USE_PP (wire_hold_aggregation_id),
     962              :     &note_held_transfer_cb,
     963              :     &hc);
     964            6 :   if (0 > qs)
     965              :   {
     966            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     967            0 :     goto cleanup;
     968              :   }
     969            6 :   if (0 > hc.err)
     970              :   {
     971            0 :     qs = hc.err;
     972            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     973            0 :     goto cleanup;
     974              :   }
     975            6 :   qs = TALER_AUDITORDB_iterate_aml_holds (TALER_ARL_adb,
     976              :                                           &evaluate_hold_cb,
     977              :                                           &hc);
     978            6 :   if (0 > qs)
     979              :   {
     980            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     981            0 :     goto cleanup;
     982              :   }
     983            6 :   if (0 > hc.err)
     984              :   {
     985            0 :     qs = hc.err;
     986            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     987            0 :     goto cleanup;
     988              :   }
     989            6 :   TALER_ARL_USE_PP (wire_hold_aggregation_id) = hc.max_aggregation_serial;
     990            6 :   TALER_ARL_USE_AB (total_aml_hold) = hc.aml_hold;
     991            6 :   TALER_ARL_USE_AB (total_small_aggregate) = hc.small_aggregate;
     992            6 :   TALER_ARL_USE_AB (total_transfer_lag) = hc.transfer_lag;
     993              :   /* One TALER_amount2s() per statement: it returns a single static buffer. */
     994            6 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     995              :               "Exchange is holding %s awaiting KYC\n",
     996              :               TALER_amount2s (&hc.aml_hold));
     997            6 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     998              :               "Exchange is holding %s in aggregates below the wire fee\n",
     999              :               TALER_amount2s (&hc.small_aggregate));
    1000            6 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1001              :               "Exchange is holding %s for no stated reason\n",
    1002              :               TALER_amount2s (&hc.transfer_lag));
    1003            6 :   qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
    1004            6 : cleanup:
    1005            6 :   GNUNET_CONTAINER_multihashmap_destroy (hc.seen);
    1006            6 :   return qs;
    1007              : }
    1008              : 
    1009              : 
    1010              : /**
    1011              :  * Start the database transactions and begin the audit.
    1012              :  *
    1013              :  * @return transaction status
    1014              :  */
    1015              : static enum GNUNET_DB_QueryStatus
    1016            6 : begin_transaction (void)
    1017              : {
    1018              :   enum GNUNET_DB_QueryStatus qs;
    1019              : 
    1020            6 :   if (GNUNET_SYSERR ==
    1021            6 :       TALER_EXCHANGEDB_preflight (TALER_ARL_edb))
    1022              :   {
    1023            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1024              :                 "Failed to initialize exchange database connection.\n");
    1025            0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    1026              :   }
    1027            6 :   if (GNUNET_SYSERR ==
    1028            6 :       TALER_AUDITORDB_preflight (TALER_ARL_adb))
    1029              :   {
    1030            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1031              :                 "Failed to initialize auditor database session.\n");
    1032            0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    1033              :   }
    1034            6 :   if (GNUNET_OK !=
    1035            6 :       TALER_AUDITORDB_start (TALER_ARL_adb,
    1036              :                              "auditor-transfer"))
    1037              :   {
    1038            0 :     GNUNET_break (0);
    1039            0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    1040              :   }
    1041            6 :   if (GNUNET_OK !=
    1042            6 :       TALER_EXCHANGEDB_start_read_only (TALER_ARL_edb,
    1043              :                                         "transfer auditor"))
    1044              :   {
    1045            0 :     GNUNET_break (0);
    1046            0 :     TALER_AUDITORDB_rollback (TALER_ARL_adb);
    1047            0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    1048              :   }
    1049            6 :   qs = TALER_AUDITORDB_get_auditor_progress (
    1050              :     TALER_ARL_adb,
    1051              :     TALER_ARL_GET_PP (wire_batch_deposit_id),
    1052              :     TALER_ARL_GET_PP (wire_aggregation_id),
    1053              :     TALER_ARL_GET_PP (wire_hold_aggregation_id),
    1054              :     NULL);
    1055            6 :   if (0 > qs)
    1056            0 :     goto handle_db_error;
    1057              : 
    1058            6 :   qs = TALER_AUDITORDB_get_balance (
    1059              :     TALER_ARL_adb,
    1060              :     TALER_ARL_GET_AB (total_amount_lag),
    1061              :     TALER_ARL_GET_AB (total_early_aggregation),
    1062              :     TALER_ARL_GET_AB (total_aml_hold),
    1063              :     TALER_ARL_GET_AB (total_small_aggregate),
    1064              :     TALER_ARL_GET_AB (total_transfer_lag),
    1065              :     NULL);
    1066            6 :   if (0 > qs)
    1067            0 :     goto handle_db_error;
    1068            6 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    1069              :   {
    1070            0 :     GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
    1071              :                 "First analysis of with transfer auditor, starting audit from scratch\n");
    1072              :   }
    1073              :   else
    1074              :   {
    1075            6 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1076              :                 "Resuming transfer audit at %llu / %llu / %llu\n",
    1077              :                 (unsigned long long) TALER_ARL_USE_PP (wire_batch_deposit_id),
    1078              :                 (unsigned long long) TALER_ARL_USE_PP (wire_aggregation_id),
    1079              :                 (unsigned long long) TALER_ARL_USE_PP (
    1080              :                   wire_hold_aggregation_id));
    1081              :   }
    1082              : 
    1083            6 :   qs = check_for_required_transfers ();
    1084            6 :   if (0 > qs)
    1085            0 :     goto handle_db_error;
    1086            6 :   qs = check_for_completed_transfers ();
    1087            6 :   if (0 > qs)
    1088            0 :     goto handle_db_error;
    1089            6 :   qs = check_for_held_transfers ();
    1090            6 :   if (0 > qs)
    1091            0 :     goto handle_db_error;
    1092              : 
    1093            6 :   qs = TALER_AUDITORDB_insert_auditor_progress (
    1094              :     TALER_ARL_adb,
    1095              :     TALER_ARL_SET_PP (wire_batch_deposit_id),
    1096              :     TALER_ARL_SET_PP (wire_aggregation_id),
    1097              :     TALER_ARL_SET_PP (wire_hold_aggregation_id),
    1098              :     NULL);
    1099            6 :   if (0 > qs)
    1100            0 :     goto handle_db_error;
    1101            6 :   qs = TALER_AUDITORDB_insert_balance (
    1102              :     TALER_ARL_adb,
    1103              :     TALER_ARL_SET_AB (total_amount_lag),
    1104              :     TALER_ARL_SET_AB (total_early_aggregation),
    1105              :     TALER_ARL_SET_AB (total_aml_hold),
    1106              :     TALER_ARL_SET_AB (total_small_aggregate),
    1107              :     TALER_ARL_SET_AB (total_transfer_lag),
    1108              :     NULL);
    1109            6 :   if (0 > qs)
    1110            0 :     goto handle_db_error;
    1111            6 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1112              :               "Concluded audit step at %llu/%llu/%llu\n",
    1113              :               (unsigned long long) TALER_ARL_USE_PP (wire_aggregation_id),
    1114              :               (unsigned long long) TALER_ARL_USE_PP (wire_batch_deposit_id),
    1115              :               (unsigned long long) TALER_ARL_USE_PP (
    1116              :                 wire_hold_aggregation_id));
    1117            6 :   TALER_EXCHANGEDB_rollback (TALER_ARL_edb);
    1118            6 :   qs = TALER_AUDITORDB_commit (TALER_ARL_adb);
    1119            6 :   if (0 > qs)
    1120            0 :     goto handle_db_error;
    1121            6 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1122              :               "Transaction concluded!\n");
    1123            6 :   return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
    1124            0 : handle_db_error:
    1125            0 :   TALER_AUDITORDB_rollback (TALER_ARL_adb);
    1126            0 :   TALER_EXCHANGEDB_rollback (TALER_ARL_edb);
    1127            0 :   GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1128            0 :   return qs;
    1129              : }
    1130              : 
    1131              : 
    1132              : /**
    1133              :  * Start auditor process.
    1134              :  */
    1135              : static void
    1136            6 : start (void)
    1137              : {
    1138              :   enum GNUNET_DB_QueryStatus qs;
    1139              : 
    1140            6 :   for (unsigned int max_retries = 3; max_retries>0; max_retries--)
    1141              :   {
    1142            6 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1143              :                 "Trying again (%u attempts left)\n",
    1144              :                 max_retries);
    1145            6 :     qs = begin_transaction ();
    1146            6 :     if (GNUNET_DB_STATUS_SOFT_ERROR != qs)
    1147            6 :       break;
    1148              :   }
    1149            6 :   if (0 > qs)
    1150              :   {
    1151            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1152              :                 "Audit failed\n");
    1153            0 :     GNUNET_break (0);
    1154            0 :     global_ret = EXIT_FAILURE;
    1155            0 :     GNUNET_SCHEDULER_shutdown ();
    1156            0 :     return;
    1157              :   }
    1158              : }
    1159              : 
    1160              : 
    1161              : /**
    1162              :  * Function called on events received from Postgres.
    1163              :  *
    1164              :  * @param cls closure, NULL
    1165              :  * @param extra additional event data provided
    1166              :  * @param extra_size number of bytes in @a extra
    1167              :  */
    1168              : static void
    1169            0 : db_notify (void *cls,
    1170              :            const void *extra,
    1171              :            size_t extra_size)
    1172              : {
    1173              :   (void) cls;
    1174              :   (void) extra;
    1175              :   (void) extra_size;
    1176              : 
    1177            0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1178              :               "Received notification to wake transfer helper\n");
    1179            0 :   start ();
    1180            0 : }
    1181              : 
    1182              : 
    1183              : /**
    1184              :  * Main function that will be run.
    1185              :  *
    1186              :  * @param cls closure
    1187              :  * @param args remaining command-line arguments
    1188              :  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
    1189              :  * @param c configuration
    1190              :  */
    1191              : static void
    1192            6 : run (void *cls,
    1193              :      char *const *args,
    1194              :      const char *cfgfile,
    1195              :      const struct GNUNET_CONFIGURATION_Handle *c)
    1196              : {
    1197              :   (void) cls;
    1198              :   (void) args;
    1199              :   (void) cfgfile;
    1200            6 :   cfg = c;
    1201            6 :   if (EXIT_SUCCESS !=
    1202            6 :       (global_ret = TALER_ARL_init (c)))
    1203              :   {
    1204            0 :     return;
    1205              :   }
    1206            6 :   GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
    1207              :                                  NULL);
    1208            6 :   if (GNUNET_OK !=
    1209            6 :       TALER_EXCHANGEDB_load_accounts (TALER_ARL_cfg,
    1210              :                                       TALER_EXCHANGEDB_ALO_DEBIT
    1211              :                                       | TALER_EXCHANGEDB_ALO_CREDIT
    1212              :                                       | TALER_EXCHANGEDB_ALO_AUTHDATA))
    1213              :   {
    1214            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1215              :                 "No bank accounts configured\n");
    1216            0 :     global_ret = EXIT_NOTCONFIGURED;
    1217            0 :     GNUNET_SCHEDULER_shutdown ();
    1218            0 :     return;
    1219              :   }
    1220            6 :   if (0 == test_mode)
    1221              :   {
    1222              :     // FIXME-Optimization: use different event type in the future!
    1223            0 :     struct GNUNET_DB_EventHeaderP es = {
    1224            0 :       .size = htons (sizeof (es)),
    1225            0 :       .type = htons (TALER_DBEVENT_EXCHANGE_AUDITOR_WAKE_HELPER_WIRE)
    1226              :     };
    1227              : 
    1228            0 :     eh = TALER_AUDITORDB_event_listen (TALER_ARL_adb,
    1229              :                                        &es,
    1230            0 :                                        GNUNET_TIME_UNIT_FOREVER_REL,
    1231              :                                        &db_notify,
    1232              :                                        NULL);
    1233            0 :     GNUNET_assert (NULL != eh);
    1234              :   }
    1235            6 :   start ();
    1236              : }
    1237              : 
    1238              : 
    1239              : /**
    1240              :  * The main function of the wire auditing tool. Checks that
    1241              :  * the exchange's records of wire transfers match that of
    1242              :  * the wire gateway.
    1243              :  *
    1244              :  * @param argc number of arguments from the command line
    1245              :  * @param argv command line arguments
    1246              :  * @return 0 ok, 1 on error
    1247              :  */
    1248              : int
    1249            6 : main (int argc,
    1250              :       char *const *argv)
    1251              : {
    1252            6 :   const struct GNUNET_GETOPT_CommandLineOption options[] = {
    1253            6 :     GNUNET_GETOPT_option_flag ('i',
    1254              :                                "internal",
    1255              :                                "perform checks only applicable for exchange-internal audits",
    1256              :                                &internal_checks),
    1257            6 :     GNUNET_GETOPT_option_flag ('t',
    1258              :                                "test",
    1259              :                                "run in test mode and exit when idle",
    1260              :                                &test_mode),
    1261            6 :     GNUNET_GETOPT_option_timetravel ('T',
    1262              :                                      "timetravel"),
    1263              :     GNUNET_GETOPT_OPTION_END
    1264              :   };
    1265              :   enum GNUNET_GenericReturnValue ret;
    1266              : 
    1267            6 :   ret = GNUNET_PROGRAM_run (
    1268              :     TALER_AUDITOR_project_data (),
    1269              :     argc,
    1270              :     argv,
    1271              :     "taler-helper-auditor-transfer",
    1272              :     gettext_noop (
    1273              :       "Audit exchange database for consistency of aggregations/transfers with respect to deposit deadlines"),
    1274              :     options,
    1275              :     &run,
    1276              :     NULL);
    1277            6 :   if (GNUNET_SYSERR == ret)
    1278            0 :     return EXIT_NOTCONFIGURED;
    1279            6 :   if (GNUNET_NO == ret)
    1280            0 :     return EXIT_SUCCESS;
    1281            6 :   return global_ret;
    1282              : }
    1283              : 
    1284              : 
    1285              : /* end of taler-helper-auditor-transfer.c */
        

Generated by: LCOV version 2.0-1