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

Generated by: LCOV version 2.0-1