LCOV - code coverage report
Current view: top level - auditor - taler-helper-auditor-reserves.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 13.9 % 546 76
Test Date: 2026-09-09 15:11:34 Functions: 22.2 % 18 4

            Line data    Source code
       1              : /*
       2              :   This file is part of TALER
       3              :   Copyright (C) 2016-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 Affero 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 Affero Public License for more details.
      12              : 
      13              :   You should have received a copy of the GNU Affero 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-reserves.c
      18              :  * @brief audits the reserves of an exchange database
      19              :  * @author Christian Grothoff
      20              :  */
      21              : #include "platform.h"
      22              : /* RESERVE_IN's callback typedef also lives in exchangedb_lib.h (pulled in
      23              :    transitively by report-lib.h below), so its closure override must be
      24              :    established before any of those includes. */
      25              : struct ReserveContext;
      26              : #define TALER_EXCHANGEDB_RESERVE_IN_RESULT_CLOSURE struct ReserveContext
      27              : #include "auditordb_lib.h"
      28              : #include "report-lib.h"
      29              : #include "taler/taler_dbevents.h"
      30              : #include "exchangedb_lib.h"
      31              : #include "exchange-database/get_reserve_close_request_info.h"
      32              : #include "auditor-database/delete_reserve_info.h"
      33              : #include "auditor-database/event_listen.h"
      34              : #include "auditor-database/get_auditor_progress.h"
      35              : #include "auditor-database/get_balance.h"
      36              : #include "auditor-database/get_reserve_info.h"
      37              : #include "auditor-database/insert_amount_arithmetic_inconsistency.h"
      38              : #include "auditor-database/insert_auditor_progress.h"
      39              : #include "auditor-database/insert_bad_sig_losses.h"
      40              : #include "auditor-database/insert_balance.h"
      41              : #include \
      42              :   "auditor-database/insert_denomination_key_validity_withdraw_inconsistency.h"
      43              : #include \
      44              :   "auditor-database/insert_reserve_balance_insufficient_inconsistency.h"
      45              : #include \
      46              :   "auditor-database/insert_reserve_balance_summary_wrong_inconsistency.h"
      47              : #include "auditor-database/insert_reserve_info.h"
      48              : #include "auditor-database/insert_reserve_not_closed_inconsistency.h"
      49              : #include "auditor-database/insert_row_inconsistency.h"
      50              : #include "auditor-database/update_reserve_info.h"
      51              : #include "exchange-database/get_denomination_revocation.h"
      52              : #include "exchange-database/get_wire_fee.h"
      53              : #include "exchange-database/get_reserve.h"
      54              : struct ReserveContext;
      55              : #define TALER_EXCHANGEDB_ACCOUNT_MERGE_RESULT_CLOSURE struct ReserveContext
      56              : #define TALER_EXCHANGEDB_PURSE_DECISION_RESULT_CLOSURE struct ReserveContext
      57              : #define TALER_EXCHANGEDB_RECOUP_RESULT_CLOSURE struct ReserveContext
      58              : #define TALER_EXCHANGEDB_RESERVE_CLOSED_RESULT_CLOSURE struct ReserveContext
      59              : #define TALER_EXCHANGEDB_RESERVE_OPEN_RESULT_CLOSURE struct ReserveContext
      60              : #define TALER_EXCHANGEDB_WITHDRAW_RESULT_CLOSURE struct ReserveContext
      61              : #include "exchange-database/iterate_account_merges_above_serial_id.h"
      62              : #include "exchange-database/iterate_purse_decisions_above_serial_id.h"
      63              : #include "exchange-database/iterate_recoups_above_serial_id.h"
      64              : #include "exchange-database/iterate_reserve_closed_above_serial_id.h"
      65              : #include "exchange-database/iterate_reserve_open_above_serial_id.h"
      66              : #include "exchange-database/iterate_reserves_in_above_serial_id.h"
      67              : #include "exchange-database/iterate_withdrawals_above_serial_id.h"
      68              : 
      69              : /**
      70              :  * Use a 1 day grace period to deal with clocks not being perfectly synchronized.
      71              :  */
      72              : #define CLOSING_GRACE_PERIOD GNUNET_TIME_UNIT_DAYS
      73              : 
      74              : /**
      75              :  * Return value from main().
      76              :  */
      77              : static int global_ret;
      78              : 
      79              : /**
      80              :  * State of the last database transaction.
      81              :  */
      82              : static enum GNUNET_DB_QueryStatus global_qs;
      83              : 
      84              : /**
      85              :  * Run in test mode. Exit when idle instead of
      86              :  * going to sleep and waiting for more work.
      87              :  */
      88              : static int test_mode;
      89              : 
      90              : /**
      91              :  * After how long should idle reserves be closed?
      92              :  */
      93              : static struct GNUNET_TIME_Relative idle_reserve_expiration_time;
      94              : 
      95              : /**
      96              :  * Checkpointing our progress for reserves.
      97              :  */
      98              : static TALER_ARL_DEF_PP (reserves_reserve_in_serial_id);
      99              : static TALER_ARL_DEF_PP (reserves_withdraw_serial_id);
     100              : static TALER_ARL_DEF_PP (reserves_reserve_recoup_serial_id);
     101              : static TALER_ARL_DEF_PP (reserves_reserve_open_serial_id);
     102              : static TALER_ARL_DEF_PP (reserves_reserve_close_serial_id);
     103              : static TALER_ARL_DEF_PP (reserves_purse_decisions_serial_id);
     104              : static TALER_ARL_DEF_PP (reserves_account_merges_serial_id);
     105              : static TALER_ARL_DEF_PP (reserves_history_requests_serial_id);
     106              : 
     107              : /**
     108              :  * Tracked global reserve balances.
     109              :  */
     110              : static TALER_ARL_DEF_AB (reserves_reserve_total_balance);
     111              : static TALER_ARL_DEF_AB (reserves_reserve_loss);
     112              : static TALER_ARL_DEF_AB (reserves_withdraw_fee_revenue);
     113              : static TALER_ARL_DEF_AB (reserves_close_fee_revenue);
     114              : static TALER_ARL_DEF_AB (reserves_purse_fee_revenue);
     115              : static TALER_ARL_DEF_AB (reserves_open_fee_revenue);
     116              : static TALER_ARL_DEF_AB (reserves_history_fee_revenue);
     117              : 
     118              : /**
     119              :  * Total amount lost by operations for which signatures were invalid.
     120              :  */
     121              : static TALER_ARL_DEF_AB (reserves_total_bad_sig_loss);
     122              : 
     123              : /**
     124              :  * Total amount affected by reserves not having been closed on time.
     125              :  */
     126              : static TALER_ARL_DEF_AB (total_balance_reserve_not_closed);
     127              : 
     128              : /**
     129              :  * Total delta between expected and stored reserve balance summaries,
     130              :  * for positive deltas.  Used only when internal checks are
     131              :  * enabled.
     132              :  */
     133              : static TALER_ARL_DEF_AB (total_balance_summary_delta_plus);
     134              : 
     135              : /**
     136              :  * Total delta between expected and stored reserve balance summaries,
     137              :  * for negative deltas.  Used only when internal checks are
     138              :  * enabled.
     139              :  */
     140              : static TALER_ARL_DEF_AB (total_balance_summary_delta_minus);
     141              : 
     142              : /**
     143              :  * Profits the exchange made by bad amount calculations.
     144              :  */
     145              : static TALER_ARL_DEF_AB (reserves_total_arithmetic_delta_plus);
     146              : 
     147              : /**
     148              :  * Losses the exchange made by bad amount calculations.
     149              :  */
     150              : static TALER_ARL_DEF_AB (reserves_total_arithmetic_delta_minus);
     151              : 
     152              : /**
     153              :  * Should we run checks that only work for exchange-internal audits?
     154              :  */
     155              : static int internal_checks;
     156              : 
     157              : static struct GNUNET_DB_EventHandler *eh;
     158              : 
     159              : /**
     160              :  * The auditors's configuration.
     161              :  */
     162              : static const struct GNUNET_CONFIGURATION_Handle *cfg;
     163              : 
     164              : /* ***************************** Report logic **************************** */
     165              : 
     166              : 
     167              : /**
     168              :  * Report a (serious) inconsistency in the exchange's database with
     169              :  * respect to calculations involving amounts.
     170              :  *
     171              :  * @param operation what operation had the inconsistency
     172              :  * @param rowid affected row, 0 if row is missing
     173              :  * @param exchange amount calculated by exchange
     174              :  * @param auditor amount calculated by auditor
     175              :  * @param profitable 1 if @a exchange being larger than @a auditor is
     176              :  *           profitable for the exchange for this operation,
     177              :  *           -1 if @a exchange being smaller than @a auditor is
     178              :  *           profitable for the exchange, and 0 if it is unclear
     179              :  */
     180              : static void
     181            0 : report_amount_arithmetic_inconsistency (
     182              :   const char *operation,
     183              :   uint64_t rowid,
     184              :   const struct TALER_Amount *exchange,
     185              :   const struct TALER_Amount *auditor,
     186              :   int profitable)
     187              : {
     188              :   struct TALER_Amount delta;
     189              :   struct TALER_Amount *target;
     190              :   enum GNUNET_DB_QueryStatus qs;
     191              : 
     192            0 :   if (0 < TALER_amount_cmp (exchange,
     193              :                             auditor))
     194              :   {
     195              :     /* exchange > auditor */
     196            0 :     TALER_ARL_amount_subtract (&delta,
     197              :                                exchange,
     198              :                                auditor);
     199              :   }
     200              :   else
     201              :   {
     202              :     /* exchange <= auditor */
     203            0 :     profitable = -profitable;
     204            0 :     TALER_ARL_amount_subtract (&delta,
     205              :                                auditor,
     206              :                                exchange);
     207              :   }
     208              : 
     209              :   {
     210            0 :     struct TALER_AUDITORDB_AmountArithmeticInconsistency aai = {
     211              :       .problem_row_id = rowid,
     212            0 :       .profitable = profitable,
     213              :       .operation = (char *) operation,
     214              :       .exchange_amount = *exchange,
     215              :       .auditor_amount = *auditor,
     216              :     };
     217              : 
     218            0 :     qs = TALER_AUDITORDB_insert_amount_arithmetic_inconsistency (
     219              :       TALER_ARL_adb,
     220              :       &aai);
     221              : 
     222            0 :     if (qs < 0)
     223              :     {
     224            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     225            0 :       global_qs = qs;
     226            0 :       return;
     227              :     }
     228              :   }
     229              : 
     230            0 :   if (0 != profitable)
     231              :   {
     232            0 :     target = (1 == profitable)
     233              :       ? &TALER_ARL_USE_AB (reserves_total_arithmetic_delta_plus)
     234            0 :       : &TALER_ARL_USE_AB (reserves_total_arithmetic_delta_minus);
     235            0 :     TALER_ARL_amount_add (target,
     236              :                           target,
     237              :                           &delta);
     238              :   }
     239              : }
     240              : 
     241              : 
     242              : /**
     243              :  * Report a (serious) inconsistency in the exchange's database.
     244              :  *
     245              :  * @param table affected table
     246              :  * @param rowid affected row, 0 if row is missing
     247              :  * @param diagnostic message explaining the problem
     248              :  */
     249              : static void
     250            0 : report_row_inconsistency (const char *table,
     251              :                           uint64_t rowid,
     252              :                           const char *diagnostic)
     253              : {
     254              :   enum GNUNET_DB_QueryStatus qs;
     255            0 :   struct TALER_AUDITORDB_RowInconsistency ri = {
     256              :     .diagnostic = (char *) diagnostic,
     257              :     .row_table = (char *) table,
     258              :     .row_id = rowid
     259              :   };
     260              : 
     261            0 :   qs = TALER_AUDITORDB_insert_row_inconsistency (
     262              :     TALER_ARL_adb,
     263              :     &ri);
     264              : 
     265            0 :   if (qs < 0)
     266              :   {
     267            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     268            0 :     global_qs = qs;
     269            0 :     return;
     270              :   }
     271              : }
     272              : 
     273              : 
     274              : /* ***************************** Analyze reserves ************************ */
     275              : /* This logic checks the reserves_in, withdraw and reserves-tables */
     276              : 
     277              : /**
     278              :  * Summary data we keep per reserve.
     279              :  */
     280              : struct ReserveSummary
     281              : {
     282              :   /**
     283              :    * Public key of the reserve.
     284              :    * Always set when the struct is first initialized.
     285              :    */
     286              :   struct TALER_ReservePublicKeyP reserve_pub;
     287              : 
     288              :   /**
     289              :    * Sum of all incoming transfers during this transaction.
     290              :    * Updated only in #handle_reserve_in().
     291              :    */
     292              :   struct TALER_Amount total_in;
     293              : 
     294              :   /**
     295              :    * Sum of all outgoing transfers during this transaction (includes fees).
     296              :    * Updated only in #handle_withdrawals().
     297              :    */
     298              :   struct TALER_Amount total_out;
     299              : 
     300              :   /**
     301              :    * Sum of balance and fees encountered during this transaction.
     302              :    */
     303              :   struct TALER_AUDITORDB_ReserveFeeBalance curr_balance;
     304              : 
     305              :   /**
     306              :    * Previous balances of the reserve as remembered by the auditor.
     307              :    * (updated based on @e total_in and @e total_out at the end).
     308              :    */
     309              :   struct TALER_AUDITORDB_ReserveFeeBalance prev_balance;
     310              : 
     311              :   /**
     312              :    * Previous reserve expiration data, as remembered by the auditor.
     313              :    * (updated on-the-fly in #handle_reserve_in()).
     314              :    */
     315              :   struct GNUNET_TIME_Timestamp a_expiration_date;
     316              : 
     317              :   /**
     318              :    * Which account did originally put money into the reserve?
     319              :    */
     320              :   struct TALER_FullPayto sender_account;
     321              : 
     322              :   /**
     323              :    * Did we have a previous reserve info?  Used to decide between
     324              :    * UPDATE and INSERT later.  Initialized in
     325              :    * #load_auditor_reserve_summary() together with the a-* values
     326              :    * (if available).
     327              :    */
     328              :   bool had_ri;
     329              : 
     330              : };
     331              : 
     332              : 
     333              : /**
     334              :  * Load the auditor's remembered state about the reserve into @a rs.
     335              :  * The "total_in" and "total_out" amounts of @a rs must already be
     336              :  * initialized (so we can determine the currency).
     337              :  *
     338              :  * @param[in,out] rs reserve summary to (fully) initialize
     339              :  * @return transaction status code
     340              :  */
     341              : static enum GNUNET_DB_QueryStatus
     342            0 : load_auditor_reserve_summary (struct ReserveSummary *rs)
     343              : {
     344              :   enum GNUNET_DB_QueryStatus qs;
     345              :   uint64_t rowid;
     346              : 
     347            0 :   qs = TALER_AUDITORDB_get_reserve_info (TALER_ARL_adb,
     348            0 :                                          &rs->reserve_pub,
     349              :                                          &rowid,
     350              :                                          &rs->prev_balance,
     351              :                                          &rs->a_expiration_date,
     352              :                                          &rs->sender_account);
     353            0 :   if (0 > qs)
     354              :   {
     355            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     356            0 :     return qs;
     357              :   }
     358            0 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
     359              :   {
     360            0 :     rs->had_ri = false;
     361            0 :     GNUNET_assert (GNUNET_OK ==
     362              :                    TALER_amount_set_zero (rs->total_in.currency,
     363              :                                           &rs->prev_balance.reserve_balance));
     364            0 :     GNUNET_assert (GNUNET_OK ==
     365              :                    TALER_amount_set_zero (rs->total_in.currency,
     366              :                                           &rs->prev_balance.reserve_loss));
     367            0 :     GNUNET_assert (GNUNET_OK ==
     368              :                    TALER_amount_set_zero (rs->total_in.currency,
     369              :                                           &rs->prev_balance.withdraw_fee_balance
     370              :                                           ));
     371            0 :     GNUNET_assert (GNUNET_OK ==
     372              :                    TALER_amount_set_zero (rs->total_in.currency,
     373              :                                           &rs->prev_balance.close_fee_balance));
     374            0 :     GNUNET_assert (GNUNET_OK ==
     375              :                    TALER_amount_set_zero (rs->total_in.currency,
     376              :                                           &rs->prev_balance.purse_fee_balance));
     377            0 :     GNUNET_assert (GNUNET_OK ==
     378              :                    TALER_amount_set_zero (rs->total_in.currency,
     379              :                                           &rs->prev_balance.open_fee_balance));
     380            0 :     GNUNET_assert (GNUNET_OK ==
     381              :                    TALER_amount_set_zero (rs->total_in.currency,
     382              :                                           &rs->prev_balance.history_fee_balance)
     383              :                    );
     384            0 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     385              :                 "Creating fresh reserve `%s'\n",
     386              :                 TALER_B2S (&rs->reserve_pub));
     387            0 :     return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
     388              :   }
     389            0 :   rs->had_ri = true;
     390            0 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     391              :               "Auditor remembers reserve `%s' has balance %s\n",
     392              :               TALER_B2S (&rs->reserve_pub),
     393              :               TALER_amount2s (&rs->prev_balance.reserve_balance));
     394            0 :   return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
     395              : }
     396              : 
     397              : 
     398              : /**
     399              :  * Closure to the various callbacks we make while checking a reserve.
     400              :  */
     401              : struct ReserveContext
     402              : {
     403              :   /**
     404              :    * Map from hash of reserve's public key to a `struct ReserveSummary`.
     405              :    */
     406              :   struct GNUNET_CONTAINER_MultiHashMap *reserves;
     407              : 
     408              :   /**
     409              :    * Map from hash of denomination's public key to a
     410              :    * static string "revoked" for keys that have been revoked,
     411              :    * or "master signature invalid" in case the revocation is
     412              :    * there but bogus.
     413              :    */
     414              :   struct GNUNET_CONTAINER_MultiHashMap *revoked;
     415              : 
     416              :   /**
     417              :    * Transaction status code, set to error codes if applicable.
     418              :    */
     419              :   enum GNUNET_DB_QueryStatus qs;
     420              : 
     421              : };
     422              : 
     423              : 
     424              : /**
     425              :  * Create a new reserve for @a reserve_pub in @a rc.
     426              :  *
     427              :  * @param[in,out] rc context to update
     428              :  * @param reserve_pub key for which to create a reserve
     429              :  * @return NULL on error
     430              :  */
     431              : static struct ReserveSummary *
     432            0 : setup_reserve (struct ReserveContext *rc,
     433              :                const struct TALER_ReservePublicKeyP *reserve_pub)
     434              : {
     435              :   struct ReserveSummary *rs;
     436              :   struct GNUNET_HashCode key;
     437              :   enum GNUNET_DB_QueryStatus qs;
     438              : 
     439            0 :   GNUNET_CRYPTO_hash (reserve_pub,
     440              :                       sizeof (*reserve_pub),
     441              :                       &key);
     442            0 :   rs = GNUNET_CONTAINER_multihashmap_get (rc->reserves,
     443              :                                           &key);
     444            0 :   if (NULL != rs)
     445            0 :     return rs;
     446            0 :   rs = GNUNET_new (struct ReserveSummary);
     447            0 :   rs->reserve_pub = *reserve_pub;
     448            0 :   GNUNET_assert (GNUNET_OK ==
     449              :                  TALER_amount_set_zero (TALER_ARL_currency,
     450              :                                         &rs->total_in));
     451            0 :   GNUNET_assert (GNUNET_OK ==
     452              :                  TALER_amount_set_zero (TALER_ARL_currency,
     453              :                                         &rs->total_out));
     454            0 :   GNUNET_assert (GNUNET_OK ==
     455              :                  TALER_amount_set_zero (TALER_ARL_currency,
     456              :                                         &rs->curr_balance.reserve_balance));
     457            0 :   GNUNET_assert (GNUNET_OK ==
     458              :                  TALER_amount_set_zero (TALER_ARL_currency,
     459              :                                         &rs->curr_balance.reserve_loss));
     460            0 :   GNUNET_assert (GNUNET_OK ==
     461              :                  TALER_amount_set_zero (TALER_ARL_currency,
     462              :                                         &rs->curr_balance.withdraw_fee_balance))
     463              :   ;
     464            0 :   GNUNET_assert (GNUNET_OK ==
     465              :                  TALER_amount_set_zero (TALER_ARL_currency,
     466              :                                         &rs->curr_balance.close_fee_balance));
     467            0 :   GNUNET_assert (GNUNET_OK ==
     468              :                  TALER_amount_set_zero (TALER_ARL_currency,
     469              :                                         &rs->curr_balance.purse_fee_balance));
     470            0 :   GNUNET_assert (GNUNET_OK ==
     471              :                  TALER_amount_set_zero (TALER_ARL_currency,
     472              :                                         &rs->curr_balance.open_fee_balance));
     473            0 :   GNUNET_assert (GNUNET_OK ==
     474              :                  TALER_amount_set_zero (TALER_ARL_currency,
     475              :                                         &rs->curr_balance.history_fee_balance));
     476            0 :   if (0 > (qs = load_auditor_reserve_summary (rs)))
     477              :   {
     478            0 :     GNUNET_free (rs);
     479            0 :     rc->qs = qs;
     480            0 :     return NULL;
     481              :   }
     482            0 :   GNUNET_assert (GNUNET_OK ==
     483              :                  GNUNET_CONTAINER_multihashmap_put (rc->reserves,
     484              :                                                     &key,
     485              :                                                     rs,
     486              :                                                     GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)
     487              :                  );
     488            0 :   return rs;
     489              : }
     490              : 
     491              : 
     492              : /**
     493              :  * Function called with details about incoming wire transfers.
     494              :  *
     495              :  * @param rc our `struct ReserveContext`
     496              :  * @param rowid unique serial ID for the refresh session in our DB
     497              :  * @param reserve_pub public key of the reserve (also the WTID)
     498              :  * @param credit amount that was received
     499              :  * @param sender_account_details information about the sender's bank account
     500              :  * @param wire_reference unique reference identifying the wire transfer
     501              :  * @param execution_date when did we receive the funds
     502              :  * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
     503              :  */
     504              : static enum GNUNET_GenericReturnValue
     505            0 : handle_reserve_in (
     506              :   struct ReserveContext *rc,
     507              :   uint64_t rowid,
     508              :   const struct TALER_ReservePublicKeyP *reserve_pub,
     509              :   const struct TALER_Amount *credit,
     510              :   const struct TALER_FullPayto sender_account_details,
     511              :   uint64_t wire_reference,
     512              :   struct GNUNET_TIME_Timestamp execution_date)
     513              : {
     514              :   struct ReserveSummary *rs;
     515              :   struct GNUNET_TIME_Timestamp expiry;
     516              : 
     517              :   (void) wire_reference;
     518              :   /* should be monotonically increasing */
     519            0 :   GNUNET_assert (rowid >= TALER_ARL_USE_PP (reserves_reserve_in_serial_id));
     520            0 :   TALER_ARL_USE_PP (reserves_reserve_in_serial_id) = rowid + 1;
     521            0 :   rs = setup_reserve (rc,
     522              :                       reserve_pub);
     523            0 :   if (NULL == rs)
     524              :   {
     525            0 :     GNUNET_break (0);
     526            0 :     return GNUNET_SYSERR;
     527              :   }
     528            0 :   if (NULL == rs->sender_account.full_payto)
     529              :     rs->sender_account.full_payto
     530            0 :       = GNUNET_strdup (sender_account_details.full_payto);
     531            0 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     532              :               "Additional incoming wire transfer for reserve `%s' of %s\n",
     533              :               TALER_B2S (reserve_pub),
     534              :               TALER_amount2s (credit));
     535            0 :   expiry = GNUNET_TIME_absolute_to_timestamp (
     536              :     GNUNET_TIME_absolute_add (execution_date.abs_time,
     537              :                               idle_reserve_expiration_time));
     538            0 :   rs->a_expiration_date = GNUNET_TIME_timestamp_max (rs->a_expiration_date,
     539              :                                                      expiry);
     540            0 :   TALER_ARL_amount_add (&rs->total_in,
     541              :                         &rs->total_in,
     542              :                         credit);
     543            0 :   return GNUNET_OK;
     544              : }
     545              : 
     546              : 
     547              : /**
     548              :  * Function called with details about withdraw operations.  Verifies
     549              :  * the signature and updates the reserve's balance.
     550              :  *
     551              :  * @param rc our `struct ReserveContext`
     552              :  * @param rowid unique serial ID for the refresh session in our DB
     553              :  * @param num_denom_serials number of elements in @e denom_serials array
     554              :  * @param denom_serials array with length @e num_denom_serials of serial ID's of denominations in our DB
     555              :  * @param selected_h hash over the gamma-selected planchets
     556              :  * @param h_planchets running hash over all hashes of blinded planchets in the original withdraw request
     557              :  * @param blinding_seed the blinding seed for CS denominations that was provided during withdraw; might be NULL
     558              :  * @param age_proof_required true if the withdraw request required an age proof.
     559              :  * @param max_age if @e age_proof_required is true, the maximum age that was set on the coins.
     560              :  * @param noreveal_index if @e age_proof_required is true, the index that was returned by the exchange for the reveal phase.
     561              :  * @param reserve_pub public key of the reserve
     562              :  * @param reserve_sig signature over the withdraw operation
     563              :  * @param execution_date when did the wallet withdraw the coin
     564              :  * @param amount_with_fee amount that was withdrawn
     565              :  * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
     566              :  */
     567              : static enum GNUNET_GenericReturnValue
     568            0 : handle_withdrawals (
     569              :   struct ReserveContext *rc,
     570              :   uint64_t rowid,
     571              :   size_t num_denom_serials,
     572              :   const uint64_t *denom_serials,
     573              :   const struct TALER_HashBlindedPlanchetsP *selected_h,
     574              :   const struct TALER_HashBlindedPlanchetsP *h_planchets,
     575              :   const struct TALER_BlindingMasterSeedP *blinding_seed,
     576              :   bool age_proof_required,
     577              :   uint8_t max_age,
     578              :   uint8_t noreveal_index,
     579              :   const struct TALER_ReservePublicKeyP *reserve_pub,
     580              :   const struct TALER_ReserveSignatureP *reserve_sig,
     581              :   struct GNUNET_TIME_Timestamp execution_date,
     582              :   const struct TALER_Amount *amount_with_fee)
     583              : {
     584              :   struct ReserveSummary *rs;
     585              :   const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue;
     586              :   struct TALER_Amount auditor_amount;
     587              :   struct TALER_Amount auditor_fee;
     588              :   struct TALER_Amount auditor_amount_with_fee;
     589              :   enum GNUNET_DB_QueryStatus qs;
     590              : 
     591              :   /* should be monotonically increasing */
     592            0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     593              :               "Analyzing withdrawal row %llu\n",
     594              :               (unsigned long long) rowid);
     595            0 :   GNUNET_assert (rowid >= TALER_ARL_USE_PP (reserves_withdraw_serial_id));
     596            0 :   TALER_ARL_USE_PP (reserves_withdraw_serial_id) = rowid + 1;
     597              : 
     598            0 :   GNUNET_assert (GNUNET_OK ==
     599              :                  TALER_amount_set_zero (TALER_ARL_currency,
     600              :                                         &auditor_amount));
     601            0 :   GNUNET_assert (GNUNET_OK ==
     602              :                  TALER_amount_set_zero (TALER_ARL_currency,
     603              :                                         &auditor_fee));
     604            0 :   GNUNET_assert (GNUNET_OK ==
     605              :                  TALER_amount_set_zero (TALER_ARL_currency,
     606              :                                         &auditor_amount_with_fee));
     607            0 :   rs = setup_reserve (rc,
     608              :                       reserve_pub);
     609            0 :   if (NULL == rs)
     610              :   {
     611            0 :     GNUNET_break (0);
     612            0 :     return GNUNET_SYSERR;
     613              :   }
     614            0 :   for (size_t i = 0; i < num_denom_serials; i++)
     615              :   {
     616              :     /* lookup denomination pub data (make sure denom_pub is valid, establish fees);
     617              :        initializes wsrd.h_denomination_pub! */
     618            0 :     qs = TALER_ARL_get_denomination_info_by_serial (denom_serials[i],
     619              :                                                     &issue);
     620            0 :     if (0 > qs)
     621              :     {
     622            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     623            0 :       if (GNUNET_DB_STATUS_HARD_ERROR == qs)
     624            0 :         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     625              :                     "Hard database error trying to get denomination by serial %llu (%s) from database!\n",
     626              :                     (unsigned long long) denom_serials[i],
     627              :                     GNUNET_h2s (&h_planchets->hash));
     628            0 :       rc->qs = qs;
     629            0 :       return GNUNET_SYSERR;
     630              :     }
     631            0 :     if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
     632              :     {
     633            0 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     634              :                   "Denomination #%llu not found\n",
     635              :                   (unsigned long long) denom_serials[i]);
     636            0 :       report_row_inconsistency ("withdraw",
     637              :                                 rowid,
     638              :                                 "denomination key not found");
     639            0 :       if (global_qs < 0)
     640            0 :         return GNUNET_SYSERR;
     641            0 :       return GNUNET_OK;
     642              :     }
     643            0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     644              :                 "Analyzing withdrawn denomination #%llu (%s)\n",
     645              :                 (unsigned long long) denom_serials[i],
     646              :                 TALER_amount2s (&issue->value));
     647              : 
     648              :     /* check that execution date is within withdraw range for denom_pub  */
     649            0 :     GNUNET_log (
     650              :       GNUNET_ERROR_TYPE_DEBUG,
     651              :       "Checking withdraw timing: %llu, expire: %llu, timing: %llu\n",
     652              :       (unsigned long long) issue->start.abs_time.abs_value_us,
     653              :       (unsigned long long) issue->expire_withdraw.abs_time.abs_value_us,
     654              :       (unsigned long long) execution_date.abs_time.abs_value_us);
     655            0 :     if (GNUNET_TIME_timestamp_cmp (issue->start,
     656              :                                    >,
     657            0 :                                    execution_date) ||
     658            0 :         GNUNET_TIME_timestamp_cmp (issue->expire_withdraw,
     659              :                                    <,
     660              :                                    execution_date))
     661              :     {
     662              :       struct TALER_AUDITORDB_DenominationKeyValidityWithdrawInconsistency
     663            0 :         dkvwi = {
     664              :         .problem_row_id = rowid,
     665              :         .execution_date = execution_date.abs_time,
     666            0 :         .denompub_h = issue->denom_hash,
     667              :         .reserve_pub = *reserve_pub
     668              :       };
     669              : 
     670            0 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     671              :                   "Withdraw outside of denomination #%llu validity period detected\n",
     672              :                   (unsigned long long) denom_serials[i]);
     673              :       qs =
     674            0 :         TALER_AUDITORDB_insert_denomination_key_validity_withdraw_inconsistency
     675              :         (
     676              :           TALER_ARL_adb,
     677              :           &dkvwi);
     678              : 
     679            0 :       if (qs < 0)
     680              :       {
     681            0 :         GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     682            0 :         rc->qs = qs;
     683            0 :         return GNUNET_SYSERR;
     684              :       }
     685              :     }
     686            0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     687              :                 "Adding withdraw fee of denomination (%s)\n",
     688              :                 TALER_amount2s (&issue->fees.withdraw));
     689            0 :     TALER_ARL_amount_add (&auditor_amount,
     690              :                           &auditor_amount,
     691              :                           &issue->value);
     692            0 :     TALER_ARL_amount_add (&auditor_fee,
     693              :                           &auditor_fee,
     694              :                           &issue->fees.withdraw);
     695              :     {
     696              :       struct TALER_Amount issue_amount_with_fee;
     697              : 
     698            0 :       TALER_ARL_amount_add (&issue_amount_with_fee,
     699              :                             &issue->value,
     700              :                             &issue->fees.withdraw);
     701            0 :       TALER_ARL_amount_add (&auditor_amount_with_fee,
     702              :                             &auditor_amount_with_fee,
     703              :                             &issue_amount_with_fee);
     704              :     }
     705            0 :     TALER_ARL_amount_add (&rs->curr_balance.withdraw_fee_balance,
     706              :                           &rs->curr_balance.withdraw_fee_balance,
     707              :                           &issue->fees.withdraw);
     708            0 :     TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_withdraw_fee_revenue),
     709              :                           &TALER_ARL_USE_AB (reserves_withdraw_fee_revenue),
     710              :                           &issue->fees.withdraw);
     711              :   } /* end for all coins in batch */
     712              : 
     713              :   /* check reserve_sig (first: setup remaining members of wsrd) */
     714            0 :   if (GNUNET_OK !=
     715            0 :       TALER_wallet_withdraw_verify (
     716              :         &auditor_amount,
     717              :         &auditor_fee,
     718              :         h_planchets,
     719              :         blinding_seed,
     720              :         age_proof_required
     721            0 :         ? &issue->age_mask
     722              :         : NULL,
     723              :         age_proof_required
     724              :         ? max_age
     725              :         : 0,
     726              :         reserve_pub,
     727              :         reserve_sig))
     728              :   {
     729            0 :     struct TALER_AUDITORDB_BadSigLosses bsl = {
     730              :       .problem_row_id = rowid,
     731              :       .operation = (char *) "withdraw",
     732              :       .loss = *amount_with_fee,
     733              :       .operation_specific_pub = reserve_pub->eddsa_pub
     734              :     };
     735              : 
     736            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     737              :                 "Withdraw signature invalid (row #%llu)\n",
     738              :                 (unsigned long long) rowid);
     739            0 :     qs = TALER_AUDITORDB_insert_bad_sig_losses (
     740              :       TALER_ARL_adb,
     741              :       &bsl);
     742            0 :     if (qs < 0)
     743              :     {
     744            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     745            0 :       rc->qs = qs;
     746            0 :       return GNUNET_SYSERR;
     747              :     }
     748            0 :     TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_total_bad_sig_loss),
     749              :                           &TALER_ARL_USE_AB (reserves_total_bad_sig_loss),
     750              :                           amount_with_fee);
     751            0 :     return GNUNET_OK;     /* exit function here, we cannot add this to the legitimate withdrawals */
     752              :   }
     753              : 
     754            0 :   if (0 !=
     755            0 :       TALER_amount_cmp (&auditor_amount_with_fee,
     756              :                         amount_with_fee))
     757              :   {
     758            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     759              :                 "Withdraw fee inconsistent (row #%llu)\n",
     760              :                 (unsigned long long) rowid);
     761            0 :     report_row_inconsistency ("withdraw",
     762              :                               rowid,
     763              :                               "amount with fee from exchange does not match denomination value plus fee");
     764            0 :     if (global_qs < 0)
     765              :     {
     766            0 :       GNUNET_break (0);
     767            0 :       return GNUNET_SYSERR;
     768              :     }
     769              :   }
     770            0 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     771              :               "Reserve `%s' reduced by %s from withdraw\n",
     772              :               TALER_B2S (reserve_pub),
     773              :               TALER_amount2s (&auditor_amount_with_fee));
     774            0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     775              :               "Increasing withdraw profits by fee %s\n",
     776              :               TALER_amount2s (&issue->fees.withdraw));
     777            0 :   TALER_ARL_amount_add (&rs->total_out,
     778              :                         &rs->total_out,
     779              :                         &auditor_amount_with_fee);
     780            0 :   return GNUNET_OK;
     781              : }
     782              : 
     783              : 
     784              : /**
     785              :  * Function called with details about withdraw operations.  Verifies
     786              :  * the signature and updates the reserve's balance.
     787              :  *
     788              :  * @param rc our `struct ReserveContext`
     789              :  * @param rowid unique serial ID for the refresh session in our DB
     790              :  * @param timestamp when did we receive the recoup request
     791              :  * @param amount how much should be added back to the reserve
     792              :  * @param reserve_pub public key of the reserve
     793              :  * @param coin public information about the coin, denomination signature is
     794              :  *        already verified in #check_recoup()
     795              :  * @param denom_pub public key of the denomionation of @a coin
     796              :  * @param coin_sig signature with @e coin_pub of type #TALER_SIGNATURE_WALLET_COIN_RECOUP
     797              :  * @param coin_blind blinding factor used to blind the coin
     798              :  * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
     799              :  */
     800              : static enum GNUNET_GenericReturnValue
     801            0 : handle_recoup_by_reserve (
     802              :   struct ReserveContext *rc,
     803              :   uint64_t rowid,
     804              :   struct GNUNET_TIME_Timestamp timestamp,
     805              :   const struct TALER_Amount *amount,
     806              :   const struct TALER_ReservePublicKeyP *reserve_pub,
     807              :   const struct TALER_CoinPublicInfo *coin,
     808              :   const struct TALER_DenominationPublicKey *denom_pub,
     809              :   const struct TALER_CoinSpendSignatureP *coin_sig,
     810              :   const union GNUNET_CRYPTO_BlindingSecretP *coin_blind)
     811              : {
     812              :   struct ReserveSummary *rs;
     813              :   struct GNUNET_TIME_Timestamp expiry;
     814              :   struct TALER_MasterSignatureP msig;
     815              :   uint64_t rev_rowid;
     816              :   enum GNUNET_DB_QueryStatus qs;
     817              :   const char *rev;
     818              : 
     819              :   (void) denom_pub;
     820              :   /* should be monotonically increasing */
     821            0 :   GNUNET_assert (rowid >= TALER_ARL_USE_PP (reserves_reserve_recoup_serial_id));
     822            0 :   TALER_ARL_USE_PP (reserves_reserve_recoup_serial_id) = rowid + 1;
     823              :   /* We know that denom_pub matches denom_pub_hash because this
     824              :      is how the SQL statement joined the tables. */
     825            0 :   if (GNUNET_OK !=
     826            0 :       TALER_wallet_recoup_verify (&coin->denom_pub_hash,
     827              :                                   coin_blind,
     828              :                                   &coin->coin_pub,
     829              :                                   coin_sig))
     830              :   {
     831            0 :     struct TALER_AUDITORDB_BadSigLosses bslr = {
     832              :       .problem_row_id = rowid,
     833              :       .operation = (char *) "recoup",
     834              :       .loss = *amount,
     835              :       .operation_specific_pub = coin->coin_pub.eddsa_pub
     836              :     };
     837              : 
     838            0 :     qs = TALER_AUDITORDB_insert_bad_sig_losses (
     839              :       TALER_ARL_adb,
     840              :       &bslr);
     841              : 
     842            0 :     if (qs < 0)
     843              :     {
     844            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     845            0 :       rc->qs = qs;
     846            0 :       return GNUNET_SYSERR;
     847              :     }
     848            0 :     TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_total_bad_sig_loss),
     849              :                           &TALER_ARL_USE_AB (reserves_total_bad_sig_loss),
     850              :                           amount);
     851              :     /* We continue here intentionally to credit the reserve anyway,
     852              :        we already accounted for the loss, so we need to continue to
     853              :        avoid getting another error when the reserve is drained which
     854              :        would double-count the damage. */
     855              :   }
     856              : 
     857              :   /* check that the coin was eligible for recoup!*/
     858            0 :   rev = GNUNET_CONTAINER_multihashmap_get (rc->revoked,
     859              :                                            &coin->denom_pub_hash.hash);
     860            0 :   if (NULL == rev)
     861              :   {
     862            0 :     qs = TALER_EXCHANGEDB_get_denomination_revocation (TALER_ARL_edb,
     863              :                                                        &coin->denom_pub_hash,
     864              :                                                        &msig,
     865              :                                                        &rev_rowid);
     866            0 :     if (0 > qs)
     867              :     {
     868            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     869            0 :       rc->qs = qs;
     870            0 :       return GNUNET_SYSERR;
     871              :     }
     872            0 :     if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
     873              :     {
     874            0 :       report_row_inconsistency ("recoup",
     875              :                                 rowid,
     876              :                                 "denomination key not in revocation set");
     877            0 :       if (global_qs < 0)
     878            0 :         return GNUNET_SYSERR;
     879            0 :       TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_reserve_loss),
     880              :                             &TALER_ARL_USE_AB (reserves_reserve_loss),
     881              :                             amount);
     882              :     }
     883              :     else
     884              :     {
     885            0 :       if (GNUNET_OK !=
     886            0 :           TALER_exchange_offline_denomination_revoke_verify (
     887              :             &coin->denom_pub_hash,
     888              :             &TALER_ARL_master_pub,
     889              :             &msig))
     890              :       {
     891            0 :         rev = "master signature invalid";
     892              :       }
     893              :       else
     894              :       {
     895            0 :         rev = "revoked";
     896              :       }
     897            0 :       GNUNET_assert (
     898              :         GNUNET_OK ==
     899              :         GNUNET_CONTAINER_multihashmap_put (
     900              :           rc->revoked,
     901              :           &coin->denom_pub_hash.hash,
     902              :           (void *) rev,
     903              :           GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
     904              :     }
     905              :   }
     906              :   else
     907              :   {
     908            0 :     rev_rowid = 0;   /* reported elsewhere */
     909              :   }
     910            0 :   if ((NULL != rev) &&
     911            0 :       (0 == strcmp (rev,
     912              :                     "master signature invalid")))
     913              :   {
     914            0 :     struct TALER_AUDITORDB_BadSigLosses bslrm = {
     915              :       .problem_row_id = rev_rowid,
     916              :       .operation = (char *) "recoup-master",
     917              :       .loss = *amount,
     918              :       .operation_specific_pub = TALER_ARL_master_pub.eddsa_pub
     919              :     };
     920              : 
     921            0 :     qs = TALER_AUDITORDB_insert_bad_sig_losses (
     922              :       TALER_ARL_adb,
     923              :       &bslrm);
     924              : 
     925            0 :     if (qs < 0)
     926              :     {
     927            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     928            0 :       rc->qs = qs;
     929            0 :       return GNUNET_SYSERR;
     930              :     }
     931            0 :     TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_total_bad_sig_loss),
     932              :                           &TALER_ARL_USE_AB (reserves_total_bad_sig_loss),
     933              :                           amount);
     934              :   }
     935              : 
     936            0 :   rs = setup_reserve (rc,
     937              :                       reserve_pub);
     938            0 :   if (NULL == rs)
     939              :   {
     940            0 :     GNUNET_break (0);
     941            0 :     return GNUNET_SYSERR;
     942              :   }
     943            0 :   TALER_ARL_amount_add (&rs->total_in,
     944              :                         &rs->total_in,
     945              :                         amount);
     946            0 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     947              :               "Additional /recoup value to for reserve `%s' of %s\n",
     948              :               TALER_B2S (reserve_pub),
     949              :               TALER_amount2s (amount));
     950            0 :   expiry = GNUNET_TIME_absolute_to_timestamp (
     951              :     GNUNET_TIME_absolute_add (timestamp.abs_time,
     952              :                               idle_reserve_expiration_time));
     953            0 :   rs->a_expiration_date = GNUNET_TIME_timestamp_max (rs->a_expiration_date,
     954              :                                                      expiry);
     955            0 :   return GNUNET_OK;
     956              : }
     957              : 
     958              : 
     959              : /**
     960              :  * Obtain the closing fee for a transfer at @a time for target
     961              :  * @a receiver_account.
     962              :  *
     963              :  * @param receiver_account payto:// URI of the target account
     964              :  * @param atime when was the transfer made
     965              :  * @param[out] fee set to the closing fee
     966              :  * @return #GNUNET_OK on success
     967              :  */
     968              : static enum GNUNET_GenericReturnValue
     969            0 : get_closing_fee (const struct TALER_FullPayto receiver_account,
     970              :                  struct GNUNET_TIME_Timestamp atime,
     971              :                  struct TALER_Amount *fee)
     972              : {
     973              :   struct TALER_MasterSignatureP master_sig;
     974              :   struct GNUNET_TIME_Timestamp start_date;
     975              :   struct GNUNET_TIME_Timestamp end_date;
     976              :   struct TALER_WireFeeSet fees;
     977              :   char *method;
     978              :   uint64_t rowid;
     979              : 
     980            0 :   method = TALER_payto_get_method (receiver_account.full_payto);
     981            0 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     982              :               "Method is `%s'\n",
     983              :               method);
     984            0 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     985            0 :       TALER_EXCHANGEDB_get_wire_fee (TALER_ARL_edb,
     986              :                                      method,
     987              :                                      atime,
     988              :                                      &rowid,
     989              :                                      &start_date,
     990              :                                      &end_date,
     991              :                                      &fees,
     992              :                                      &master_sig))
     993              :   {
     994              :     char *diag;
     995              : 
     996            0 :     GNUNET_asprintf (&diag,
     997              :                      "closing fee for `%s' unavailable at %s\n",
     998              :                      method,
     999              :                      GNUNET_TIME_timestamp2s (atime));
    1000            0 :     report_row_inconsistency ("closing-fee",
    1001              :                               rowid,
    1002              :                               diag);
    1003            0 :     GNUNET_free (diag);
    1004            0 :     GNUNET_free (method);
    1005            0 :     return GNUNET_SYSERR;
    1006              :   }
    1007            0 :   *fee = fees.closing;
    1008            0 :   GNUNET_free (method);
    1009            0 :   return GNUNET_OK;
    1010              : }
    1011              : 
    1012              : 
    1013              : /**
    1014              :  * Function called about reserve opening operations.
    1015              :  *
    1016              :  * @param rc closure
    1017              :  * @param rowid row identifier used to uniquely identify the reserve closing operation
    1018              :  * @param reserve_payment how much to pay from the
    1019              :  *        reserve's own balance for opening the reserve
    1020              :  * @param request_timestamp when was the request created
    1021              :  * @param reserve_expiration desired expiration time for the reserve
    1022              :  * @param purse_limit minimum number of purses the client
    1023              :  *       wants to have concurrently open for this reserve
    1024              :  * @param reserve_pub public key of the reserve
    1025              :  * @param reserve_sig signature affirming the operation
    1026              :  * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
    1027              :  */
    1028              : static enum GNUNET_GenericReturnValue
    1029            0 : handle_reserve_open (
    1030              :   struct ReserveContext *rc,
    1031              :   uint64_t rowid,
    1032              :   const struct TALER_Amount *reserve_payment,
    1033              :   struct GNUNET_TIME_Timestamp request_timestamp,
    1034              :   struct GNUNET_TIME_Timestamp reserve_expiration,
    1035              :   uint32_t purse_limit,
    1036              :   const struct TALER_ReservePublicKeyP *reserve_pub,
    1037              :   const struct TALER_ReserveSignatureP *reserve_sig)
    1038              : {
    1039              :   struct ReserveSummary *rs;
    1040              :   enum GNUNET_DB_QueryStatus qs;
    1041              : 
    1042              :   /* should be monotonically increasing */
    1043            0 :   GNUNET_assert (rowid >= TALER_ARL_USE_PP (reserves_reserve_open_serial_id));
    1044            0 :   TALER_ARL_USE_PP (reserves_reserve_open_serial_id) = rowid + 1;
    1045              : 
    1046            0 :   rs = setup_reserve (rc,
    1047              :                       reserve_pub);
    1048            0 :   if (NULL == rs)
    1049              :   {
    1050            0 :     GNUNET_break (0);
    1051            0 :     return GNUNET_SYSERR;
    1052              :   }
    1053            0 :   if (GNUNET_OK !=
    1054            0 :       TALER_wallet_reserve_open_verify (reserve_payment,
    1055              :                                         request_timestamp,
    1056              :                                         reserve_expiration,
    1057              :                                         purse_limit,
    1058              :                                         reserve_pub,
    1059              :                                         reserve_sig))
    1060              :   {
    1061            0 :     struct TALER_AUDITORDB_BadSigLosses bsl = {
    1062              :       .problem_row_id = rowid,
    1063              :       .operation = (char *) "reserve-open",
    1064              :       .loss = *reserve_payment,
    1065              :       .operation_specific_pub = reserve_pub->eddsa_pub
    1066              :     };
    1067              : 
    1068            0 :     qs = TALER_AUDITORDB_insert_bad_sig_losses (
    1069              :       TALER_ARL_adb,
    1070              :       &bsl);
    1071              : 
    1072            0 :     if (qs < 0)
    1073              :     {
    1074            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1075            0 :       rc->qs = qs;
    1076            0 :       return GNUNET_SYSERR;
    1077              :     }
    1078            0 :     TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_total_bad_sig_loss),
    1079              :                           &TALER_ARL_USE_AB (reserves_total_bad_sig_loss),
    1080              :                           reserve_payment);
    1081            0 :     return GNUNET_OK;
    1082              :   }
    1083            0 :   TALER_ARL_amount_add (&rs->curr_balance.open_fee_balance,
    1084              :                         &rs->curr_balance.open_fee_balance,
    1085              :                         reserve_payment);
    1086            0 :   TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_open_fee_revenue),
    1087              :                         &TALER_ARL_USE_AB (reserves_open_fee_revenue),
    1088              :                         reserve_payment);
    1089            0 :   TALER_ARL_amount_add (&rs->total_out,
    1090              :                         &rs->total_out,
    1091              :                         reserve_payment);
    1092            0 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1093              :               "Additional open operation for reserve `%s' of %s\n",
    1094              :               TALER_B2S (reserve_pub),
    1095              :               TALER_amount2s (reserve_payment));
    1096            0 :   return GNUNET_OK;
    1097              : }
    1098              : 
    1099              : 
    1100              : /**
    1101              :  * Function called about reserve closing operations
    1102              :  * the aggregator triggered.
    1103              :  *
    1104              :  * @param rc closure
    1105              :  * @param rowid row identifier used to uniquely identify the reserve closing operation
    1106              :  * @param execution_date when did we execute the close operation
    1107              :  * @param amount_with_fee how much did we debit the reserve
    1108              :  * @param closing_fee how much did we charge for closing the reserve
    1109              :  * @param reserve_pub public key of the reserve
    1110              :  * @param receiver_account where did we send the funds
    1111              :  * @param transfer_details details about the wire transfer
    1112              :  * @param close_request_row which close request triggered the operation?
    1113              :  *         0 if it was a timeout
    1114              :  * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
    1115              :  */
    1116              : static enum GNUNET_GenericReturnValue
    1117            0 : handle_reserve_closed (
    1118              :   struct ReserveContext *rc,
    1119              :   uint64_t rowid,
    1120              :   struct GNUNET_TIME_Timestamp execution_date,
    1121              :   const struct TALER_Amount *amount_with_fee,
    1122              :   const struct TALER_Amount *closing_fee,
    1123              :   const struct TALER_ReservePublicKeyP *reserve_pub,
    1124              :   const struct TALER_FullPayto receiver_account,
    1125              :   const struct TALER_WireTransferIdentifierRawP *transfer_details,
    1126              :   uint64_t close_request_row)
    1127              : {
    1128              :   struct ReserveSummary *rs;
    1129              : 
    1130              :   (void) transfer_details;
    1131              :   /* should be monotonically increasing */
    1132            0 :   GNUNET_assert (rowid >= TALER_ARL_USE_PP (reserves_reserve_close_serial_id));
    1133            0 :   TALER_ARL_USE_PP (reserves_reserve_close_serial_id) = rowid + 1;
    1134              : 
    1135            0 :   rs = setup_reserve (rc,
    1136              :                       reserve_pub);
    1137            0 :   if (NULL == rs)
    1138              :   {
    1139            0 :     GNUNET_break (0);
    1140            0 :     return GNUNET_SYSERR;
    1141              :   }
    1142              :   {
    1143              :     struct TALER_Amount expected_fee;
    1144              : 
    1145              :     /* verify closing_fee is correct! */
    1146            0 :     if (GNUNET_OK !=
    1147            0 :         get_closing_fee (receiver_account,
    1148              :                          execution_date,
    1149              :                          &expected_fee))
    1150              :     {
    1151            0 :       GNUNET_break (0);
    1152              :     }
    1153            0 :     else if (0 != TALER_amount_cmp (&expected_fee,
    1154              :                                     closing_fee))
    1155              :     {
    1156            0 :       report_amount_arithmetic_inconsistency (
    1157              :         "closing aggregation fee",
    1158              :         rowid,
    1159              :         closing_fee,
    1160              :         &expected_fee,
    1161              :         1);
    1162            0 :       if (global_qs < 0)
    1163            0 :         return GNUNET_SYSERR;
    1164              :     }
    1165              :   }
    1166              : 
    1167            0 :   TALER_ARL_amount_add (&rs->curr_balance.close_fee_balance,
    1168              :                         &rs->curr_balance.close_fee_balance,
    1169              :                         closing_fee);
    1170            0 :   TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_close_fee_revenue),
    1171              :                         &TALER_ARL_USE_AB (reserves_close_fee_revenue),
    1172              :                         closing_fee);
    1173            0 :   TALER_ARL_amount_add (&rs->total_out,
    1174              :                         &rs->total_out,
    1175              :                         amount_with_fee);
    1176            0 :   if (0 != close_request_row)
    1177              :   {
    1178              :     struct TALER_ReserveSignatureP reserve_sig;
    1179              :     struct GNUNET_TIME_Timestamp request_timestamp;
    1180              :     struct TALER_Amount close_balance;
    1181              :     struct TALER_Amount close_fee;
    1182            0 :     struct TALER_FullPayto payto_uri = { NULL };
    1183              :     enum GNUNET_DB_QueryStatus qs;
    1184              : 
    1185            0 :     qs = TALER_EXCHANGEDB_get_reserve_close_request_info (
    1186              :       TALER_ARL_edb,
    1187              :       reserve_pub,
    1188              :       close_request_row,
    1189              :       &reserve_sig,
    1190              :       &request_timestamp,
    1191              :       &close_balance,
    1192              :       &close_fee,
    1193              :       &payto_uri);
    1194            0 :     if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
    1195              :     {
    1196            0 :       report_row_inconsistency ("reserves_close",
    1197              :                                 rowid,
    1198              :                                 "reserve close request unknown");
    1199            0 :       if (global_qs < 0)
    1200            0 :         return GNUNET_SYSERR;
    1201              :     }
    1202              :     else
    1203              :     {
    1204              :       struct TALER_FullPaytoHashP h_payto;
    1205              : 
    1206            0 :       TALER_full_payto_hash (payto_uri,
    1207              :                              &h_payto);
    1208            0 :       if (GNUNET_OK !=
    1209            0 :           TALER_wallet_reserve_close_verify (
    1210              :             request_timestamp,
    1211              :             &h_payto,
    1212              :             reserve_pub,
    1213              :             &reserve_sig))
    1214              :       {
    1215            0 :         struct TALER_AUDITORDB_BadSigLosses bsl = {
    1216              :           .problem_row_id = close_request_row,
    1217              :           .operation = (char *) "close-request",
    1218              :           .loss = *amount_with_fee,
    1219              :           .operation_specific_pub = reserve_pub->eddsa_pub
    1220              :         };
    1221              : 
    1222            0 :         qs = TALER_AUDITORDB_insert_bad_sig_losses (
    1223              :           TALER_ARL_adb,
    1224              :           &bsl);
    1225              : 
    1226            0 :         if (qs < 0)
    1227              :         {
    1228            0 :           GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1229            0 :           rc->qs = qs;
    1230            0 :           GNUNET_free (payto_uri.full_payto);
    1231            0 :           return GNUNET_SYSERR;
    1232              :         }
    1233            0 :         TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_total_bad_sig_loss),
    1234              :                               &TALER_ARL_USE_AB (reserves_total_bad_sig_loss),
    1235              :                               amount_with_fee);
    1236              :       }
    1237              :     }
    1238            0 :     if ( (NULL == payto_uri.full_payto) &&
    1239            0 :          (NULL == rs->sender_account.full_payto) )
    1240              :     {
    1241            0 :       GNUNET_break (! rs->had_ri);
    1242            0 :       report_row_inconsistency ("reserves_close",
    1243              :                                 rowid,
    1244              :                                 "target account not verified, auditor does not know reserve");
    1245            0 :       if (global_qs < 0)
    1246            0 :         return GNUNET_SYSERR;
    1247              :     }
    1248            0 :     if (NULL == payto_uri.full_payto)
    1249              :     {
    1250            0 :       if ((NULL == rs->sender_account.full_payto) ||
    1251            0 :           (0 != TALER_full_payto_cmp (rs->sender_account,
    1252              :                                       receiver_account)))
    1253              :       {
    1254            0 :         report_row_inconsistency ("reserves_close",
    1255              :                                   rowid,
    1256              :                                   "target account does not match origin account");
    1257            0 :         if (global_qs < 0)
    1258            0 :           return GNUNET_SYSERR;
    1259              :       }
    1260              :     }
    1261              :     else
    1262              :     {
    1263            0 :       if (0 != TALER_full_payto_cmp (payto_uri,
    1264              :                                      receiver_account))
    1265              :       {
    1266            0 :         report_row_inconsistency ("reserves_close",
    1267              :                                   rowid,
    1268              :                                   "target account does not match origin account");
    1269            0 :         if (global_qs < 0)
    1270              :         {
    1271            0 :           GNUNET_free (payto_uri.full_payto);
    1272            0 :           return GNUNET_SYSERR;
    1273              :         }
    1274              :       }
    1275              :     }
    1276            0 :     GNUNET_free (payto_uri.full_payto);
    1277              :   }
    1278              :   else
    1279              :   {
    1280            0 :     if (NULL == rs->sender_account.full_payto)
    1281              :     {
    1282            0 :       GNUNET_break (! rs->had_ri);
    1283            0 :       report_row_inconsistency ("reserves_close",
    1284              :                                 rowid,
    1285              :                                 "target account not verified, auditor does not know reserve");
    1286            0 :       if (global_qs < 0)
    1287            0 :         return GNUNET_SYSERR;
    1288              :     }
    1289            0 :     else if (0 != TALER_full_payto_cmp (rs->sender_account,
    1290              :                                         receiver_account))
    1291              :     {
    1292            0 :       report_row_inconsistency ("reserves_close",
    1293              :                                 rowid,
    1294              :                                 "target account does not match origin account");
    1295            0 :       if (global_qs < 0)
    1296            0 :         return GNUNET_SYSERR;
    1297              :     }
    1298              :   }
    1299              : 
    1300            0 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1301              :               "Additional closing operation for reserve `%s' of %s\n",
    1302              :               TALER_B2S (reserve_pub),
    1303              :               TALER_amount2s (amount_with_fee));
    1304            0 :   return GNUNET_OK;
    1305              : }
    1306              : 
    1307              : 
    1308              : /**
    1309              :  * Function called with details about account merge requests that have been
    1310              :  * made, with the goal of accounting for the merge fee paid by the reserve (if
    1311              :  * applicable).
    1312              :  *
    1313              :  * @param rc closure
    1314              :  * @param rowid unique serial ID for the deposit in our DB
    1315              :  * @param reserve_pub reserve affected by the merge
    1316              :  * @param purse_pub purse being merged
    1317              :  * @param h_contract_terms hash over contract of the purse
    1318              :  * @param purse_expiration when would the purse expire
    1319              :  * @param amount total amount in the purse
    1320              :  * @param min_age minimum age of all coins deposited into the purse
    1321              :  * @param flags how was the purse created
    1322              :  * @param purse_fee if a purse fee was paid, how high is it
    1323              :  * @param merge_timestamp when was the merge approved
    1324              :  * @param reserve_sig signature by reserve approving the merge
    1325              :  * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
    1326              :  */
    1327              : static enum GNUNET_GenericReturnValue
    1328            0 : handle_account_merged (
    1329              :   struct ReserveContext *rc,
    1330              :   uint64_t rowid,
    1331              :   const struct TALER_ReservePublicKeyP *reserve_pub,
    1332              :   const struct TALER_PurseContractPublicKeyP *purse_pub,
    1333              :   const struct TALER_PrivateContractHashP *h_contract_terms,
    1334              :   struct GNUNET_TIME_Timestamp purse_expiration,
    1335              :   const struct TALER_Amount *amount,
    1336              :   uint32_t min_age,
    1337              :   enum TALER_WalletAccountMergeFlags flags,
    1338              :   const struct TALER_Amount *purse_fee,
    1339              :   struct GNUNET_TIME_Timestamp merge_timestamp,
    1340              :   const struct TALER_ReserveSignatureP *reserve_sig)
    1341              : {
    1342              :   struct ReserveSummary *rs;
    1343              :   enum GNUNET_DB_QueryStatus qs;
    1344              : 
    1345              :   /* should be monotonically increasing */
    1346            0 :   GNUNET_assert (rowid >= TALER_ARL_USE_PP (reserves_account_merges_serial_id));
    1347            0 :   TALER_ARL_USE_PP (reserves_account_merges_serial_id) = rowid + 1;
    1348            0 :   if (GNUNET_OK !=
    1349            0 :       TALER_wallet_account_merge_verify (merge_timestamp,
    1350              :                                          purse_pub,
    1351              :                                          purse_expiration,
    1352              :                                          h_contract_terms,
    1353              :                                          amount,
    1354              :                                          purse_fee,
    1355              :                                          min_age,
    1356              :                                          flags,
    1357              :                                          reserve_pub,
    1358              :                                          reserve_sig))
    1359              :   {
    1360            0 :     struct TALER_AUDITORDB_BadSigLosses bsl = {
    1361              :       .problem_row_id = rowid,
    1362              :       .operation = (char *) "account-merge",
    1363              :       .loss = *purse_fee,
    1364              :       .operation_specific_pub = reserve_pub->eddsa_pub
    1365              :     };
    1366              : 
    1367            0 :     qs = TALER_AUDITORDB_insert_bad_sig_losses (
    1368              :       TALER_ARL_adb,
    1369              :       &bsl);
    1370            0 :     if (qs < 0)
    1371              :     {
    1372            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1373            0 :       rc->qs = qs;
    1374            0 :       return GNUNET_SYSERR;
    1375              :     }
    1376            0 :     TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_total_bad_sig_loss),
    1377              :                           &TALER_ARL_USE_AB (reserves_total_bad_sig_loss),
    1378              :                           purse_fee);
    1379            0 :     return GNUNET_OK;
    1380              :   }
    1381            0 :   if ((flags & TALER_WAMF_MERGE_MODE_MASK) !=
    1382              :       TALER_WAMF_MODE_CREATE_WITH_PURSE_FEE)
    1383            0 :     return GNUNET_OK; /* no impact on reserve balance */
    1384            0 :   rs = setup_reserve (rc,
    1385              :                       reserve_pub);
    1386            0 :   if (NULL == rs)
    1387              :   {
    1388            0 :     GNUNET_break (0);
    1389            0 :     return GNUNET_SYSERR;
    1390              :   }
    1391            0 :   TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_purse_fee_revenue),
    1392              :                         &TALER_ARL_USE_AB (reserves_purse_fee_revenue),
    1393              :                         purse_fee);
    1394            0 :   TALER_ARL_amount_add (&rs->curr_balance.purse_fee_balance,
    1395              :                         &rs->curr_balance.purse_fee_balance,
    1396              :                         purse_fee);
    1397            0 :   TALER_ARL_amount_add (&rs->total_out,
    1398              :                         &rs->total_out,
    1399              :                         purse_fee);
    1400            0 :   return GNUNET_OK;
    1401              : }
    1402              : 
    1403              : 
    1404              : /**
    1405              :  * Function called with details about a purse that was merged into an account.
    1406              :  * Only updates the reserve balance, the actual verifications are done in the
    1407              :  * purse helper.
    1408              :  *
    1409              :  * @param rc closure
    1410              :  * @param rowid unique serial ID for the refund in our DB
    1411              :  * @param purse_pub public key of the purse
    1412              :  * @param reserve_pub which reserve is the purse credited to
    1413              :  * @param purse_value what is the target value of the purse
    1414              :  * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
    1415              :  */
    1416              : static enum GNUNET_GenericReturnValue
    1417            0 : purse_decision_cb (struct ReserveContext *rc,
    1418              :                    uint64_t rowid,
    1419              :                    const struct TALER_PurseContractPublicKeyP *purse_pub,
    1420              :                    const struct TALER_ReservePublicKeyP *reserve_pub,
    1421              :                    const struct TALER_Amount *purse_value)
    1422              : {
    1423              :   struct ReserveSummary *rs;
    1424              : 
    1425            0 :   GNUNET_assert (rowid >= TALER_ARL_USE_PP (
    1426              :                    reserves_purse_decisions_serial_id)); /* should be monotonically increasing */
    1427            0 :   TALER_ARL_USE_PP (reserves_purse_decisions_serial_id) = rowid + 1;
    1428            0 :   rs = setup_reserve (rc,
    1429              :                       reserve_pub);
    1430            0 :   if (NULL == rs)
    1431              :   {
    1432            0 :     GNUNET_break (0);
    1433            0 :     return GNUNET_SYSERR;
    1434              :   }
    1435            0 :   TALER_ARL_amount_add (&rs->total_in,
    1436              :                         &rs->total_in,
    1437              :                         purse_value);
    1438            0 :   return GNUNET_OK;
    1439              : }
    1440              : 
    1441              : 
    1442              : /**
    1443              :  * Check that the reserve summary matches what the exchange database
    1444              :  * thinks about the reserve, and update our own state of the reserve.
    1445              :  *
    1446              :  * Remove all reserves that we are happy with from the DB.
    1447              :  *
    1448              :  * @param cls our `struct ReserveContext`
    1449              :  * @param key hash of the reserve public key
    1450              :  * @param value a `struct ReserveSummary`
    1451              :  * @return #GNUNET_OK to process more entries
    1452              :  */
    1453              : static enum GNUNET_GenericReturnValue
    1454            0 : verify_reserve_balance (void *cls,
    1455              :                         const struct GNUNET_HashCode *key,
    1456              :                         void *value)
    1457              : {
    1458            0 :   struct ReserveContext *rc = cls;
    1459            0 :   struct ReserveSummary *rs = value;
    1460              :   struct TALER_Amount mbalance;
    1461              :   struct TALER_Amount nbalance;
    1462              :   enum GNUNET_DB_QueryStatus qs;
    1463              :   enum GNUNET_GenericReturnValue ret;
    1464              : 
    1465            0 :   ret = GNUNET_OK;
    1466              :   /* Check our reserve summary balance calculation shows that
    1467              :      the reserve balance is acceptable (i.e. non-negative) */
    1468            0 :   TALER_ARL_amount_add (&mbalance,
    1469              :                         &rs->total_in,
    1470              :                         &rs->prev_balance.reserve_balance);
    1471            0 :   if (TALER_ARL_SR_INVALID_NEGATIVE ==
    1472            0 :       TALER_ARL_amount_subtract_neg (&nbalance,
    1473              :                                      &mbalance,
    1474              :                                      &rs->total_out))
    1475              :   {
    1476            0 :     struct TALER_AUDITORDB_ReserveBalanceInsufficientInconsistency rbiil = {
    1477              :       .reserve_pub = rs->reserve_pub.eddsa_pub,
    1478              :       .inconsistency_gain = false
    1479              :     };
    1480              : 
    1481            0 :     TALER_ARL_amount_subtract (&rbiil.inconsistency_amount,
    1482              :                                &rs->total_out,
    1483              :                                &mbalance);
    1484            0 :     TALER_ARL_amount_add (&rs->curr_balance.reserve_loss,
    1485              :                           &rs->prev_balance.reserve_loss,
    1486              :                           &rbiil.inconsistency_amount);
    1487            0 :     TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_reserve_loss),
    1488              :                           &TALER_ARL_USE_AB (reserves_reserve_loss),
    1489              :                           &rbiil.inconsistency_amount);
    1490            0 :     qs = TALER_AUDITORDB_insert_reserve_balance_insufficient_inconsistency (
    1491              :       TALER_ARL_adb,
    1492              :       &rbiil);
    1493              : 
    1494            0 :     if (qs < 0)
    1495              :     {
    1496            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1497            0 :       rc->qs = qs;
    1498            0 :       return GNUNET_SYSERR;
    1499              :     }
    1500              :     /* Continue with a reserve balance of zero */
    1501            0 :     GNUNET_assert (GNUNET_OK ==
    1502              :                    TALER_amount_set_zero (TALER_ARL_currency,
    1503              :                                           &rs->curr_balance.reserve_balance));
    1504            0 :     nbalance = rs->curr_balance.reserve_balance;
    1505              :   }
    1506              :   else
    1507              :   {
    1508              :     /* Update remaining reserve balance! */
    1509            0 :     rs->curr_balance.reserve_balance = nbalance;
    1510              :   }
    1511              : 
    1512            0 :   if (internal_checks)
    1513              :   {
    1514              :     /* Now check OUR balance calculation vs. the one the exchange has
    1515              :        in its database. This can only be done when we are doing an
    1516              :        internal audit, as otherwise the balance of the 'reserves' table
    1517              :        is not replicated at the auditor. */
    1518            0 :     struct TALER_EXCHANGEDB_Reserve reserve = {
    1519              :       .pub = rs->reserve_pub
    1520              :     };
    1521              : 
    1522            0 :     qs = TALER_EXCHANGEDB_get_reserve (TALER_ARL_edb,
    1523              :                                        &reserve);
    1524            0 :     if (0 > qs)
    1525              :     {
    1526            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1527            0 :       rc->qs = qs;
    1528            0 :       return GNUNET_SYSERR;
    1529              :     }
    1530            0 :     if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    1531              :     {
    1532              :       /* If the exchange doesn't have this reserve in the summary, it
    1533              :          is like the exchange 'lost' that amount from its records,
    1534              :          making an illegitimate gain over the amount it dropped.
    1535              :          We don't add the amount to some total simply because it is
    1536              :          not an actualized gain and could be trivially corrected by
    1537              :          restoring the summary. */
    1538            0 :       struct TALER_AUDITORDB_ReserveBalanceInsufficientInconsistency rbiig = {
    1539              :         .reserve_pub = rs->reserve_pub.eddsa_pub,
    1540              :         .inconsistency_amount = nbalance,
    1541              :         .inconsistency_gain = true
    1542              :       };
    1543              : 
    1544            0 :       qs = TALER_AUDITORDB_insert_reserve_balance_insufficient_inconsistency (
    1545              :         TALER_ARL_adb,
    1546              :         &rbiig);
    1547              : 
    1548            0 :       if (qs < 0)
    1549              :       {
    1550            0 :         GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1551            0 :         rc->qs = qs;
    1552            0 :         return GNUNET_SYSERR;
    1553              :       }
    1554            0 :       if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    1555              :       {
    1556            0 :         GNUNET_break (0);
    1557            0 :         qs = GNUNET_DB_STATUS_HARD_ERROR;
    1558              :       }
    1559            0 :       rc->qs = qs;
    1560              :     }
    1561              :     else
    1562              :     {
    1563              :       /* Check that exchange's balance matches our expected balance for the reserve */
    1564            0 :       if (0 != TALER_amount_cmp (&rs->curr_balance.reserve_balance,
    1565              :                                  &reserve.balance))
    1566              :       {
    1567              :         struct TALER_Amount delta;
    1568              : 
    1569            0 :         if (0 < TALER_amount_cmp (&rs->curr_balance.reserve_balance,
    1570              :                                   &reserve.balance))
    1571              :         {
    1572              :           /* balance > reserve.balance */
    1573            0 :           TALER_ARL_amount_subtract (&delta,
    1574              :                                      &rs->curr_balance.reserve_balance,
    1575              :                                      &reserve.balance);
    1576            0 :           TALER_ARL_amount_add (&TALER_ARL_USE_AB (
    1577              :                                   total_balance_summary_delta_plus),
    1578              :                                 &TALER_ARL_USE_AB (
    1579              :                                   total_balance_summary_delta_plus),
    1580              :                                 &delta);
    1581              :         }
    1582              :         else
    1583              :         {
    1584              :           /* balance < reserve.balance */
    1585            0 :           TALER_ARL_amount_subtract (&delta,
    1586              :                                      &reserve.balance,
    1587              :                                      &rs->curr_balance.reserve_balance);
    1588            0 :           TALER_ARL_amount_add (&TALER_ARL_USE_AB (
    1589              :                                   total_balance_summary_delta_minus),
    1590              :                                 &TALER_ARL_USE_AB (
    1591              :                                   total_balance_summary_delta_minus),
    1592              :                                 &delta);
    1593              :         }
    1594              : 
    1595              :         {
    1596            0 :           struct TALER_AUDITORDB_ReserveBalanceInsufficientInconsistency rbiig =
    1597              :           {
    1598              :             .reserve_pub = rs->reserve_pub.eddsa_pub,
    1599              :             .inconsistency_amount = nbalance,
    1600              :             .inconsistency_gain = true
    1601              :           };
    1602              : 
    1603            0 :           qs = TALER_AUDITORDB_insert_reserve_balance_insufficient_inconsistency
    1604              :                (
    1605              :             TALER_ARL_adb,
    1606              :             &rbiig);
    1607              :         }
    1608            0 :         if (qs < 0)
    1609              :         {
    1610            0 :           GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1611            0 :           rc->qs = qs;
    1612            0 :           return GNUNET_SYSERR;
    1613              :         }
    1614              : 
    1615              :         {
    1616            0 :           struct TALER_AUDITORDB_ReserveBalanceSummaryWrongInconsistency rbswi =
    1617              :           {
    1618              :             .exchange_amount = reserve.balance,
    1619              :             .auditor_amount = rs->curr_balance.reserve_balance,
    1620              :             .reserve_pub = rs->reserve_pub
    1621              :           };
    1622              : 
    1623              :           qs =
    1624            0 :             TALER_AUDITORDB_insert_reserve_balance_summary_wrong_inconsistency
    1625              :             (
    1626              :               TALER_ARL_adb,
    1627              :               &rbswi);
    1628              :         }
    1629            0 :         if (qs < 0)
    1630              :         {
    1631            0 :           GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1632            0 :           rc->qs = qs;
    1633            0 :           return GNUNET_SYSERR;
    1634              :         }
    1635              :       }
    1636              :     }
    1637              :   }   /* end of 'if (internal_checks)' */
    1638              : 
    1639              :   /* Check that reserve is being closed if it is past its expiration date
    1640              :      (and the closing fee would not exceed the remaining balance) */
    1641            0 :   if (GNUNET_TIME_relative_cmp (CLOSING_GRACE_PERIOD,
    1642              :                                 <,
    1643              :                                 GNUNET_TIME_absolute_get_duration (
    1644              :                                   rs->a_expiration_date.abs_time)))
    1645              :   {
    1646              :     /* Reserve is expired */
    1647              :     struct TALER_Amount cfee;
    1648              : 
    1649            0 :     if ( (NULL != rs->sender_account.full_payto) &&
    1650              :          (GNUNET_OK ==
    1651            0 :           get_closing_fee (rs->sender_account,
    1652              :                            rs->a_expiration_date,
    1653              :                            &cfee)) )
    1654              :     {
    1655              :       /* We got the closing fee */
    1656            0 :       if (1 == TALER_amount_cmp (&nbalance,
    1657              :                                  &cfee))
    1658              :       {
    1659            0 :         struct TALER_AUDITORDB_ReserveNotClosedInconsistency rnci = {
    1660              :           .reserve_pub = rs->reserve_pub,
    1661              :           .expiration_time = rs->a_expiration_date.abs_time,
    1662              :           .balance = nbalance,
    1663            0 :           .diagnostic = rs->sender_account.full_payto
    1664              :         };
    1665              : 
    1666              :         /* remaining balance (according to us) exceeds closing fee */
    1667            0 :         TALER_ARL_amount_add (&TALER_ARL_USE_AB (
    1668              :                                 total_balance_reserve_not_closed),
    1669              :                               &TALER_ARL_USE_AB (
    1670              :                                 total_balance_reserve_not_closed),
    1671              :                               &rnci.balance);
    1672            0 :         qs = TALER_AUDITORDB_insert_reserve_not_closed_inconsistency (
    1673              :           TALER_ARL_adb,
    1674              :           &rnci);
    1675            0 :         if (qs < 0)
    1676              :         {
    1677            0 :           GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1678            0 :           rc->qs = qs;
    1679            0 :           return GNUNET_SYSERR;
    1680              :         }
    1681              :       }
    1682              :     }
    1683              :     else
    1684              :     {
    1685              :       /* We failed to determine the closing fee, complain! */
    1686            0 :       struct TALER_AUDITORDB_ReserveNotClosedInconsistency rncid = {
    1687              :         .reserve_pub = rs->reserve_pub,
    1688              :         .balance = nbalance,
    1689              :         .expiration_time = rs->a_expiration_date.abs_time,
    1690              :         .diagnostic = (char *) "could not determine closing fee"
    1691              :       };
    1692              : 
    1693              :       /* Even if we don't know the closing fee, update the
    1694              :          total_balance_reserve_not_closed */
    1695            0 :       TALER_ARL_amount_add (&TALER_ARL_USE_AB (
    1696              :                               total_balance_reserve_not_closed),
    1697              :                             &TALER_ARL_USE_AB (
    1698              :                               total_balance_reserve_not_closed),
    1699              :                             &nbalance);
    1700            0 :       qs = TALER_AUDITORDB_insert_reserve_not_closed_inconsistency (
    1701              :         TALER_ARL_adb,
    1702              :         &rncid);
    1703            0 :       if (qs < 0)
    1704              :       {
    1705            0 :         GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1706            0 :         rc->qs = qs;
    1707            0 :         return GNUNET_SYSERR;
    1708              :       }
    1709              :     }
    1710              :   }
    1711              :   /* We already computed the 'new' balance in 'curr_balance'
    1712              :      to include the previous balance, so this one is just
    1713              :      an assignment, not adding up! */
    1714            0 :   rs->prev_balance.reserve_balance = rs->curr_balance.reserve_balance;
    1715              : 
    1716              :   /* Add up new totals to previous totals  */
    1717            0 :   TALER_ARL_amount_add (&rs->prev_balance.reserve_loss,
    1718              :                         &rs->prev_balance.reserve_loss,
    1719              :                         &rs->curr_balance.reserve_loss);
    1720            0 :   TALER_ARL_amount_add (&rs->prev_balance.withdraw_fee_balance,
    1721              :                         &rs->prev_balance.withdraw_fee_balance,
    1722              :                         &rs->curr_balance.withdraw_fee_balance);
    1723            0 :   TALER_ARL_amount_add (&rs->prev_balance.close_fee_balance,
    1724              :                         &rs->prev_balance.close_fee_balance,
    1725              :                         &rs->curr_balance.close_fee_balance);
    1726            0 :   TALER_ARL_amount_add (&rs->prev_balance.purse_fee_balance,
    1727              :                         &rs->prev_balance.purse_fee_balance,
    1728              :                         &rs->curr_balance.purse_fee_balance);
    1729            0 :   TALER_ARL_amount_add (&rs->prev_balance.open_fee_balance,
    1730              :                         &rs->prev_balance.open_fee_balance,
    1731              :                         &rs->curr_balance.open_fee_balance);
    1732            0 :   TALER_ARL_amount_add (&rs->prev_balance.history_fee_balance,
    1733              :                         &rs->prev_balance.history_fee_balance,
    1734              :                         &rs->curr_balance.history_fee_balance);
    1735              :   /* Update global balance: add incoming first, then try
    1736              :      to subtract outgoing... */
    1737            0 :   TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_reserve_total_balance),
    1738              :                         &TALER_ARL_USE_AB (reserves_reserve_total_balance),
    1739              :                         &rs->total_in);
    1740              :   {
    1741              :     struct TALER_Amount r;
    1742              : 
    1743            0 :     if (TALER_ARL_SR_INVALID_NEGATIVE ==
    1744            0 :         TALER_ARL_amount_subtract_neg (&r,
    1745              :                                        &TALER_ARL_USE_AB (
    1746              :                                          reserves_reserve_total_balance),
    1747              :                                        &rs->total_out))
    1748              :     {
    1749              :       /* We could not reduce our total balance, i.e. exchange allowed IN TOTAL (!)
    1750              :          to be withdrawn more than it was IN TOTAL ever given (exchange balance
    1751              :          went negative!).  Woopsie. Calculate how badly it went and log. */
    1752            0 :       report_amount_arithmetic_inconsistency ("global escrow balance",
    1753              :                                               0,
    1754              :                                               &TALER_ARL_USE_AB (
    1755              :                                                 reserves_reserve_total_balance),                   /* what we had */
    1756            0 :                                               &rs->total_out,   /* what we needed */
    1757              :                                               0 /* specific profit/loss does not apply to the total summary */
    1758              :                                               );
    1759            0 :       if (global_qs < 0)
    1760            0 :         return GNUNET_SYSERR;
    1761              :       /* We unexpectedly went negative, so a sane value to continue from
    1762              :          would be zero. */
    1763            0 :       GNUNET_assert (GNUNET_OK ==
    1764              :                      TALER_amount_set_zero (TALER_ARL_currency,
    1765              :                                             &TALER_ARL_USE_AB (
    1766              :                                               reserves_reserve_total_balance)));
    1767              :     }
    1768              :     else
    1769              :     {
    1770            0 :       TALER_ARL_USE_AB (reserves_reserve_total_balance) = r;
    1771              :     }
    1772              :   }
    1773            0 :   if (TALER_amount_is_zero (&rs->prev_balance.reserve_balance))
    1774              :   {
    1775              :     /* balance is zero, drop reserve details (and then do not update/insert) */
    1776            0 :     if (rs->had_ri)
    1777              :     {
    1778            0 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1779              :                   "Final balance of reserve `%s' is zero, dropping it\n",
    1780              :                   TALER_B2S (&rs->reserve_pub));
    1781            0 :       qs = TALER_AUDITORDB_delete_reserve_info (TALER_ARL_adb,
    1782            0 :                                                 &rs->reserve_pub);
    1783            0 :       if (0 >= qs)
    1784              :       {
    1785            0 :         GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1786            0 :         ret = GNUNET_SYSERR;
    1787            0 :         rc->qs = qs;
    1788              :       }
    1789              :     }
    1790              :     else
    1791              :     {
    1792            0 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1793              :                   "Final balance of reserve `%s' is zero, no need to remember it\n",
    1794              :                   TALER_B2S (&rs->reserve_pub));
    1795              :     }
    1796              :   }
    1797              :   else
    1798              :   {
    1799              :     /* balance is non-zero, persist for future audits */
    1800            0 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1801              :                 "Remembering final balance of reserve `%s' as %s\n",
    1802              :                 TALER_B2S (&rs->reserve_pub),
    1803              :                 TALER_amount2s (&rs->prev_balance.reserve_balance));
    1804            0 :     if (rs->had_ri)
    1805            0 :       qs = TALER_AUDITORDB_update_reserve_info (TALER_ARL_adb,
    1806            0 :                                                 &rs->reserve_pub,
    1807            0 :                                                 &rs->prev_balance,
    1808              :                                                 rs->a_expiration_date);
    1809              :     else
    1810            0 :       qs = TALER_AUDITORDB_insert_reserve_info (TALER_ARL_adb,
    1811            0 :                                                 &rs->reserve_pub,
    1812            0 :                                                 &rs->prev_balance,
    1813              :                                                 rs->a_expiration_date,
    1814              :                                                 rs->sender_account);
    1815            0 :     if (0 >= qs)
    1816              :     {
    1817            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1818            0 :       ret = GNUNET_SYSERR;
    1819            0 :       rc->qs = qs;
    1820              :     }
    1821              :   }
    1822              :   /* now we can discard the cached entry */
    1823            0 :   GNUNET_assert (GNUNET_YES ==
    1824              :                  GNUNET_CONTAINER_multihashmap_remove (rc->reserves,
    1825              :                                                        key,
    1826              :                                                        rs));
    1827            0 :   GNUNET_free (rs->sender_account.full_payto);
    1828            0 :   GNUNET_free (rs);
    1829            0 :   return ret;
    1830              : }
    1831              : 
    1832              : 
    1833              : #define CHECK_DB() do {                                       \
    1834              :           if (qs < 0) {                                       \
    1835              :             GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); \
    1836              :             goto cleanup;                                     \
    1837              :           }                                                   \
    1838              :           if (global_qs < 0) {                                \
    1839              :             GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == global_qs); \
    1840              :             qs = global_qs;                                          \
    1841              :             goto cleanup;                                            \
    1842              :           }                                                          \
    1843              :           if (rc.qs < 0) {                                           \
    1844              :             GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == rc.qs);     \
    1845              :             qs = rc.qs;                                              \
    1846              :             goto cleanup;                                            \
    1847              :           }                                                          \
    1848              : } while (0)
    1849              : 
    1850              : 
    1851              : /**
    1852              :  * Analyze reserves for being well-formed.
    1853              :  *
    1854              :  * @param cls NULL
    1855              :  * @return transaction status code
    1856              :  */
    1857              : static enum GNUNET_DB_QueryStatus
    1858            8 : analyze_reserves (void *cls)
    1859              : {
    1860            8 :   struct ReserveContext rc = {
    1861              :     .qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT
    1862              :   };
    1863              :   enum GNUNET_DB_QueryStatus qs;
    1864              : 
    1865              :   (void) cls;
    1866            8 :   global_qs = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
    1867            8 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1868              :               "Analyzing reserves\n");
    1869            8 :   qs = TALER_AUDITORDB_get_auditor_progress (
    1870              :     TALER_ARL_adb,
    1871              :     TALER_ARL_GET_PP (reserves_reserve_in_serial_id),
    1872              :     TALER_ARL_GET_PP (reserves_withdraw_serial_id),
    1873              :     TALER_ARL_GET_PP (reserves_reserve_recoup_serial_id),
    1874              :     TALER_ARL_GET_PP (reserves_reserve_open_serial_id),
    1875              :     TALER_ARL_GET_PP (reserves_reserve_close_serial_id),
    1876              :     TALER_ARL_GET_PP (reserves_purse_decisions_serial_id),
    1877              :     TALER_ARL_GET_PP (reserves_account_merges_serial_id),
    1878              :     TALER_ARL_GET_PP (reserves_history_requests_serial_id),
    1879              :     NULL);
    1880            8 :   if (0 > qs)
    1881              :   {
    1882            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1883            0 :     return qs;
    1884              :   }
    1885            8 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    1886              :   {
    1887            0 :     GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
    1888              :                 "First analysis using this auditor, starting audit from scratch\n");
    1889              :   }
    1890              :   else
    1891              :   {
    1892            8 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1893              :                 "Resuming reserve audit at %llu/%llu/%llu/%llu/%llu/%llu/%llu/%llu\n",
    1894              :                 (unsigned long long) TALER_ARL_USE_PP (
    1895              :                   reserves_reserve_in_serial_id),
    1896              :                 (unsigned long long) TALER_ARL_USE_PP (
    1897              :                   reserves_withdraw_serial_id),
    1898              :                 (unsigned long long) TALER_ARL_USE_PP (
    1899              :                   reserves_reserve_recoup_serial_id),
    1900              :                 (unsigned long long) TALER_ARL_USE_PP (
    1901              :                   reserves_reserve_open_serial_id),
    1902              :                 (unsigned long long) TALER_ARL_USE_PP (
    1903              :                   reserves_reserve_close_serial_id),
    1904              :                 (unsigned long long) TALER_ARL_USE_PP (
    1905              :                   reserves_purse_decisions_serial_id),
    1906              :                 (unsigned long long) TALER_ARL_USE_PP (
    1907              :                   reserves_account_merges_serial_id),
    1908              :                 (unsigned long long) TALER_ARL_USE_PP (
    1909              :                   reserves_history_requests_serial_id));
    1910              :   }
    1911            8 :   qs = TALER_AUDITORDB_get_balance (
    1912              :     TALER_ARL_adb,
    1913              :     TALER_ARL_GET_AB (reserves_reserve_total_balance),
    1914              :     TALER_ARL_GET_AB (reserves_reserve_loss),
    1915              :     TALER_ARL_GET_AB (reserves_withdraw_fee_revenue),
    1916              :     TALER_ARL_GET_AB (reserves_close_fee_revenue),
    1917              :     TALER_ARL_GET_AB (reserves_purse_fee_revenue),
    1918              :     TALER_ARL_GET_AB (reserves_open_fee_revenue),
    1919              :     TALER_ARL_GET_AB (reserves_history_fee_revenue),
    1920              :     TALER_ARL_GET_AB (reserves_total_bad_sig_loss),
    1921              :     TALER_ARL_GET_AB (total_balance_reserve_not_closed),
    1922              :     TALER_ARL_GET_AB (reserves_total_arithmetic_delta_plus),
    1923              :     TALER_ARL_GET_AB (reserves_total_arithmetic_delta_minus),
    1924              :     TALER_ARL_GET_AB (total_balance_summary_delta_plus),
    1925              :     TALER_ARL_GET_AB (total_balance_summary_delta_minus),
    1926              :     NULL);
    1927            8 :   if (qs < 0)
    1928              :   {
    1929            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1930            0 :     return qs;
    1931              :   }
    1932            8 :   rc.reserves = GNUNET_CONTAINER_multihashmap_create (512,
    1933              :                                                       GNUNET_NO);
    1934            8 :   rc.revoked = GNUNET_CONTAINER_multihashmap_create (4,
    1935              :                                                      GNUNET_NO);
    1936              : 
    1937            8 :   qs = TALER_EXCHANGEDB_iterate_reserves_in_above_serial_id (
    1938              :     TALER_ARL_edb,
    1939              :     TALER_ARL_USE_PP (reserves_reserve_in_serial_id),
    1940              :     &handle_reserve_in,
    1941              :     &rc);
    1942            8 :   CHECK_DB ();
    1943            8 :   qs = TALER_EXCHANGEDB_iterate_withdrawals_above_serial_id (
    1944              :     TALER_ARL_edb,
    1945              :     TALER_ARL_USE_PP (reserves_withdraw_serial_id),
    1946              :     &handle_withdrawals,
    1947              :     &rc);
    1948            8 :   CHECK_DB ();
    1949            8 :   qs = TALER_EXCHANGEDB_iterate_recoups_above_serial_id (
    1950              :     TALER_ARL_edb,
    1951              :     TALER_ARL_USE_PP (reserves_reserve_recoup_serial_id),
    1952              :     &handle_recoup_by_reserve,
    1953              :     &rc);
    1954            8 :   if ( (qs < 0) ||
    1955            8 :        (rc.qs < 0) ||
    1956            8 :        (global_qs < 0) )
    1957              :   {
    1958            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1959            0 :     return qs;
    1960              :   }
    1961              : 
    1962            8 :   qs = TALER_EXCHANGEDB_iterate_reserve_open_above_serial_id (
    1963              :     TALER_ARL_edb,
    1964              :     TALER_ARL_USE_PP (reserves_reserve_open_serial_id),
    1965              :     &handle_reserve_open,
    1966              :     &rc);
    1967            8 :   CHECK_DB ();
    1968            8 :   qs = TALER_EXCHANGEDB_iterate_reserve_closed_above_serial_id (
    1969              :     TALER_ARL_edb,
    1970              :     TALER_ARL_USE_PP (reserves_reserve_close_serial_id),
    1971              :     &handle_reserve_closed,
    1972              :     &rc);
    1973            8 :   CHECK_DB ();
    1974              :   /* process purse_decisions (to credit reserve) */
    1975            8 :   qs = TALER_EXCHANGEDB_iterate_purse_decisions_above_serial_id (
    1976              :     TALER_ARL_edb,
    1977              :     TALER_ARL_USE_PP (reserves_purse_decisions_serial_id),
    1978              :     false,      /* only go for merged purses! */
    1979              :     &purse_decision_cb,
    1980              :     &rc);
    1981            8 :   CHECK_DB ();
    1982              :   /* Charge purse fee! */
    1983              : 
    1984            8 :   qs = TALER_EXCHANGEDB_iterate_account_merges_above_serial_id (
    1985              :     TALER_ARL_edb,
    1986              :     TALER_ARL_USE_PP (reserves_account_merges_serial_id),
    1987              :     &handle_account_merged,
    1988              :     &rc);
    1989            8 :   CHECK_DB ();
    1990            8 :   GNUNET_CONTAINER_multihashmap_iterate (rc.reserves,
    1991              :                                          &verify_reserve_balance,
    1992              :                                          &rc);
    1993            8 :   CHECK_DB ();
    1994            8 :   GNUNET_break (0 ==
    1995              :                 GNUNET_CONTAINER_multihashmap_size (rc.reserves));
    1996              : 
    1997            8 :   qs = TALER_AUDITORDB_insert_balance (
    1998              :     TALER_ARL_adb,
    1999              :     TALER_ARL_SET_AB (reserves_reserve_total_balance),
    2000              :     TALER_ARL_SET_AB (reserves_reserve_loss),
    2001              :     TALER_ARL_SET_AB (reserves_withdraw_fee_revenue),
    2002              :     TALER_ARL_SET_AB (reserves_close_fee_revenue),
    2003              :     TALER_ARL_SET_AB (reserves_purse_fee_revenue),
    2004              :     TALER_ARL_SET_AB (reserves_open_fee_revenue),
    2005              :     TALER_ARL_SET_AB (reserves_history_fee_revenue),
    2006              :     TALER_ARL_SET_AB (reserves_total_bad_sig_loss),
    2007              :     TALER_ARL_SET_AB (total_balance_reserve_not_closed),
    2008              :     TALER_ARL_SET_AB (reserves_total_arithmetic_delta_plus),
    2009              :     TALER_ARL_SET_AB (reserves_total_arithmetic_delta_minus),
    2010              :     TALER_ARL_SET_AB (total_balance_summary_delta_plus),
    2011              :     TALER_ARL_SET_AB (total_balance_summary_delta_minus),
    2012              :     NULL);
    2013            8 :   if (0 > qs)
    2014              :   {
    2015            4 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    2016            4 :     goto cleanup;
    2017              :   }
    2018              : 
    2019            4 :   qs = TALER_AUDITORDB_insert_auditor_progress (
    2020              :     TALER_ARL_adb,
    2021              :     TALER_ARL_SET_PP (reserves_reserve_in_serial_id),
    2022              :     TALER_ARL_SET_PP (reserves_withdraw_serial_id),
    2023              :     TALER_ARL_SET_PP (reserves_reserve_recoup_serial_id),
    2024              :     TALER_ARL_SET_PP (reserves_reserve_open_serial_id),
    2025              :     TALER_ARL_SET_PP (reserves_reserve_close_serial_id),
    2026              :     TALER_ARL_SET_PP (reserves_purse_decisions_serial_id),
    2027              :     TALER_ARL_SET_PP (reserves_account_merges_serial_id),
    2028              :     TALER_ARL_SET_PP (reserves_history_requests_serial_id),
    2029              :     NULL);
    2030            4 :   if (0 > qs)
    2031              :   {
    2032            0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    2033              :                 "Failed to update auditor DB, not recording progress\n");
    2034            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    2035            0 :     goto cleanup;
    2036              :   }
    2037              : 
    2038            4 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    2039              :               "Concluded reserve audit step at %llu/%llu/%llu/%llu/%llu/%llu/%llu/%llu\n",
    2040              :               (unsigned long long) TALER_ARL_USE_PP (
    2041              :                 reserves_reserve_in_serial_id),
    2042              :               (unsigned long long) TALER_ARL_USE_PP (
    2043              :                 reserves_withdraw_serial_id),
    2044              :               (unsigned long long) TALER_ARL_USE_PP (
    2045              :                 reserves_reserve_recoup_serial_id),
    2046              :               (unsigned long long) TALER_ARL_USE_PP (
    2047              :                 reserves_reserve_open_serial_id),
    2048              :               (unsigned long long) TALER_ARL_USE_PP (
    2049              :                 reserves_reserve_close_serial_id),
    2050              :               (unsigned long long) TALER_ARL_USE_PP (
    2051              :                 reserves_purse_decisions_serial_id),
    2052              :               (unsigned long long) TALER_ARL_USE_PP (
    2053              :                 reserves_account_merges_serial_id),
    2054              :               (unsigned long long) TALER_ARL_USE_PP (
    2055              :                 reserves_history_requests_serial_id));
    2056            4 :   qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
    2057            8 : cleanup:
    2058            8 :   GNUNET_CONTAINER_multihashmap_destroy (rc.reserves);
    2059            8 :   GNUNET_CONTAINER_multihashmap_destroy (rc.revoked);
    2060            8 :   return qs;
    2061              : }
    2062              : 
    2063              : 
    2064              : #undef CHECK_DB
    2065              : 
    2066              : 
    2067              : /**
    2068              :  * Function called on events received from Postgres.
    2069              :  *
    2070              :  * @param cls closure, NULL
    2071              :  * @param extra additional event data provided
    2072              :  * @param extra_size number of bytes in @a extra
    2073              :  */
    2074              : static void
    2075            0 : db_notify (void *cls,
    2076              :            const void *extra,
    2077              :            size_t extra_size)
    2078              : {
    2079              :   (void) cls;
    2080              :   (void) extra;
    2081              :   (void) extra_size;
    2082              : 
    2083            0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    2084              :               "Received notification to wake reserves helper\n");
    2085            0 :   if (GNUNET_OK !=
    2086            0 :       TALER_ARL_setup_sessions_and_run (&analyze_reserves,
    2087              :                                         NULL))
    2088              :   {
    2089            0 :     GNUNET_SCHEDULER_shutdown ();
    2090            0 :     global_ret = EXIT_FAILURE;
    2091            0 :     return;
    2092              :   }
    2093              : }
    2094              : 
    2095              : 
    2096              : /**
    2097              :  * Function called on shutdown.
    2098              :  */
    2099              : static void
    2100            4 : do_shutdown (void *cls)
    2101              : {
    2102              :   (void) cls;
    2103            4 :   if (NULL != eh)
    2104              :   {
    2105            4 :     TALER_AUDITORDB_event_listen_cancel (eh);
    2106            4 :     eh = NULL;
    2107              :   }
    2108            4 :   TALER_ARL_done ();
    2109            4 : }
    2110              : 
    2111              : 
    2112              : /**
    2113              :  * Main function that will be run.
    2114              :  *
    2115              :  * @param cls closure
    2116              :  * @param args remaining command-line arguments
    2117              :  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
    2118              :  * @param c configuration
    2119              :  */
    2120              : static void
    2121            4 : run (void *cls,
    2122              :      char *const *args,
    2123              :      const char *cfgfile,
    2124              :      const struct GNUNET_CONFIGURATION_Handle *c)
    2125              : {
    2126              :   (void) cls;
    2127              :   (void) args;
    2128              :   (void) cfgfile;
    2129              : 
    2130            4 :   cfg = c;
    2131            4 :   GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
    2132              :                                  NULL);
    2133            4 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    2134              :               "Launching reserves auditor\n");
    2135            4 :   if (EXIT_SUCCESS !=
    2136            4 :       (global_ret = TALER_ARL_init (c)))
    2137              :   {
    2138            0 :     return;
    2139              :   }
    2140            4 :   if (GNUNET_OK !=
    2141            4 :       GNUNET_CONFIGURATION_get_value_time (TALER_ARL_cfg,
    2142              :                                            "exchangedb",
    2143              :                                            "IDLE_RESERVE_EXPIRATION_TIME",
    2144              :                                            &idle_reserve_expiration_time))
    2145              :   {
    2146            0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    2147              :                                "exchangedb",
    2148              :                                "IDLE_RESERVE_EXPIRATION_TIME");
    2149            0 :     GNUNET_SCHEDULER_shutdown ();
    2150            0 :     global_ret = EXIT_FAILURE;
    2151            0 :     return;
    2152              :   }
    2153            4 :   if (test_mode != 1)
    2154              :   {
    2155            4 :     struct GNUNET_DB_EventHeaderP es = {
    2156            4 :       .size = htons (sizeof (es)),
    2157            4 :       .type = htons (TALER_DBEVENT_EXCHANGE_AUDITOR_WAKE_HELPER_RESERVES)
    2158              :     };
    2159              : 
    2160            4 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    2161              :                 "Running helper indefinitely\n");
    2162            4 :     eh = TALER_AUDITORDB_event_listen (TALER_ARL_adb,
    2163              :                                        &es,
    2164            4 :                                        GNUNET_TIME_UNIT_FOREVER_REL,
    2165              :                                        &db_notify,
    2166              :                                        NULL);
    2167              :   }
    2168            4 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    2169              :               "Starting audit\n");
    2170            4 :   if (GNUNET_OK !=
    2171            4 :       TALER_ARL_setup_sessions_and_run (&analyze_reserves,
    2172              :                                         NULL))
    2173              :   {
    2174            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    2175              :                 "Audit failed\n");
    2176            0 :     GNUNET_SCHEDULER_shutdown ();
    2177            0 :     global_ret = EXIT_FAILURE;
    2178            0 :     return;
    2179              :   }
    2180              : }
    2181              : 
    2182              : 
    2183              : /**
    2184              :  * The main function to check the database's handling of reserves.
    2185              :  *
    2186              :  * @param argc number of arguments from the command line
    2187              :  * @param argv command line arguments
    2188              :  * @return 0 ok, 1 on error
    2189              :  */
    2190              : int
    2191            4 : main (int argc,
    2192              :       char *const *argv)
    2193              : {
    2194            4 :   const struct GNUNET_GETOPT_CommandLineOption options[] = {
    2195            4 :     GNUNET_GETOPT_option_flag ('i',
    2196              :                                "internal",
    2197              :                                "perform checks only applicable for exchange-internal audits",
    2198              :                                &internal_checks),
    2199            4 :     GNUNET_GETOPT_option_flag ('t',
    2200              :                                "test",
    2201              :                                "run in test mode and exit when idle",
    2202              :                                &test_mode),
    2203            4 :     GNUNET_GETOPT_option_timetravel ('T',
    2204              :                                      "timetravel"),
    2205              :     GNUNET_GETOPT_OPTION_END
    2206              :   };
    2207              :   enum GNUNET_GenericReturnValue ret;
    2208              : 
    2209            4 :   ret = GNUNET_PROGRAM_run (
    2210              :     TALER_AUDITOR_project_data (),
    2211              :     argc,
    2212              :     argv,
    2213              :     "taler-helper-auditor-reserves",
    2214              :     gettext_noop ("Audit Taler exchange reserve handling"),
    2215              :     options,
    2216              :     &run,
    2217              :     NULL);
    2218            4 :   if (GNUNET_SYSERR == ret)
    2219            0 :     return EXIT_NOTCONFIGURED;
    2220            4 :   if (GNUNET_NO == ret)
    2221            0 :     return EXIT_SUCCESS;
    2222            4 :   return global_ret;
    2223              : }
    2224              : 
    2225              : 
    2226              : /* end of taler-helper-auditor-reserves.c */
        

Generated by: LCOV version 2.0-1