LCOV - code coverage report
Current view: top level - auditor - taler-helper-auditor-reserves.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 44.0 % 562 247
Test Date: 2026-11-03 22:15:25 Functions: 66.7 % 18 12

            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              :       .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              :     .problem_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           16 : load_auditor_reserve_summary (struct ReserveSummary *rs)
     343              : {
     344              :   enum GNUNET_DB_QueryStatus qs;
     345              :   uint64_t rowid;
     346              : 
     347           16 :   qs = TALER_AUDITORDB_get_reserve_info (TALER_ARL_adb,
     348           16 :                                          &rs->reserve_pub,
     349              :                                          &rowid,
     350              :                                          &rs->prev_balance,
     351              :                                          &rs->a_expiration_date,
     352              :                                          &rs->sender_account);
     353           16 :   if (0 > qs)
     354              :   {
     355            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     356            0 :     return qs;
     357              :   }
     358           16 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
     359              :   {
     360           16 :     rs->had_ri = false;
     361           16 :     GNUNET_assert (GNUNET_OK ==
     362              :                    TALER_amount_set_zero (rs->total_in.currency,
     363              :                                           &rs->prev_balance.reserve_balance));
     364           16 :     GNUNET_assert (GNUNET_OK ==
     365              :                    TALER_amount_set_zero (rs->total_in.currency,
     366              :                                           &rs->prev_balance.reserve_loss));
     367           16 :     GNUNET_assert (GNUNET_OK ==
     368              :                    TALER_amount_set_zero (rs->total_in.currency,
     369              :                                           &rs->prev_balance.withdraw_fee_balance
     370              :                                           ));
     371           16 :     GNUNET_assert (GNUNET_OK ==
     372              :                    TALER_amount_set_zero (rs->total_in.currency,
     373              :                                           &rs->prev_balance.close_fee_balance));
     374           16 :     GNUNET_assert (GNUNET_OK ==
     375              :                    TALER_amount_set_zero (rs->total_in.currency,
     376              :                                           &rs->prev_balance.purse_fee_balance));
     377           16 :     GNUNET_assert (GNUNET_OK ==
     378              :                    TALER_amount_set_zero (rs->total_in.currency,
     379              :                                           &rs->prev_balance.open_fee_balance));
     380           16 :     GNUNET_assert (GNUNET_OK ==
     381              :                    TALER_amount_set_zero (rs->total_in.currency,
     382              :                                           &rs->prev_balance.history_fee_balance)
     383              :                    );
     384           16 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     385              :                 "Creating fresh reserve `%s'\n",
     386              :                 TALER_B2S (&rs->reserve_pub));
     387           16 :     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           58 : 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           58 :   GNUNET_CRYPTO_hash (reserve_pub,
     440              :                       sizeof (*reserve_pub),
     441              :                       &key);
     442           58 :   rs = GNUNET_CONTAINER_multihashmap_get (rc->reserves,
     443              :                                           &key);
     444           58 :   if (NULL != rs)
     445           42 :     return rs;
     446           16 :   rs = GNUNET_new (struct ReserveSummary);
     447           16 :   rs->reserve_pub = *reserve_pub;
     448           16 :   GNUNET_assert (GNUNET_OK ==
     449              :                  TALER_amount_set_zero (TALER_ARL_currency,
     450              :                                         &rs->total_in));
     451           16 :   GNUNET_assert (GNUNET_OK ==
     452              :                  TALER_amount_set_zero (TALER_ARL_currency,
     453              :                                         &rs->total_out));
     454           16 :   GNUNET_assert (GNUNET_OK ==
     455              :                  TALER_amount_set_zero (TALER_ARL_currency,
     456              :                                         &rs->curr_balance.reserve_balance));
     457           16 :   GNUNET_assert (GNUNET_OK ==
     458              :                  TALER_amount_set_zero (TALER_ARL_currency,
     459              :                                         &rs->curr_balance.reserve_loss));
     460           16 :   GNUNET_assert (GNUNET_OK ==
     461              :                  TALER_amount_set_zero (TALER_ARL_currency,
     462              :                                         &rs->curr_balance.withdraw_fee_balance))
     463              :   ;
     464           16 :   GNUNET_assert (GNUNET_OK ==
     465              :                  TALER_amount_set_zero (TALER_ARL_currency,
     466              :                                         &rs->curr_balance.close_fee_balance));
     467           16 :   GNUNET_assert (GNUNET_OK ==
     468              :                  TALER_amount_set_zero (TALER_ARL_currency,
     469              :                                         &rs->curr_balance.purse_fee_balance));
     470           16 :   GNUNET_assert (GNUNET_OK ==
     471              :                  TALER_amount_set_zero (TALER_ARL_currency,
     472              :                                         &rs->curr_balance.open_fee_balance));
     473           16 :   GNUNET_assert (GNUNET_OK ==
     474              :                  TALER_amount_set_zero (TALER_ARL_currency,
     475              :                                         &rs->curr_balance.history_fee_balance));
     476           16 :   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           16 :   GNUNET_assert (GNUNET_OK ==
     483              :                  GNUNET_CONTAINER_multihashmap_put (rc->reserves,
     484              :                                                     &key,
     485              :                                                     rs,
     486              :                                                     GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)
     487              :                  );
     488           16 :   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           16 : 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           16 :   GNUNET_assert (rowid >= TALER_ARL_USE_PP (reserves_reserve_in_serial_id));
     520           16 :   TALER_ARL_USE_PP (reserves_reserve_in_serial_id) = rowid + 1;
     521           16 :   rs = setup_reserve (rc,
     522              :                       reserve_pub);
     523           16 :   if (NULL == rs)
     524              :   {
     525            0 :     GNUNET_break (0);
     526            0 :     return GNUNET_SYSERR;
     527              :   }
     528           16 :   if (NULL == rs->sender_account.full_payto)
     529              :     rs->sender_account.full_payto
     530           16 :       = GNUNET_strdup (sender_account_details.full_payto);
     531           16 :   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           16 :   expiry = GNUNET_TIME_absolute_to_timestamp (
     536              :     GNUNET_TIME_absolute_add (execution_date.abs_time,
     537              :                               idle_reserve_expiration_time));
     538           16 :   rs->a_expiration_date = GNUNET_TIME_timestamp_max (rs->a_expiration_date,
     539              :                                                      expiry);
     540           16 :   TALER_ARL_amount_add (&rs->total_in,
     541              :                         &rs->total_in,
     542              :                         credit);
     543           16 :   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           36 : 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              :   struct TALER_Amount auditor_amount;
     586              :   struct TALER_Amount auditor_fee;
     587              :   struct TALER_Amount auditor_amount_with_fee;
     588           36 :   struct TALER_AgeMask age_mask = { 0 };
     589           36 :   bool have_age_mask = false;
     590           36 :   bool missing_denom = false;
     591              :   enum GNUNET_DB_QueryStatus qs;
     592              : 
     593              :   /* should be monotonically increasing */
     594           36 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     595              :               "Analyzing withdrawal row %llu\n",
     596              :               (unsigned long long) rowid);
     597           36 :   GNUNET_assert (rowid >= TALER_ARL_USE_PP (reserves_withdraw_serial_id));
     598           36 :   TALER_ARL_USE_PP (reserves_withdraw_serial_id) = rowid + 1;
     599              : 
     600           36 :   GNUNET_assert (GNUNET_OK ==
     601              :                  TALER_amount_set_zero (TALER_ARL_currency,
     602              :                                         &auditor_amount));
     603           36 :   GNUNET_assert (GNUNET_OK ==
     604              :                  TALER_amount_set_zero (TALER_ARL_currency,
     605              :                                         &auditor_fee));
     606           36 :   GNUNET_assert (GNUNET_OK ==
     607              :                  TALER_amount_set_zero (TALER_ARL_currency,
     608              :                                         &auditor_amount_with_fee));
     609           36 :   rs = setup_reserve (rc,
     610              :                       reserve_pub);
     611           36 :   if (NULL == rs)
     612              :   {
     613            0 :     GNUNET_break (0);
     614            0 :     return GNUNET_SYSERR;
     615              :   }
     616           72 :   for (size_t i = 0; i < num_denom_serials; i++)
     617              :   {
     618              :     const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue;
     619              : 
     620              :     /* lookup denomination pub data (make sure denom_pub is valid, establish fees);
     621              :        initializes wsrd.h_denomination_pub! */
     622           36 :     qs = TALER_ARL_get_denomination_info_by_serial (denom_serials[i],
     623              :                                                     &issue);
     624           36 :     if (0 > qs)
     625              :     {
     626            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     627            0 :       if (GNUNET_DB_STATUS_HARD_ERROR == qs)
     628            0 :         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     629              :                     "Hard database error trying to get denomination by serial %llu (%s) from database!\n",
     630              :                     (unsigned long long) denom_serials[i],
     631              :                     GNUNET_h2s (&h_planchets->hash));
     632            0 :       rc->qs = qs;
     633            0 :       return GNUNET_SYSERR;
     634              :     }
     635           36 :     if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
     636              :     {
     637            0 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     638              :                   "Denomination #%llu not found\n",
     639              :                   (unsigned long long) denom_serials[i]);
     640            0 :       report_row_inconsistency ("withdraw",
     641              :                                 rowid,
     642              :                                 "denomination key not found");
     643            0 :       if (global_qs < 0)
     644            0 :         return GNUNET_SYSERR;
     645              :       /* We cannot account for this planchet, but the other planchets
     646              :          of the batch must still be processed. */
     647            0 :       missing_denom = true;
     648            0 :       continue;
     649              :     }
     650           36 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     651              :                 "Analyzing withdrawn denomination #%llu (%s)\n",
     652              :                 (unsigned long long) denom_serials[i],
     653              :                 TALER_amount2s (&issue->value));
     654           36 :     if (! have_age_mask)
     655              :     {
     656           36 :       age_mask = issue->age_mask;
     657           36 :       have_age_mask = true;
     658              :     }
     659            0 :     else if (age_mask.bits != issue->age_mask.bits)
     660              :     {
     661              :       /* All denominations of one batch must share the age mask, as the
     662              :          reserve signature is made over a single age mask. */
     663            0 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     664              :                   "Denominations of withdraw row %llu disagree on the age mask\n",
     665              :                   (unsigned long long) rowid);
     666            0 :       report_row_inconsistency ("withdraw",
     667              :                                 rowid,
     668              :                                 "denominations of the batch disagree on the age mask");
     669            0 :       if (global_qs < 0)
     670            0 :         return GNUNET_SYSERR;
     671              :     }
     672              : 
     673              :     /* check that execution date is within withdraw range for denom_pub  */
     674           36 :     GNUNET_log (
     675              :       GNUNET_ERROR_TYPE_DEBUG,
     676              :       "Checking withdraw timing: %llu, expire: %llu, timing: %llu\n",
     677              :       (unsigned long long) issue->start.abs_time.abs_value_us,
     678              :       (unsigned long long) issue->expire_withdraw.abs_time.abs_value_us,
     679              :       (unsigned long long) execution_date.abs_time.abs_value_us);
     680           36 :     if (GNUNET_TIME_timestamp_cmp (issue->start,
     681              :                                    >,
     682           36 :                                    execution_date) ||
     683           36 :         GNUNET_TIME_timestamp_cmp (issue->expire_withdraw,
     684              :                                    <,
     685              :                                    execution_date))
     686              :     {
     687              :       struct TALER_AUDITORDB_DenominationKeyValidityWithdrawInconsistency
     688            0 :         dkvwi = {
     689              :         .problem_row_id = rowid,
     690              :         .execution_date = execution_date.abs_time,
     691            0 :         .denompub_h = issue->denom_hash,
     692              :         .reserve_pub = *reserve_pub
     693              :       };
     694              : 
     695            0 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     696              :                   "Withdraw outside of denomination #%llu validity period detected\n",
     697              :                   (unsigned long long) denom_serials[i]);
     698              :       qs =
     699            0 :         TALER_AUDITORDB_insert_denomination_key_validity_withdraw_inconsistency
     700              :         (
     701              :           TALER_ARL_adb,
     702              :           &dkvwi);
     703              : 
     704            0 :       if (qs < 0)
     705              :       {
     706            0 :         GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     707            0 :         rc->qs = qs;
     708            0 :         return GNUNET_SYSERR;
     709              :       }
     710              :     }
     711           36 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     712              :                 "Adding withdraw fee of denomination (%s)\n",
     713              :                 TALER_amount2s (&issue->fees.withdraw));
     714           36 :     TALER_ARL_amount_add (&auditor_amount,
     715              :                           &auditor_amount,
     716              :                           &issue->value);
     717           36 :     TALER_ARL_amount_add (&auditor_fee,
     718              :                           &auditor_fee,
     719              :                           &issue->fees.withdraw);
     720              :     {
     721              :       struct TALER_Amount issue_amount_with_fee;
     722              : 
     723           36 :       TALER_ARL_amount_add (&issue_amount_with_fee,
     724              :                             &issue->value,
     725              :                             &issue->fees.withdraw);
     726           36 :       TALER_ARL_amount_add (&auditor_amount_with_fee,
     727              :                             &auditor_amount_with_fee,
     728              :                             &issue_amount_with_fee);
     729              :     }
     730           36 :     TALER_ARL_amount_add (&rs->curr_balance.withdraw_fee_balance,
     731              :                           &rs->curr_balance.withdraw_fee_balance,
     732              :                           &issue->fees.withdraw);
     733           36 :     TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_withdraw_fee_revenue),
     734              :                           &TALER_ARL_USE_AB (reserves_withdraw_fee_revenue),
     735              :                           &issue->fees.withdraw);
     736              :   } /* end for all coins in batch */
     737              : 
     738           36 :   if (missing_denom)
     739              :   {
     740              :     /* We do not know the value of the entire batch, so we can check
     741              :        neither the reserve signature nor the total; still debit the
     742              :        reserve by the amount the exchange claims. The row inconsistency
     743              :        reported above states that we could not verify it. */
     744            0 :     TALER_ARL_amount_add (&rs->total_out,
     745              :                           &rs->total_out,
     746              :                           amount_with_fee);
     747            0 :     return GNUNET_OK;
     748              :   }
     749           36 :   if (age_proof_required &&
     750            0 :       (! have_age_mask))
     751              :   {
     752              :     /* Without a denomination we do not know the age mask the reserve
     753              :        signature was made over. */
     754            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     755              :                 "Age-restricted withdraw row %llu has no denominations\n",
     756              :                 (unsigned long long) rowid);
     757            0 :     report_row_inconsistency ("withdraw",
     758              :                               rowid,
     759              :                               "no denomination given for age-restricted withdraw");
     760            0 :     if (global_qs < 0)
     761            0 :       return GNUNET_SYSERR;
     762            0 :     return GNUNET_OK;
     763              :   }
     764              : 
     765              :   /* check reserve_sig (first: setup remaining members of wsrd) */
     766           36 :   if (GNUNET_OK !=
     767           36 :       TALER_wallet_withdraw_verify (
     768              :         &auditor_amount,
     769              :         &auditor_fee,
     770              :         h_planchets,
     771              :         blinding_seed,
     772              :         age_proof_required
     773              :         ? &age_mask
     774              :         : NULL,
     775              :         age_proof_required
     776              :         ? max_age
     777              :         : 0,
     778              :         reserve_pub,
     779              :         reserve_sig))
     780              :   {
     781            0 :     struct TALER_AUDITORDB_BadSigLosses bsl = {
     782              :       .problem_row_id = rowid,
     783              :       .operation = (char *) "withdraw",
     784              :       .loss = *amount_with_fee,
     785              :       .operation_specific_pub = reserve_pub->eddsa_pub
     786              :     };
     787              : 
     788            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     789              :                 "Withdraw signature invalid (row #%llu)\n",
     790              :                 (unsigned long long) rowid);
     791            0 :     qs = TALER_AUDITORDB_insert_bad_sig_losses (
     792              :       TALER_ARL_adb,
     793              :       &bsl);
     794            0 :     if (qs < 0)
     795              :     {
     796            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     797            0 :       rc->qs = qs;
     798            0 :       return GNUNET_SYSERR;
     799              :     }
     800            0 :     TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_total_bad_sig_loss),
     801              :                           &TALER_ARL_USE_AB (reserves_total_bad_sig_loss),
     802              :                           amount_with_fee);
     803            0 :     return GNUNET_OK;     /* exit function here, we cannot add this to the legitimate withdrawals */
     804              :   }
     805              : 
     806           36 :   if (0 !=
     807           36 :       TALER_amount_cmp (&auditor_amount_with_fee,
     808              :                         amount_with_fee))
     809              :   {
     810            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     811              :                 "Withdraw fee inconsistent (row #%llu)\n",
     812              :                 (unsigned long long) rowid);
     813            0 :     report_row_inconsistency ("withdraw",
     814              :                               rowid,
     815              :                               "amount with fee from exchange does not match denomination value plus fee");
     816            0 :     if (global_qs < 0)
     817              :     {
     818            0 :       GNUNET_break (0);
     819            0 :       return GNUNET_SYSERR;
     820              :     }
     821              :   }
     822           36 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     823              :               "Reserve `%s' reduced by %s from withdraw\n",
     824              :               TALER_B2S (reserve_pub),
     825              :               TALER_amount2s (&auditor_amount_with_fee));
     826           36 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     827              :               "Increasing withdraw profits by fee %s\n",
     828              :               TALER_amount2s (&auditor_fee));
     829           36 :   TALER_ARL_amount_add (&rs->total_out,
     830              :                         &rs->total_out,
     831              :                         &auditor_amount_with_fee);
     832           36 :   return GNUNET_OK;
     833              : }
     834              : 
     835              : 
     836              : /**
     837              :  * Function called with details about withdraw operations.  Verifies
     838              :  * the signature and updates the reserve's balance.
     839              :  *
     840              :  * @param rc our `struct ReserveContext`
     841              :  * @param rowid unique serial ID for the refresh session in our DB
     842              :  * @param timestamp when did we receive the recoup request
     843              :  * @param amount how much should be added back to the reserve
     844              :  * @param reserve_pub public key of the reserve
     845              :  * @param coin public information about the coin, denomination signature is
     846              :  *        already verified in #check_recoup()
     847              :  * @param denom_pub public key of the denomionation of @a coin
     848              :  * @param coin_sig signature with @e coin_pub of type #TALER_SIGNATURE_WALLET_COIN_RECOUP
     849              :  * @param coin_blind blinding factor used to blind the coin
     850              :  * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
     851              :  */
     852              : static enum GNUNET_GenericReturnValue
     853            4 : handle_recoup_by_reserve (
     854              :   struct ReserveContext *rc,
     855              :   uint64_t rowid,
     856              :   struct GNUNET_TIME_Timestamp timestamp,
     857              :   const struct TALER_Amount *amount,
     858              :   const struct TALER_ReservePublicKeyP *reserve_pub,
     859              :   const struct TALER_CoinPublicInfo *coin,
     860              :   const struct TALER_DenominationPublicKey *denom_pub,
     861              :   const struct TALER_CoinSpendSignatureP *coin_sig,
     862              :   const union GNUNET_CRYPTO_BlindingSecretP *coin_blind)
     863              : {
     864              :   struct ReserveSummary *rs;
     865              :   struct GNUNET_TIME_Timestamp expiry;
     866              :   struct TALER_MasterSignatureP msig;
     867              :   uint64_t rev_rowid;
     868              :   enum GNUNET_DB_QueryStatus qs;
     869              :   const char *rev;
     870              : 
     871              :   (void) denom_pub;
     872              :   /* should be monotonically increasing */
     873            4 :   GNUNET_assert (rowid >= TALER_ARL_USE_PP (reserves_reserve_recoup_serial_id));
     874            4 :   TALER_ARL_USE_PP (reserves_reserve_recoup_serial_id) = rowid + 1;
     875              :   /* We know that denom_pub matches denom_pub_hash because this
     876              :      is how the SQL statement joined the tables. */
     877            4 :   if (GNUNET_OK !=
     878            4 :       TALER_wallet_recoup_verify (&coin->denom_pub_hash,
     879              :                                   coin_blind,
     880              :                                   &coin->coin_pub,
     881              :                                   coin_sig))
     882              :   {
     883            0 :     struct TALER_AUDITORDB_BadSigLosses bslr = {
     884              :       .problem_row_id = rowid,
     885              :       .operation = (char *) "recoup",
     886              :       .loss = *amount,
     887              :       .operation_specific_pub = coin->coin_pub.eddsa_pub
     888              :     };
     889              : 
     890            0 :     qs = TALER_AUDITORDB_insert_bad_sig_losses (
     891              :       TALER_ARL_adb,
     892              :       &bslr);
     893              : 
     894            0 :     if (qs < 0)
     895              :     {
     896            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     897            0 :       rc->qs = qs;
     898            0 :       return GNUNET_SYSERR;
     899              :     }
     900            0 :     TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_total_bad_sig_loss),
     901              :                           &TALER_ARL_USE_AB (reserves_total_bad_sig_loss),
     902              :                           amount);
     903              :     /* We continue here intentionally to credit the reserve anyway,
     904              :        we already accounted for the loss, so we need to continue to
     905              :        avoid getting another error when the reserve is drained which
     906              :        would double-count the damage. */
     907              :   }
     908              : 
     909              :   /* check that the coin was eligible for recoup!*/
     910            4 :   rev = GNUNET_CONTAINER_multihashmap_get (rc->revoked,
     911              :                                            &coin->denom_pub_hash.hash);
     912            4 :   if (NULL == rev)
     913              :   {
     914            4 :     qs = TALER_EXCHANGEDB_get_denomination_revocation (TALER_ARL_edb,
     915              :                                                        &coin->denom_pub_hash,
     916              :                                                        &msig,
     917              :                                                        &rev_rowid);
     918            4 :     if (0 > qs)
     919              :     {
     920            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     921            0 :       rc->qs = qs;
     922            0 :       return GNUNET_SYSERR;
     923              :     }
     924            4 :     if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
     925              :     {
     926            0 :       report_row_inconsistency ("recoup",
     927              :                                 rowid,
     928              :                                 "denomination key not in revocation set");
     929            0 :       if (global_qs < 0)
     930            0 :         return GNUNET_SYSERR;
     931            0 :       TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_reserve_loss),
     932              :                             &TALER_ARL_USE_AB (reserves_reserve_loss),
     933              :                             amount);
     934              :     }
     935              :     else
     936              :     {
     937            4 :       if (GNUNET_OK !=
     938            4 :           TALER_exchange_offline_denomination_revoke_verify (
     939              :             &coin->denom_pub_hash,
     940              :             &TALER_ARL_master_pub,
     941              :             &msig))
     942              :       {
     943            0 :         rev = "master signature invalid";
     944              :       }
     945              :       else
     946              :       {
     947            4 :         rev = "revoked";
     948              :       }
     949            4 :       GNUNET_assert (
     950              :         GNUNET_OK ==
     951              :         GNUNET_CONTAINER_multihashmap_put (
     952              :           rc->revoked,
     953              :           &coin->denom_pub_hash.hash,
     954              :           (void *) rev,
     955              :           GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
     956              :     }
     957              :   }
     958              :   else
     959              :   {
     960            0 :     rev_rowid = 0;   /* reported elsewhere */
     961              :   }
     962            4 :   if ((NULL != rev) &&
     963            4 :       (0 == strcmp (rev,
     964              :                     "master signature invalid")))
     965              :   {
     966            0 :     struct TALER_AUDITORDB_BadSigLosses bslrm = {
     967              :       .problem_row_id = rev_rowid,
     968              :       .operation = (char *) "recoup-master",
     969              :       .loss = *amount,
     970              :       .operation_specific_pub = TALER_ARL_master_pub.eddsa_pub
     971              :     };
     972              : 
     973            0 :     qs = TALER_AUDITORDB_insert_bad_sig_losses (
     974              :       TALER_ARL_adb,
     975              :       &bslrm);
     976              : 
     977            0 :     if (qs < 0)
     978              :     {
     979            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     980            0 :       rc->qs = qs;
     981            0 :       return GNUNET_SYSERR;
     982              :     }
     983            0 :     TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_total_bad_sig_loss),
     984              :                           &TALER_ARL_USE_AB (reserves_total_bad_sig_loss),
     985              :                           amount);
     986              :   }
     987              : 
     988            4 :   rs = setup_reserve (rc,
     989              :                       reserve_pub);
     990            4 :   if (NULL == rs)
     991              :   {
     992            0 :     GNUNET_break (0);
     993            0 :     return GNUNET_SYSERR;
     994              :   }
     995            4 :   TALER_ARL_amount_add (&rs->total_in,
     996              :                         &rs->total_in,
     997              :                         amount);
     998            4 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     999              :               "Additional /recoup value to for reserve `%s' of %s\n",
    1000              :               TALER_B2S (reserve_pub),
    1001              :               TALER_amount2s (amount));
    1002            4 :   expiry = GNUNET_TIME_absolute_to_timestamp (
    1003              :     GNUNET_TIME_absolute_add (timestamp.abs_time,
    1004              :                               idle_reserve_expiration_time));
    1005            4 :   rs->a_expiration_date = GNUNET_TIME_timestamp_max (rs->a_expiration_date,
    1006              :                                                      expiry);
    1007            4 :   return GNUNET_OK;
    1008              : }
    1009              : 
    1010              : 
    1011              : /**
    1012              :  * Obtain the closing fee for a transfer at @a time for target
    1013              :  * @a receiver_account.
    1014              :  *
    1015              :  * @param receiver_account payto:// URI of the target account
    1016              :  * @param atime when was the transfer made
    1017              :  * @param[out] fee set to the closing fee
    1018              :  * @return #GNUNET_OK on success
    1019              :  */
    1020              : static enum GNUNET_GenericReturnValue
    1021            2 : get_closing_fee (const struct TALER_FullPayto receiver_account,
    1022              :                  struct GNUNET_TIME_Timestamp atime,
    1023              :                  struct TALER_Amount *fee)
    1024              : {
    1025              :   struct TALER_MasterSignatureP master_sig;
    1026              :   struct GNUNET_TIME_Timestamp start_date;
    1027              :   struct GNUNET_TIME_Timestamp end_date;
    1028              :   struct TALER_WireFeeSet fees;
    1029              :   char *method;
    1030              :   uint64_t rowid;
    1031              : 
    1032            2 :   method = TALER_payto_get_method (receiver_account.full_payto);
    1033            2 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1034              :               "Method is `%s'\n",
    1035              :               method);
    1036            2 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    1037            2 :       TALER_EXCHANGEDB_get_wire_fee (TALER_ARL_edb,
    1038              :                                      method,
    1039              :                                      atime,
    1040              :                                      &rowid,
    1041              :                                      &start_date,
    1042              :                                      &end_date,
    1043              :                                      &fees,
    1044              :                                      &master_sig))
    1045              :   {
    1046              :     char *diag;
    1047              : 
    1048            0 :     GNUNET_asprintf (&diag,
    1049              :                      "closing fee for `%s' unavailable at %s\n",
    1050              :                      method,
    1051              :                      GNUNET_TIME_timestamp2s (atime));
    1052            0 :     report_row_inconsistency ("closing-fee",
    1053              :                               rowid,
    1054              :                               diag);
    1055            0 :     GNUNET_free (diag);
    1056            0 :     GNUNET_free (method);
    1057            0 :     return GNUNET_SYSERR;
    1058              :   }
    1059            2 :   *fee = fees.closing;
    1060            2 :   GNUNET_free (method);
    1061            2 :   return GNUNET_OK;
    1062              : }
    1063              : 
    1064              : 
    1065              : /**
    1066              :  * Function called about reserve opening operations.
    1067              :  *
    1068              :  * @param rc closure
    1069              :  * @param rowid row identifier used to uniquely identify the reserve closing operation
    1070              :  * @param reserve_payment how much to pay from the
    1071              :  *        reserve's own balance for opening the reserve
    1072              :  * @param request_timestamp when was the request created
    1073              :  * @param reserve_expiration desired expiration time for the reserve
    1074              :  * @param purse_limit minimum number of purses the client
    1075              :  *       wants to have concurrently open for this reserve
    1076              :  * @param reserve_pub public key of the reserve
    1077              :  * @param reserve_sig signature affirming the operation
    1078              :  * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
    1079              :  */
    1080              : static enum GNUNET_GenericReturnValue
    1081            0 : handle_reserve_open (
    1082              :   struct ReserveContext *rc,
    1083              :   uint64_t rowid,
    1084              :   const struct TALER_Amount *reserve_payment,
    1085              :   struct GNUNET_TIME_Timestamp request_timestamp,
    1086              :   struct GNUNET_TIME_Timestamp reserve_expiration,
    1087              :   uint32_t purse_limit,
    1088              :   const struct TALER_ReservePublicKeyP *reserve_pub,
    1089              :   const struct TALER_ReserveSignatureP *reserve_sig)
    1090              : {
    1091              :   struct ReserveSummary *rs;
    1092              :   enum GNUNET_DB_QueryStatus qs;
    1093              : 
    1094              :   /* should be monotonically increasing */
    1095            0 :   GNUNET_assert (rowid >= TALER_ARL_USE_PP (reserves_reserve_open_serial_id));
    1096            0 :   TALER_ARL_USE_PP (reserves_reserve_open_serial_id) = rowid + 1;
    1097              : 
    1098            0 :   rs = setup_reserve (rc,
    1099              :                       reserve_pub);
    1100            0 :   if (NULL == rs)
    1101              :   {
    1102            0 :     GNUNET_break (0);
    1103            0 :     return GNUNET_SYSERR;
    1104              :   }
    1105            0 :   if (GNUNET_OK !=
    1106            0 :       TALER_wallet_reserve_open_verify (reserve_payment,
    1107              :                                         request_timestamp,
    1108              :                                         reserve_expiration,
    1109              :                                         purse_limit,
    1110              :                                         reserve_pub,
    1111              :                                         reserve_sig))
    1112              :   {
    1113            0 :     struct TALER_AUDITORDB_BadSigLosses bsl = {
    1114              :       .problem_row_id = rowid,
    1115              :       .operation = (char *) "reserve-open",
    1116              :       .loss = *reserve_payment,
    1117              :       .operation_specific_pub = reserve_pub->eddsa_pub
    1118              :     };
    1119              : 
    1120            0 :     qs = TALER_AUDITORDB_insert_bad_sig_losses (
    1121              :       TALER_ARL_adb,
    1122              :       &bsl);
    1123              : 
    1124            0 :     if (qs < 0)
    1125              :     {
    1126            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1127            0 :       rc->qs = qs;
    1128            0 :       return GNUNET_SYSERR;
    1129              :     }
    1130            0 :     TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_total_bad_sig_loss),
    1131              :                           &TALER_ARL_USE_AB (reserves_total_bad_sig_loss),
    1132              :                           reserve_payment);
    1133            0 :     return GNUNET_OK;
    1134              :   }
    1135            0 :   TALER_ARL_amount_add (&rs->curr_balance.open_fee_balance,
    1136              :                         &rs->curr_balance.open_fee_balance,
    1137              :                         reserve_payment);
    1138            0 :   TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_open_fee_revenue),
    1139              :                         &TALER_ARL_USE_AB (reserves_open_fee_revenue),
    1140              :                         reserve_payment);
    1141            0 :   TALER_ARL_amount_add (&rs->total_out,
    1142              :                         &rs->total_out,
    1143              :                         reserve_payment);
    1144            0 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1145              :               "Additional open operation for reserve `%s' of %s\n",
    1146              :               TALER_B2S (reserve_pub),
    1147              :               TALER_amount2s (reserve_payment));
    1148            0 :   return GNUNET_OK;
    1149              : }
    1150              : 
    1151              : 
    1152              : /**
    1153              :  * Function called about reserve closing operations
    1154              :  * the aggregator triggered.
    1155              :  *
    1156              :  * @param rc closure
    1157              :  * @param rowid row identifier used to uniquely identify the reserve closing operation
    1158              :  * @param execution_date when did we execute the close operation
    1159              :  * @param amount_with_fee how much did we debit the reserve
    1160              :  * @param closing_fee how much did we charge for closing the reserve
    1161              :  * @param reserve_pub public key of the reserve
    1162              :  * @param receiver_account where did we send the funds
    1163              :  * @param transfer_details details about the wire transfer
    1164              :  * @param close_request_row which close request triggered the operation?
    1165              :  *         0 if it was a timeout
    1166              :  * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
    1167              :  */
    1168              : static enum GNUNET_GenericReturnValue
    1169            2 : handle_reserve_closed (
    1170              :   struct ReserveContext *rc,
    1171              :   uint64_t rowid,
    1172              :   struct GNUNET_TIME_Timestamp execution_date,
    1173              :   const struct TALER_Amount *amount_with_fee,
    1174              :   const struct TALER_Amount *closing_fee,
    1175              :   const struct TALER_ReservePublicKeyP *reserve_pub,
    1176              :   const struct TALER_FullPayto receiver_account,
    1177              :   const struct TALER_WireTransferIdentifierRawP *transfer_details,
    1178              :   uint64_t close_request_row)
    1179              : {
    1180              :   struct ReserveSummary *rs;
    1181              : 
    1182              :   (void) transfer_details;
    1183              :   /* should be monotonically increasing */
    1184            2 :   GNUNET_assert (rowid >= TALER_ARL_USE_PP (reserves_reserve_close_serial_id));
    1185            2 :   TALER_ARL_USE_PP (reserves_reserve_close_serial_id) = rowid + 1;
    1186              : 
    1187            2 :   rs = setup_reserve (rc,
    1188              :                       reserve_pub);
    1189            2 :   if (NULL == rs)
    1190              :   {
    1191            0 :     GNUNET_break (0);
    1192            0 :     return GNUNET_SYSERR;
    1193              :   }
    1194              :   {
    1195              :     struct TALER_Amount expected_fee;
    1196              : 
    1197              :     /* verify closing_fee is correct! */
    1198            2 :     if (GNUNET_OK !=
    1199            2 :         get_closing_fee (receiver_account,
    1200              :                          execution_date,
    1201              :                          &expected_fee))
    1202              :     {
    1203            0 :       GNUNET_break (0);
    1204              :     }
    1205            2 :     else if (0 != TALER_amount_cmp (&expected_fee,
    1206              :                                     closing_fee))
    1207              :     {
    1208            0 :       report_amount_arithmetic_inconsistency (
    1209              :         "closing aggregation fee",
    1210              :         rowid,
    1211              :         closing_fee,
    1212              :         &expected_fee,
    1213              :         1);
    1214            0 :       if (global_qs < 0)
    1215            0 :         return GNUNET_SYSERR;
    1216              :     }
    1217              :   }
    1218              : 
    1219            2 :   TALER_ARL_amount_add (&rs->curr_balance.close_fee_balance,
    1220              :                         &rs->curr_balance.close_fee_balance,
    1221              :                         closing_fee);
    1222            2 :   TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_close_fee_revenue),
    1223              :                         &TALER_ARL_USE_AB (reserves_close_fee_revenue),
    1224              :                         closing_fee);
    1225            2 :   TALER_ARL_amount_add (&rs->total_out,
    1226              :                         &rs->total_out,
    1227              :                         amount_with_fee);
    1228            2 :   if (0 != close_request_row)
    1229              :   {
    1230              :     struct TALER_ReserveSignatureP reserve_sig;
    1231              :     struct GNUNET_TIME_Timestamp request_timestamp;
    1232              :     struct TALER_Amount close_balance;
    1233              :     struct TALER_Amount close_fee;
    1234            0 :     struct TALER_FullPayto payto_uri = { NULL };
    1235              :     enum GNUNET_DB_QueryStatus qs;
    1236              : 
    1237            0 :     qs = TALER_EXCHANGEDB_get_reserve_close_request_info (
    1238              :       TALER_ARL_edb,
    1239              :       reserve_pub,
    1240              :       close_request_row,
    1241              :       &reserve_sig,
    1242              :       &request_timestamp,
    1243              :       &close_balance,
    1244              :       &close_fee,
    1245              :       &payto_uri);
    1246            0 :     if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
    1247              :     {
    1248            0 :       report_row_inconsistency ("reserves_close",
    1249              :                                 rowid,
    1250              :                                 "reserve close request unknown");
    1251            0 :       if (global_qs < 0)
    1252            0 :         return GNUNET_SYSERR;
    1253              :     }
    1254              :     else
    1255              :     {
    1256              :       struct TALER_FullPaytoHashP h_payto;
    1257              : 
    1258            0 :       TALER_full_payto_hash (payto_uri,
    1259              :                              &h_payto);
    1260            0 :       if (GNUNET_OK !=
    1261            0 :           TALER_wallet_reserve_close_verify (
    1262              :             request_timestamp,
    1263              :             &h_payto,
    1264              :             reserve_pub,
    1265              :             &reserve_sig))
    1266              :       {
    1267            0 :         struct TALER_AUDITORDB_BadSigLosses bsl = {
    1268              :           .problem_row_id = close_request_row,
    1269              :           .operation = (char *) "close-request",
    1270              :           .loss = *amount_with_fee,
    1271              :           .operation_specific_pub = reserve_pub->eddsa_pub
    1272              :         };
    1273              : 
    1274            0 :         qs = TALER_AUDITORDB_insert_bad_sig_losses (
    1275              :           TALER_ARL_adb,
    1276              :           &bsl);
    1277              : 
    1278            0 :         if (qs < 0)
    1279              :         {
    1280            0 :           GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1281            0 :           rc->qs = qs;
    1282            0 :           GNUNET_free (payto_uri.full_payto);
    1283            0 :           return GNUNET_SYSERR;
    1284              :         }
    1285            0 :         TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_total_bad_sig_loss),
    1286              :                               &TALER_ARL_USE_AB (reserves_total_bad_sig_loss),
    1287              :                               amount_with_fee);
    1288              :       }
    1289              :     }
    1290            0 :     if ( (NULL == payto_uri.full_payto) &&
    1291            0 :          (NULL == rs->sender_account.full_payto) )
    1292              :     {
    1293            0 :       GNUNET_break (! rs->had_ri);
    1294            0 :       report_row_inconsistency ("reserves_close",
    1295              :                                 rowid,
    1296              :                                 "target account not verified, auditor does not know reserve");
    1297            0 :       if (global_qs < 0)
    1298            0 :         return GNUNET_SYSERR;
    1299              :     }
    1300            0 :     if (NULL == payto_uri.full_payto)
    1301              :     {
    1302            0 :       if ((NULL == rs->sender_account.full_payto) ||
    1303            0 :           (0 != TALER_full_payto_cmp (rs->sender_account,
    1304              :                                       receiver_account)))
    1305              :       {
    1306            0 :         report_row_inconsistency ("reserves_close",
    1307              :                                   rowid,
    1308              :                                   "target account does not match origin account");
    1309            0 :         if (global_qs < 0)
    1310            0 :           return GNUNET_SYSERR;
    1311              :       }
    1312              :     }
    1313              :     else
    1314              :     {
    1315            0 :       if (0 != TALER_full_payto_cmp (payto_uri,
    1316              :                                      receiver_account))
    1317              :       {
    1318            0 :         report_row_inconsistency ("reserves_close",
    1319              :                                   rowid,
    1320              :                                   "target account does not match origin account");
    1321            0 :         if (global_qs < 0)
    1322              :         {
    1323            0 :           GNUNET_free (payto_uri.full_payto);
    1324            0 :           return GNUNET_SYSERR;
    1325              :         }
    1326              :       }
    1327              :     }
    1328            0 :     GNUNET_free (payto_uri.full_payto);
    1329              :   }
    1330              :   else
    1331              :   {
    1332            2 :     if (NULL == rs->sender_account.full_payto)
    1333              :     {
    1334            0 :       GNUNET_break (! rs->had_ri);
    1335            0 :       report_row_inconsistency ("reserves_close",
    1336              :                                 rowid,
    1337              :                                 "target account not verified, auditor does not know reserve");
    1338            0 :       if (global_qs < 0)
    1339            0 :         return GNUNET_SYSERR;
    1340              :     }
    1341            2 :     else if (0 != TALER_full_payto_cmp (rs->sender_account,
    1342              :                                         receiver_account))
    1343              :     {
    1344            0 :       report_row_inconsistency ("reserves_close",
    1345              :                                 rowid,
    1346              :                                 "target account does not match origin account");
    1347            0 :       if (global_qs < 0)
    1348            0 :         return GNUNET_SYSERR;
    1349              :     }
    1350              :   }
    1351              : 
    1352            2 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1353              :               "Additional closing operation for reserve `%s' of %s\n",
    1354              :               TALER_B2S (reserve_pub),
    1355              :               TALER_amount2s (amount_with_fee));
    1356            2 :   return GNUNET_OK;
    1357              : }
    1358              : 
    1359              : 
    1360              : /**
    1361              :  * Function called with details about account merge requests that have been
    1362              :  * made, with the goal of accounting for the merge fee paid by the reserve (if
    1363              :  * applicable).
    1364              :  *
    1365              :  * @param rc closure
    1366              :  * @param rowid unique serial ID for the deposit in our DB
    1367              :  * @param reserve_pub reserve affected by the merge
    1368              :  * @param purse_pub purse being merged
    1369              :  * @param h_contract_terms hash over contract of the purse
    1370              :  * @param purse_expiration when would the purse expire
    1371              :  * @param amount total amount in the purse
    1372              :  * @param min_age minimum age of all coins deposited into the purse
    1373              :  * @param flags how was the purse created
    1374              :  * @param purse_fee if a purse fee was paid, how high is it
    1375              :  * @param merge_timestamp when was the merge approved
    1376              :  * @param reserve_sig signature by reserve approving the merge
    1377              :  * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
    1378              :  */
    1379              : static enum GNUNET_GenericReturnValue
    1380            0 : handle_account_merged (
    1381              :   struct ReserveContext *rc,
    1382              :   uint64_t rowid,
    1383              :   const struct TALER_ReservePublicKeyP *reserve_pub,
    1384              :   const struct TALER_PurseContractPublicKeyP *purse_pub,
    1385              :   const struct TALER_PrivateContractHashP *h_contract_terms,
    1386              :   struct GNUNET_TIME_Timestamp purse_expiration,
    1387              :   const struct TALER_Amount *amount,
    1388              :   uint32_t min_age,
    1389              :   enum TALER_WalletAccountMergeFlags flags,
    1390              :   const struct TALER_Amount *purse_fee,
    1391              :   struct GNUNET_TIME_Timestamp merge_timestamp,
    1392              :   const struct TALER_ReserveSignatureP *reserve_sig)
    1393              : {
    1394              :   struct ReserveSummary *rs;
    1395              :   enum GNUNET_DB_QueryStatus qs;
    1396              : 
    1397              :   /* should be monotonically increasing */
    1398            0 :   GNUNET_assert (rowid >= TALER_ARL_USE_PP (reserves_account_merges_serial_id));
    1399            0 :   TALER_ARL_USE_PP (reserves_account_merges_serial_id) = rowid + 1;
    1400            0 :   if (GNUNET_OK !=
    1401            0 :       TALER_wallet_account_merge_verify (merge_timestamp,
    1402              :                                          purse_pub,
    1403              :                                          purse_expiration,
    1404              :                                          h_contract_terms,
    1405              :                                          amount,
    1406              :                                          purse_fee,
    1407              :                                          min_age,
    1408              :                                          flags,
    1409              :                                          reserve_pub,
    1410              :                                          reserve_sig))
    1411              :   {
    1412            0 :     struct TALER_AUDITORDB_BadSigLosses bsl = {
    1413              :       .problem_row_id = rowid,
    1414              :       .operation = (char *) "account-merge",
    1415              :       .loss = *purse_fee,
    1416              :       .operation_specific_pub = reserve_pub->eddsa_pub
    1417              :     };
    1418              : 
    1419            0 :     qs = TALER_AUDITORDB_insert_bad_sig_losses (
    1420              :       TALER_ARL_adb,
    1421              :       &bsl);
    1422            0 :     if (qs < 0)
    1423              :     {
    1424            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1425            0 :       rc->qs = qs;
    1426            0 :       return GNUNET_SYSERR;
    1427              :     }
    1428            0 :     TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_total_bad_sig_loss),
    1429              :                           &TALER_ARL_USE_AB (reserves_total_bad_sig_loss),
    1430              :                           purse_fee);
    1431            0 :     return GNUNET_OK;
    1432              :   }
    1433            0 :   if ((flags & TALER_WAMF_MERGE_MODE_MASK) !=
    1434              :       TALER_WAMF_MODE_CREATE_WITH_PURSE_FEE)
    1435            0 :     return GNUNET_OK; /* no impact on reserve balance */
    1436            0 :   rs = setup_reserve (rc,
    1437              :                       reserve_pub);
    1438            0 :   if (NULL == rs)
    1439              :   {
    1440            0 :     GNUNET_break (0);
    1441            0 :     return GNUNET_SYSERR;
    1442              :   }
    1443            0 :   TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_purse_fee_revenue),
    1444              :                         &TALER_ARL_USE_AB (reserves_purse_fee_revenue),
    1445              :                         purse_fee);
    1446            0 :   TALER_ARL_amount_add (&rs->curr_balance.purse_fee_balance,
    1447              :                         &rs->curr_balance.purse_fee_balance,
    1448              :                         purse_fee);
    1449            0 :   TALER_ARL_amount_add (&rs->total_out,
    1450              :                         &rs->total_out,
    1451              :                         purse_fee);
    1452            0 :   return GNUNET_OK;
    1453              : }
    1454              : 
    1455              : 
    1456              : /**
    1457              :  * Function called with details about a purse that was merged into an account.
    1458              :  * Only updates the reserve balance, the actual verifications are done in the
    1459              :  * purse helper.
    1460              :  *
    1461              :  * @param rc closure
    1462              :  * @param rowid unique serial ID for the refund in our DB
    1463              :  * @param purse_pub public key of the purse
    1464              :  * @param reserve_pub which reserve is the purse credited to
    1465              :  * @param purse_value what is the target value of the purse
    1466              :  * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
    1467              :  */
    1468              : static enum GNUNET_GenericReturnValue
    1469            0 : purse_decision_cb (struct ReserveContext *rc,
    1470              :                    uint64_t rowid,
    1471              :                    const struct TALER_PurseContractPublicKeyP *purse_pub,
    1472              :                    const struct TALER_ReservePublicKeyP *reserve_pub,
    1473              :                    const struct TALER_Amount *purse_value)
    1474              : {
    1475              :   struct ReserveSummary *rs;
    1476              : 
    1477            0 :   GNUNET_assert (rowid >= TALER_ARL_USE_PP (
    1478              :                    reserves_purse_decisions_serial_id)); /* should be monotonically increasing */
    1479            0 :   TALER_ARL_USE_PP (reserves_purse_decisions_serial_id) = rowid + 1;
    1480            0 :   rs = setup_reserve (rc,
    1481              :                       reserve_pub);
    1482            0 :   if (NULL == rs)
    1483              :   {
    1484            0 :     GNUNET_break (0);
    1485            0 :     return GNUNET_SYSERR;
    1486              :   }
    1487            0 :   TALER_ARL_amount_add (&rs->total_in,
    1488              :                         &rs->total_in,
    1489              :                         purse_value);
    1490            0 :   return GNUNET_OK;
    1491              : }
    1492              : 
    1493              : 
    1494              : /**
    1495              :  * Check that the reserve summary matches what the exchange database
    1496              :  * thinks about the reserve, and update our own state of the reserve.
    1497              :  *
    1498              :  * Remove all reserves that we are happy with from the DB.
    1499              :  *
    1500              :  * @param cls our `struct ReserveContext`
    1501              :  * @param key hash of the reserve public key
    1502              :  * @param value a `struct ReserveSummary`
    1503              :  * @return #GNUNET_OK to process more entries
    1504              :  */
    1505              : static enum GNUNET_GenericReturnValue
    1506           16 : verify_reserve_balance (void *cls,
    1507              :                         const struct GNUNET_HashCode *key,
    1508              :                         void *value)
    1509              : {
    1510           16 :   struct ReserveContext *rc = cls;
    1511           16 :   struct ReserveSummary *rs = value;
    1512              :   struct TALER_Amount mbalance;
    1513              :   struct TALER_Amount nbalance;
    1514              :   enum GNUNET_DB_QueryStatus qs;
    1515              :   enum GNUNET_GenericReturnValue ret;
    1516              : 
    1517           16 :   ret = GNUNET_OK;
    1518              :   /* Check our reserve summary balance calculation shows that
    1519              :      the reserve balance is acceptable (i.e. non-negative) */
    1520           16 :   TALER_ARL_amount_add (&mbalance,
    1521              :                         &rs->total_in,
    1522              :                         &rs->prev_balance.reserve_balance);
    1523           16 :   if (TALER_ARL_SR_INVALID_NEGATIVE ==
    1524           16 :       TALER_ARL_amount_subtract_neg (&nbalance,
    1525              :                                      &mbalance,
    1526              :                                      &rs->total_out))
    1527              :   {
    1528            0 :     struct TALER_AUDITORDB_ReserveBalanceInsufficientInconsistency rbiil = {
    1529              :       .reserve_pub = rs->reserve_pub.eddsa_pub,
    1530              :       .inconsistency_gain = false
    1531              :     };
    1532              : 
    1533            0 :     TALER_ARL_amount_subtract (&rbiil.inconsistency_amount,
    1534              :                                &rs->total_out,
    1535              :                                &mbalance);
    1536            0 :     TALER_ARL_amount_add (&rs->curr_balance.reserve_loss,
    1537              :                           &rs->prev_balance.reserve_loss,
    1538              :                           &rbiil.inconsistency_amount);
    1539            0 :     TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_reserve_loss),
    1540              :                           &TALER_ARL_USE_AB (reserves_reserve_loss),
    1541              :                           &rbiil.inconsistency_amount);
    1542            0 :     qs = TALER_AUDITORDB_insert_reserve_balance_insufficient_inconsistency (
    1543              :       TALER_ARL_adb,
    1544              :       &rbiil);
    1545              : 
    1546            0 :     if (qs < 0)
    1547              :     {
    1548            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1549            0 :       rc->qs = qs;
    1550            0 :       return GNUNET_SYSERR;
    1551              :     }
    1552              :     /* Continue with a reserve balance of zero */
    1553            0 :     GNUNET_assert (GNUNET_OK ==
    1554              :                    TALER_amount_set_zero (TALER_ARL_currency,
    1555              :                                           &rs->curr_balance.reserve_balance));
    1556            0 :     nbalance = rs->curr_balance.reserve_balance;
    1557              :   }
    1558              :   else
    1559              :   {
    1560              :     /* Update remaining reserve balance! */
    1561           16 :     rs->curr_balance.reserve_balance = nbalance;
    1562              :   }
    1563              : 
    1564           16 :   if (internal_checks)
    1565              :   {
    1566              :     /* Now check OUR balance calculation vs. the one the exchange has
    1567              :        in its database. This can only be done when we are doing an
    1568              :        internal audit, as otherwise the balance of the 'reserves' table
    1569              :        is not replicated at the auditor. */
    1570           16 :     struct TALER_EXCHANGEDB_Reserve reserve = {
    1571              :       .pub = rs->reserve_pub
    1572              :     };
    1573              : 
    1574           16 :     qs = TALER_EXCHANGEDB_get_reserve (TALER_ARL_edb,
    1575              :                                        &reserve);
    1576           16 :     if (0 > qs)
    1577              :     {
    1578            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1579            0 :       rc->qs = qs;
    1580            0 :       return GNUNET_SYSERR;
    1581              :     }
    1582           16 :     if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    1583              :     {
    1584              :       /* If the exchange doesn't have this reserve in the summary, it
    1585              :          is like the exchange 'lost' that amount from its records,
    1586              :          making an illegitimate gain over the amount it dropped.
    1587              :          We don't add the amount to some total simply because it is
    1588              :          not an actualized gain and could be trivially corrected by
    1589              :          restoring the summary. */
    1590            0 :       struct TALER_AUDITORDB_ReserveBalanceInsufficientInconsistency rbiig = {
    1591              :         .reserve_pub = rs->reserve_pub.eddsa_pub,
    1592              :         .inconsistency_amount = nbalance,
    1593              :         .inconsistency_gain = true
    1594              :       };
    1595              : 
    1596            0 :       qs = TALER_AUDITORDB_insert_reserve_balance_insufficient_inconsistency (
    1597              :         TALER_ARL_adb,
    1598              :         &rbiig);
    1599              : 
    1600            0 :       if (qs < 0)
    1601              :       {
    1602            0 :         GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1603            0 :         rc->qs = qs;
    1604            0 :         return GNUNET_SYSERR;
    1605              :       }
    1606            0 :       if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    1607              :       {
    1608            0 :         GNUNET_break (0);
    1609            0 :         qs = GNUNET_DB_STATUS_HARD_ERROR;
    1610              :       }
    1611            0 :       rc->qs = qs;
    1612              :     }
    1613              :     else
    1614              :     {
    1615              :       /* Check that exchange's balance matches our expected balance for the reserve */
    1616           16 :       if (0 != TALER_amount_cmp (&rs->curr_balance.reserve_balance,
    1617              :                                  &reserve.balance))
    1618              :       {
    1619              :         struct TALER_Amount delta;
    1620              : 
    1621            0 :         if (0 < TALER_amount_cmp (&rs->curr_balance.reserve_balance,
    1622              :                                   &reserve.balance))
    1623              :         {
    1624              :           /* balance > reserve.balance */
    1625            0 :           TALER_ARL_amount_subtract (&delta,
    1626              :                                      &rs->curr_balance.reserve_balance,
    1627              :                                      &reserve.balance);
    1628            0 :           TALER_ARL_amount_add (&TALER_ARL_USE_AB (
    1629              :                                   total_balance_summary_delta_plus),
    1630              :                                 &TALER_ARL_USE_AB (
    1631              :                                   total_balance_summary_delta_plus),
    1632              :                                 &delta);
    1633              :         }
    1634              :         else
    1635              :         {
    1636              :           /* balance < reserve.balance */
    1637            0 :           TALER_ARL_amount_subtract (&delta,
    1638              :                                      &reserve.balance,
    1639              :                                      &rs->curr_balance.reserve_balance);
    1640            0 :           TALER_ARL_amount_add (&TALER_ARL_USE_AB (
    1641              :                                   total_balance_summary_delta_minus),
    1642              :                                 &TALER_ARL_USE_AB (
    1643              :                                   total_balance_summary_delta_minus),
    1644              :                                 &delta);
    1645              :         }
    1646              : 
    1647              :         {
    1648            0 :           struct TALER_AUDITORDB_ReserveBalanceInsufficientInconsistency rbiig =
    1649              :           {
    1650              :             .reserve_pub = rs->reserve_pub.eddsa_pub,
    1651              :             .inconsistency_amount = nbalance,
    1652              :             .inconsistency_gain = true
    1653              :           };
    1654              : 
    1655            0 :           qs = TALER_AUDITORDB_insert_reserve_balance_insufficient_inconsistency
    1656              :                (
    1657              :             TALER_ARL_adb,
    1658              :             &rbiig);
    1659              :         }
    1660            0 :         if (qs < 0)
    1661              :         {
    1662            0 :           GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1663            0 :           rc->qs = qs;
    1664            0 :           return GNUNET_SYSERR;
    1665              :         }
    1666              : 
    1667              :         {
    1668            0 :           struct TALER_AUDITORDB_ReserveBalanceSummaryWrongInconsistency rbswi =
    1669              :           {
    1670              :             .exchange_amount = reserve.balance,
    1671              :             .auditor_amount = rs->curr_balance.reserve_balance,
    1672              :             .reserve_pub = rs->reserve_pub
    1673              :           };
    1674              : 
    1675              :           qs =
    1676            0 :             TALER_AUDITORDB_insert_reserve_balance_summary_wrong_inconsistency
    1677              :             (
    1678              :               TALER_ARL_adb,
    1679              :               &rbswi);
    1680              :         }
    1681            0 :         if (qs < 0)
    1682              :         {
    1683            0 :           GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1684            0 :           rc->qs = qs;
    1685            0 :           return GNUNET_SYSERR;
    1686              :         }
    1687              :       }
    1688              :     }
    1689              :   }   /* end of 'if (internal_checks)' */
    1690              : 
    1691              :   /* Check that reserve is being closed if it is past its expiration date
    1692              :      (and the closing fee would not exceed the remaining balance) */
    1693           16 :   if (GNUNET_TIME_relative_cmp (CLOSING_GRACE_PERIOD,
    1694              :                                 <,
    1695              :                                 GNUNET_TIME_absolute_get_duration (
    1696              :                                   rs->a_expiration_date.abs_time)))
    1697              :   {
    1698              :     /* Reserve is expired */
    1699              :     struct TALER_Amount cfee;
    1700              : 
    1701            0 :     if ( (NULL != rs->sender_account.full_payto) &&
    1702              :          (GNUNET_OK ==
    1703            0 :           get_closing_fee (rs->sender_account,
    1704              :                            rs->a_expiration_date,
    1705              :                            &cfee)) )
    1706              :     {
    1707              :       /* We got the closing fee */
    1708            0 :       if (1 == TALER_amount_cmp (&nbalance,
    1709              :                                  &cfee))
    1710              :       {
    1711            0 :         struct TALER_AUDITORDB_ReserveNotClosedInconsistency rnci = {
    1712              :           .reserve_pub = rs->reserve_pub,
    1713              :           .expiration_time = rs->a_expiration_date.abs_time,
    1714              :           .balance = nbalance,
    1715            0 :           .diagnostic = rs->sender_account.full_payto
    1716              :         };
    1717              : 
    1718              :         /* remaining balance (according to us) exceeds closing fee */
    1719            0 :         TALER_ARL_amount_add (&TALER_ARL_USE_AB (
    1720              :                                 total_balance_reserve_not_closed),
    1721              :                               &TALER_ARL_USE_AB (
    1722              :                                 total_balance_reserve_not_closed),
    1723              :                               &rnci.balance);
    1724            0 :         qs = TALER_AUDITORDB_insert_reserve_not_closed_inconsistency (
    1725              :           TALER_ARL_adb,
    1726              :           &rnci);
    1727            0 :         if (qs < 0)
    1728              :         {
    1729            0 :           GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1730            0 :           rc->qs = qs;
    1731            0 :           return GNUNET_SYSERR;
    1732              :         }
    1733              :       }
    1734              :     }
    1735              :     else
    1736              :     {
    1737              :       /* We failed to determine the closing fee, complain! */
    1738            0 :       struct TALER_AUDITORDB_ReserveNotClosedInconsistency rncid = {
    1739              :         .reserve_pub = rs->reserve_pub,
    1740              :         .balance = nbalance,
    1741              :         .expiration_time = rs->a_expiration_date.abs_time,
    1742              :         .diagnostic = (char *) "could not determine closing fee"
    1743              :       };
    1744              : 
    1745              :       /* Even if we don't know the closing fee, update the
    1746              :          total_balance_reserve_not_closed */
    1747            0 :       TALER_ARL_amount_add (&TALER_ARL_USE_AB (
    1748              :                               total_balance_reserve_not_closed),
    1749              :                             &TALER_ARL_USE_AB (
    1750              :                               total_balance_reserve_not_closed),
    1751              :                             &nbalance);
    1752            0 :       qs = TALER_AUDITORDB_insert_reserve_not_closed_inconsistency (
    1753              :         TALER_ARL_adb,
    1754              :         &rncid);
    1755            0 :       if (qs < 0)
    1756              :       {
    1757            0 :         GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1758            0 :         rc->qs = qs;
    1759            0 :         return GNUNET_SYSERR;
    1760              :       }
    1761              :     }
    1762              :   }
    1763              :   /* We already computed the 'new' balance in 'curr_balance'
    1764              :      to include the previous balance, so this one is just
    1765              :      an assignment, not adding up! */
    1766           16 :   rs->prev_balance.reserve_balance = rs->curr_balance.reserve_balance;
    1767              : 
    1768              :   /* Add up new totals to previous totals  */
    1769           16 :   TALER_ARL_amount_add (&rs->prev_balance.reserve_loss,
    1770              :                         &rs->prev_balance.reserve_loss,
    1771              :                         &rs->curr_balance.reserve_loss);
    1772           16 :   TALER_ARL_amount_add (&rs->prev_balance.withdraw_fee_balance,
    1773              :                         &rs->prev_balance.withdraw_fee_balance,
    1774              :                         &rs->curr_balance.withdraw_fee_balance);
    1775           16 :   TALER_ARL_amount_add (&rs->prev_balance.close_fee_balance,
    1776              :                         &rs->prev_balance.close_fee_balance,
    1777              :                         &rs->curr_balance.close_fee_balance);
    1778           16 :   TALER_ARL_amount_add (&rs->prev_balance.purse_fee_balance,
    1779              :                         &rs->prev_balance.purse_fee_balance,
    1780              :                         &rs->curr_balance.purse_fee_balance);
    1781           16 :   TALER_ARL_amount_add (&rs->prev_balance.open_fee_balance,
    1782              :                         &rs->prev_balance.open_fee_balance,
    1783              :                         &rs->curr_balance.open_fee_balance);
    1784           16 :   TALER_ARL_amount_add (&rs->prev_balance.history_fee_balance,
    1785              :                         &rs->prev_balance.history_fee_balance,
    1786              :                         &rs->curr_balance.history_fee_balance);
    1787              :   /* Update global balance: add incoming first, then try
    1788              :      to subtract outgoing... */
    1789           16 :   TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_reserve_total_balance),
    1790              :                         &TALER_ARL_USE_AB (reserves_reserve_total_balance),
    1791              :                         &rs->total_in);
    1792              :   {
    1793              :     struct TALER_Amount r;
    1794              : 
    1795           16 :     if (TALER_ARL_SR_INVALID_NEGATIVE ==
    1796           16 :         TALER_ARL_amount_subtract_neg (&r,
    1797              :                                        &TALER_ARL_USE_AB (
    1798              :                                          reserves_reserve_total_balance),
    1799              :                                        &rs->total_out))
    1800              :     {
    1801              :       /* We could not reduce our total balance, i.e. exchange allowed IN TOTAL (!)
    1802              :          to be withdrawn more than it was IN TOTAL ever given (exchange balance
    1803              :          went negative!).  Woopsie. Calculate how badly it went and log. */
    1804            0 :       report_amount_arithmetic_inconsistency ("global escrow balance",
    1805              :                                               0,
    1806              :                                               &TALER_ARL_USE_AB (
    1807              :                                                 reserves_reserve_total_balance),                   /* what we had */
    1808            0 :                                               &rs->total_out,   /* what we needed */
    1809              :                                               0 /* specific profit/loss does not apply to the total summary */
    1810              :                                               );
    1811            0 :       if (global_qs < 0)
    1812            0 :         return GNUNET_SYSERR;
    1813              :       /* We unexpectedly went negative, so a sane value to continue from
    1814              :          would be zero. */
    1815            0 :       GNUNET_assert (GNUNET_OK ==
    1816              :                      TALER_amount_set_zero (TALER_ARL_currency,
    1817              :                                             &TALER_ARL_USE_AB (
    1818              :                                               reserves_reserve_total_balance)));
    1819              :     }
    1820              :     else
    1821              :     {
    1822           16 :       TALER_ARL_USE_AB (reserves_reserve_total_balance) = r;
    1823              :     }
    1824              :   }
    1825           16 :   if (TALER_amount_is_zero (&rs->prev_balance.reserve_balance))
    1826              :   {
    1827              :     /* balance is zero, drop reserve details (and then do not update/insert) */
    1828           12 :     if (rs->had_ri)
    1829              :     {
    1830            0 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1831              :                   "Final balance of reserve `%s' is zero, dropping it\n",
    1832              :                   TALER_B2S (&rs->reserve_pub));
    1833            0 :       qs = TALER_AUDITORDB_delete_reserve_info (TALER_ARL_adb,
    1834            0 :                                                 &rs->reserve_pub);
    1835            0 :       if (0 >= qs)
    1836              :       {
    1837            0 :         GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1838            0 :         ret = GNUNET_SYSERR;
    1839            0 :         rc->qs = qs;
    1840              :       }
    1841              :     }
    1842              :     else
    1843              :     {
    1844           12 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1845              :                   "Final balance of reserve `%s' is zero, no need to remember it\n",
    1846              :                   TALER_B2S (&rs->reserve_pub));
    1847              :     }
    1848              :   }
    1849              :   else
    1850              :   {
    1851              :     /* balance is non-zero, persist for future audits */
    1852            4 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1853              :                 "Remembering final balance of reserve `%s' as %s\n",
    1854              :                 TALER_B2S (&rs->reserve_pub),
    1855              :                 TALER_amount2s (&rs->prev_balance.reserve_balance));
    1856            4 :     if (rs->had_ri)
    1857            0 :       qs = TALER_AUDITORDB_update_reserve_info (TALER_ARL_adb,
    1858            0 :                                                 &rs->reserve_pub,
    1859            0 :                                                 &rs->prev_balance,
    1860              :                                                 rs->a_expiration_date);
    1861              :     else
    1862            4 :       qs = TALER_AUDITORDB_insert_reserve_info (TALER_ARL_adb,
    1863            4 :                                                 &rs->reserve_pub,
    1864            4 :                                                 &rs->prev_balance,
    1865              :                                                 rs->a_expiration_date,
    1866              :                                                 rs->sender_account);
    1867            4 :     if (0 >= qs)
    1868              :     {
    1869            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1870            0 :       ret = GNUNET_SYSERR;
    1871            0 :       rc->qs = qs;
    1872              :     }
    1873              :   }
    1874              :   /* now we can discard the cached entry */
    1875           16 :   GNUNET_assert (GNUNET_YES ==
    1876              :                  GNUNET_CONTAINER_multihashmap_remove (rc->reserves,
    1877              :                                                        key,
    1878              :                                                        rs));
    1879           16 :   GNUNET_free (rs->sender_account.full_payto);
    1880           16 :   GNUNET_free (rs);
    1881           16 :   return ret;
    1882              : }
    1883              : 
    1884              : 
    1885              : #define CHECK_DB() do {                                       \
    1886              :           if (qs < 0) {                                       \
    1887              :             GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); \
    1888              :             goto cleanup;                                     \
    1889              :           }                                                   \
    1890              :           if (global_qs < 0) {                                \
    1891              :             GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == global_qs); \
    1892              :             qs = global_qs;                                          \
    1893              :             goto cleanup;                                            \
    1894              :           }                                                          \
    1895              :           if (rc.qs < 0) {                                           \
    1896              :             GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == rc.qs);     \
    1897              :             qs = rc.qs;                                              \
    1898              :             goto cleanup;                                            \
    1899              :           }                                                          \
    1900              : } while (0)
    1901              : 
    1902              : 
    1903              : /**
    1904              :  * Analyze reserves for being well-formed.
    1905              :  *
    1906              :  * @param cls NULL
    1907              :  * @return transaction status code
    1908              :  */
    1909              : static enum GNUNET_DB_QueryStatus
    1910           14 : analyze_reserves (void *cls)
    1911              : {
    1912           14 :   struct ReserveContext rc = {
    1913              :     .qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT
    1914              :   };
    1915              :   enum GNUNET_DB_QueryStatus qs;
    1916              : 
    1917              :   (void) cls;
    1918           14 :   global_qs = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
    1919           14 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1920              :               "Analyzing reserves\n");
    1921           14 :   qs = TALER_AUDITORDB_get_auditor_progress (
    1922              :     TALER_ARL_adb,
    1923              :     TALER_ARL_GET_PP (reserves_reserve_in_serial_id),
    1924              :     TALER_ARL_GET_PP (reserves_withdraw_serial_id),
    1925              :     TALER_ARL_GET_PP (reserves_reserve_recoup_serial_id),
    1926              :     TALER_ARL_GET_PP (reserves_reserve_open_serial_id),
    1927              :     TALER_ARL_GET_PP (reserves_reserve_close_serial_id),
    1928              :     TALER_ARL_GET_PP (reserves_purse_decisions_serial_id),
    1929              :     TALER_ARL_GET_PP (reserves_account_merges_serial_id),
    1930              :     TALER_ARL_GET_PP (reserves_history_requests_serial_id),
    1931              :     NULL);
    1932           14 :   if (0 > qs)
    1933              :   {
    1934            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1935            0 :     return qs;
    1936              :   }
    1937           14 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    1938              :   {
    1939            0 :     GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
    1940              :                 "First analysis using this auditor, starting audit from scratch\n");
    1941              :   }
    1942              :   else
    1943              :   {
    1944           14 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1945              :                 "Resuming reserve audit at %llu/%llu/%llu/%llu/%llu/%llu/%llu/%llu\n",
    1946              :                 (unsigned long long) TALER_ARL_USE_PP (
    1947              :                   reserves_reserve_in_serial_id),
    1948              :                 (unsigned long long) TALER_ARL_USE_PP (
    1949              :                   reserves_withdraw_serial_id),
    1950              :                 (unsigned long long) TALER_ARL_USE_PP (
    1951              :                   reserves_reserve_recoup_serial_id),
    1952              :                 (unsigned long long) TALER_ARL_USE_PP (
    1953              :                   reserves_reserve_open_serial_id),
    1954              :                 (unsigned long long) TALER_ARL_USE_PP (
    1955              :                   reserves_reserve_close_serial_id),
    1956              :                 (unsigned long long) TALER_ARL_USE_PP (
    1957              :                   reserves_purse_decisions_serial_id),
    1958              :                 (unsigned long long) TALER_ARL_USE_PP (
    1959              :                   reserves_account_merges_serial_id),
    1960              :                 (unsigned long long) TALER_ARL_USE_PP (
    1961              :                   reserves_history_requests_serial_id));
    1962              :   }
    1963           14 :   qs = TALER_AUDITORDB_get_balance (
    1964              :     TALER_ARL_adb,
    1965              :     TALER_ARL_GET_AB (reserves_reserve_total_balance),
    1966              :     TALER_ARL_GET_AB (reserves_reserve_loss),
    1967              :     TALER_ARL_GET_AB (reserves_withdraw_fee_revenue),
    1968              :     TALER_ARL_GET_AB (reserves_close_fee_revenue),
    1969              :     TALER_ARL_GET_AB (reserves_purse_fee_revenue),
    1970              :     TALER_ARL_GET_AB (reserves_open_fee_revenue),
    1971              :     TALER_ARL_GET_AB (reserves_history_fee_revenue),
    1972              :     TALER_ARL_GET_AB (reserves_total_bad_sig_loss),
    1973              :     TALER_ARL_GET_AB (total_balance_reserve_not_closed),
    1974              :     TALER_ARL_GET_AB (reserves_total_arithmetic_delta_plus),
    1975              :     TALER_ARL_GET_AB (reserves_total_arithmetic_delta_minus),
    1976              :     TALER_ARL_GET_AB (total_balance_summary_delta_plus),
    1977              :     TALER_ARL_GET_AB (total_balance_summary_delta_minus),
    1978              :     NULL);
    1979           14 :   if (qs < 0)
    1980              :   {
    1981            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1982            0 :     return qs;
    1983              :   }
    1984           14 :   rc.reserves = GNUNET_CONTAINER_multihashmap_create (512,
    1985              :                                                       GNUNET_NO);
    1986           14 :   rc.revoked = GNUNET_CONTAINER_multihashmap_create (4,
    1987              :                                                      GNUNET_NO);
    1988              : 
    1989           14 :   qs = TALER_EXCHANGEDB_iterate_reserves_in_above_serial_id (
    1990              :     TALER_ARL_edb,
    1991              :     TALER_ARL_USE_PP (reserves_reserve_in_serial_id),
    1992              :     &handle_reserve_in,
    1993              :     &rc);
    1994           14 :   CHECK_DB ();
    1995           14 :   qs = TALER_EXCHANGEDB_iterate_withdrawals_above_serial_id (
    1996              :     TALER_ARL_edb,
    1997              :     TALER_ARL_USE_PP (reserves_withdraw_serial_id),
    1998              :     &handle_withdrawals,
    1999              :     &rc);
    2000           14 :   CHECK_DB ();
    2001           14 :   qs = TALER_EXCHANGEDB_iterate_recoups_above_serial_id (
    2002              :     TALER_ARL_edb,
    2003              :     TALER_ARL_USE_PP (reserves_reserve_recoup_serial_id),
    2004              :     &handle_recoup_by_reserve,
    2005              :     &rc);
    2006           14 :   CHECK_DB ();
    2007           14 :   qs = TALER_EXCHANGEDB_iterate_reserve_open_above_serial_id (
    2008              :     TALER_ARL_edb,
    2009              :     TALER_ARL_USE_PP (reserves_reserve_open_serial_id),
    2010              :     &handle_reserve_open,
    2011              :     &rc);
    2012           14 :   CHECK_DB ();
    2013           14 :   qs = TALER_EXCHANGEDB_iterate_reserve_closed_above_serial_id (
    2014              :     TALER_ARL_edb,
    2015              :     TALER_ARL_USE_PP (reserves_reserve_close_serial_id),
    2016              :     &handle_reserve_closed,
    2017              :     &rc);
    2018           14 :   CHECK_DB ();
    2019              :   /* process purse_decisions (to credit reserve) */
    2020           14 :   qs = TALER_EXCHANGEDB_iterate_purse_decisions_above_serial_id (
    2021              :     TALER_ARL_edb,
    2022              :     TALER_ARL_USE_PP (reserves_purse_decisions_serial_id),
    2023              :     false,      /* only go for merged purses! */
    2024              :     &purse_decision_cb,
    2025              :     &rc);
    2026           14 :   CHECK_DB ();
    2027              :   /* Charge purse fee! */
    2028              : 
    2029           14 :   qs = TALER_EXCHANGEDB_iterate_account_merges_above_serial_id (
    2030              :     TALER_ARL_edb,
    2031              :     TALER_ARL_USE_PP (reserves_account_merges_serial_id),
    2032              :     &handle_account_merged,
    2033              :     &rc);
    2034           14 :   CHECK_DB ();
    2035           14 :   GNUNET_CONTAINER_multihashmap_iterate (rc.reserves,
    2036              :                                          &verify_reserve_balance,
    2037              :                                          &rc);
    2038           14 :   CHECK_DB ();
    2039           14 :   GNUNET_break (0 ==
    2040              :                 GNUNET_CONTAINER_multihashmap_size (rc.reserves));
    2041              : 
    2042           14 :   qs = TALER_AUDITORDB_insert_balance (
    2043              :     TALER_ARL_adb,
    2044              :     TALER_ARL_SET_AB (reserves_reserve_total_balance),
    2045              :     TALER_ARL_SET_AB (reserves_reserve_loss),
    2046              :     TALER_ARL_SET_AB (reserves_withdraw_fee_revenue),
    2047              :     TALER_ARL_SET_AB (reserves_close_fee_revenue),
    2048              :     TALER_ARL_SET_AB (reserves_purse_fee_revenue),
    2049              :     TALER_ARL_SET_AB (reserves_open_fee_revenue),
    2050              :     TALER_ARL_SET_AB (reserves_history_fee_revenue),
    2051              :     TALER_ARL_SET_AB (reserves_total_bad_sig_loss),
    2052              :     TALER_ARL_SET_AB (total_balance_reserve_not_closed),
    2053              :     TALER_ARL_SET_AB (reserves_total_arithmetic_delta_plus),
    2054              :     TALER_ARL_SET_AB (reserves_total_arithmetic_delta_minus),
    2055              :     TALER_ARL_SET_AB (total_balance_summary_delta_plus),
    2056              :     TALER_ARL_SET_AB (total_balance_summary_delta_minus),
    2057              :     NULL);
    2058           14 :   if (0 > qs)
    2059              :   {
    2060            3 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    2061            3 :     goto cleanup;
    2062              :   }
    2063              : 
    2064           11 :   qs = TALER_AUDITORDB_insert_auditor_progress (
    2065              :     TALER_ARL_adb,
    2066              :     TALER_ARL_SET_PP (reserves_reserve_in_serial_id),
    2067              :     TALER_ARL_SET_PP (reserves_withdraw_serial_id),
    2068              :     TALER_ARL_SET_PP (reserves_reserve_recoup_serial_id),
    2069              :     TALER_ARL_SET_PP (reserves_reserve_open_serial_id),
    2070              :     TALER_ARL_SET_PP (reserves_reserve_close_serial_id),
    2071              :     TALER_ARL_SET_PP (reserves_purse_decisions_serial_id),
    2072              :     TALER_ARL_SET_PP (reserves_account_merges_serial_id),
    2073              :     TALER_ARL_SET_PP (reserves_history_requests_serial_id),
    2074              :     NULL);
    2075           11 :   if (0 > qs)
    2076              :   {
    2077            0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    2078              :                 "Failed to update auditor DB, not recording progress\n");
    2079            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    2080            0 :     goto cleanup;
    2081              :   }
    2082              : 
    2083           11 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    2084              :               "Concluded reserve audit step at %llu/%llu/%llu/%llu/%llu/%llu/%llu/%llu\n",
    2085              :               (unsigned long long) TALER_ARL_USE_PP (
    2086              :                 reserves_reserve_in_serial_id),
    2087              :               (unsigned long long) TALER_ARL_USE_PP (
    2088              :                 reserves_withdraw_serial_id),
    2089              :               (unsigned long long) TALER_ARL_USE_PP (
    2090              :                 reserves_reserve_recoup_serial_id),
    2091              :               (unsigned long long) TALER_ARL_USE_PP (
    2092              :                 reserves_reserve_open_serial_id),
    2093              :               (unsigned long long) TALER_ARL_USE_PP (
    2094              :                 reserves_reserve_close_serial_id),
    2095              :               (unsigned long long) TALER_ARL_USE_PP (
    2096              :                 reserves_purse_decisions_serial_id),
    2097              :               (unsigned long long) TALER_ARL_USE_PP (
    2098              :                 reserves_account_merges_serial_id),
    2099              :               (unsigned long long) TALER_ARL_USE_PP (
    2100              :                 reserves_history_requests_serial_id));
    2101           11 :   qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
    2102           14 : cleanup:
    2103           14 :   GNUNET_CONTAINER_multihashmap_destroy (rc.reserves);
    2104           14 :   GNUNET_CONTAINER_multihashmap_destroy (rc.revoked);
    2105           14 :   return qs;
    2106              : }
    2107              : 
    2108              : 
    2109              : #undef CHECK_DB
    2110              : 
    2111              : 
    2112              : /**
    2113              :  * Function called on events received from Postgres.
    2114              :  *
    2115              :  * @param cls closure, NULL
    2116              :  * @param extra additional event data provided
    2117              :  * @param extra_size number of bytes in @a extra
    2118              :  */
    2119              : static void
    2120            0 : db_notify (void *cls,
    2121              :            const void *extra,
    2122              :            size_t extra_size)
    2123              : {
    2124              :   (void) cls;
    2125              :   (void) extra;
    2126              :   (void) extra_size;
    2127              : 
    2128            0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    2129              :               "Received notification to wake reserves helper\n");
    2130            0 :   if (GNUNET_OK !=
    2131            0 :       TALER_ARL_setup_sessions_and_run (&analyze_reserves,
    2132              :                                         NULL))
    2133              :   {
    2134            0 :     GNUNET_SCHEDULER_shutdown ();
    2135            0 :     global_ret = EXIT_FAILURE;
    2136            0 :     return;
    2137              :   }
    2138              : }
    2139              : 
    2140              : 
    2141              : /**
    2142              :  * Function called on shutdown.
    2143              :  */
    2144              : static void
    2145           10 : do_shutdown (void *cls)
    2146              : {
    2147              :   (void) cls;
    2148           10 :   if (NULL != eh)
    2149              :   {
    2150            4 :     TALER_AUDITORDB_event_listen_cancel (eh);
    2151            4 :     eh = NULL;
    2152              :   }
    2153           10 :   TALER_ARL_done ();
    2154           10 : }
    2155              : 
    2156              : 
    2157              : /**
    2158              :  * Main function that will be run.
    2159              :  *
    2160              :  * @param cls closure
    2161              :  * @param args remaining command-line arguments
    2162              :  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
    2163              :  * @param c configuration
    2164              :  */
    2165              : static void
    2166           10 : run (void *cls,
    2167              :      char *const *args,
    2168              :      const char *cfgfile,
    2169              :      const struct GNUNET_CONFIGURATION_Handle *c)
    2170              : {
    2171              :   (void) cls;
    2172              :   (void) args;
    2173              :   (void) cfgfile;
    2174              : 
    2175           10 :   cfg = c;
    2176           10 :   GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
    2177              :                                  NULL);
    2178           10 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    2179              :               "Launching reserves auditor\n");
    2180           10 :   if (EXIT_SUCCESS !=
    2181           10 :       (global_ret = TALER_ARL_init (c)))
    2182              :   {
    2183            0 :     return;
    2184              :   }
    2185           10 :   if (GNUNET_OK !=
    2186           10 :       GNUNET_CONFIGURATION_get_value_time (TALER_ARL_cfg,
    2187              :                                            "exchangedb",
    2188              :                                            "IDLE_RESERVE_EXPIRATION_TIME",
    2189              :                                            &idle_reserve_expiration_time))
    2190              :   {
    2191            0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    2192              :                                "exchangedb",
    2193              :                                "IDLE_RESERVE_EXPIRATION_TIME");
    2194            0 :     GNUNET_SCHEDULER_shutdown ();
    2195            0 :     global_ret = EXIT_FAILURE;
    2196            0 :     return;
    2197              :   }
    2198           10 :   if (test_mode != 1)
    2199              :   {
    2200            4 :     struct GNUNET_DB_EventHeaderP es = {
    2201            4 :       .size = htons (sizeof (es)),
    2202            4 :       .type = htons (TALER_DBEVENT_EXCHANGE_AUDITOR_WAKE_HELPER_RESERVES)
    2203              :     };
    2204              : 
    2205            4 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    2206              :                 "Running helper indefinitely\n");
    2207            4 :     eh = TALER_AUDITORDB_event_listen (TALER_ARL_adb,
    2208              :                                        &es,
    2209            4 :                                        GNUNET_TIME_UNIT_FOREVER_REL,
    2210              :                                        &db_notify,
    2211              :                                        NULL);
    2212              :   }
    2213           10 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    2214              :               "Starting audit\n");
    2215           10 :   if (GNUNET_OK !=
    2216           10 :       TALER_ARL_setup_sessions_and_run (&analyze_reserves,
    2217              :                                         NULL))
    2218              :   {
    2219            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    2220              :                 "Audit failed\n");
    2221            0 :     GNUNET_SCHEDULER_shutdown ();
    2222            0 :     global_ret = EXIT_FAILURE;
    2223            0 :     return;
    2224              :   }
    2225              : }
    2226              : 
    2227              : 
    2228              : /**
    2229              :  * The main function to check the database's handling of reserves.
    2230              :  *
    2231              :  * @param argc number of arguments from the command line
    2232              :  * @param argv command line arguments
    2233              :  * @return 0 ok, 1 on error
    2234              :  */
    2235              : int
    2236           10 : main (int argc,
    2237              :       char *const *argv)
    2238              : {
    2239           10 :   const struct GNUNET_GETOPT_CommandLineOption options[] = {
    2240           10 :     GNUNET_GETOPT_option_flag ('i',
    2241              :                                "internal",
    2242              :                                "perform checks only applicable for exchange-internal audits",
    2243              :                                &internal_checks),
    2244           10 :     GNUNET_GETOPT_option_flag ('t',
    2245              :                                "test",
    2246              :                                "run in test mode and exit when idle",
    2247              :                                &test_mode),
    2248           10 :     GNUNET_GETOPT_option_timetravel ('T',
    2249              :                                      "timetravel"),
    2250              :     GNUNET_GETOPT_OPTION_END
    2251              :   };
    2252              :   enum GNUNET_GenericReturnValue ret;
    2253              : 
    2254           10 :   ret = GNUNET_PROGRAM_run (
    2255              :     TALER_AUDITOR_project_data (),
    2256              :     argc,
    2257              :     argv,
    2258              :     "taler-helper-auditor-reserves",
    2259              :     gettext_noop ("Audit Taler exchange reserve handling"),
    2260              :     options,
    2261              :     &run,
    2262              :     NULL);
    2263           10 :   if (GNUNET_SYSERR == ret)
    2264            0 :     return EXIT_NOTCONFIGURED;
    2265           10 :   if (GNUNET_NO == ret)
    2266            0 :     return EXIT_SUCCESS;
    2267           10 :   return global_ret;
    2268              : }
    2269              : 
    2270              : 
    2271              : /* end of taler-helper-auditor-reserves.c */
        

Generated by: LCOV version 2.0-1