LCOV - code coverage report
Current view: top level - auditor - taler-helper-auditor-reserves.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 16.4 % 586 96
Test Date: 2025-12-28 14:06:02 Functions: 22.2 % 18 4

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

Generated by: LCOV version 2.0-1