LCOV - code coverage report
Current view: top level - auditor - taler-helper-auditor-purses.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 14.9 % 503 75
Test Date: 2026-11-03 22:15:25 Functions: 21.1 % 19 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-purses.c
      18              :  * @brief audits the purses of an exchange database
      19              :  * @author Christian Grothoff
      20              :  */
      21              : #include "platform.h"
      22              : #include <gnunet/gnunet_util_lib.h>
      23              : #include "auditordb_lib.h"
      24              : #include "exchangedb_lib.h"
      25              : #include "taler/taler_bank_service.h"
      26              : #include "taler/taler_signatures.h"
      27              : #include "report-lib.h"
      28              : #include "taler/taler_dbevents.h"
      29              : #include "auditor-database/delete_purse_info.h"
      30              : #include "auditor-database/event_listen.h"
      31              : #include "auditor-database/get_auditor_progress.h"
      32              : #include "auditor-database/get_balance.h"
      33              : #include "auditor-database/get_purse_info.h"
      34              : #include "auditor-database/insert_amount_arithmetic_inconsistency.h"
      35              : #include "auditor-database/insert_auditor_progress.h"
      36              : #include "auditor-database/insert_bad_sig_losses.h"
      37              : #include "auditor-database/insert_balance.h"
      38              : #include "auditor-database/insert_purse_info.h"
      39              : #include "auditor-database/insert_purse_not_closed_inconsistencies.h"
      40              : #include "auditor-database/insert_row_inconsistency.h"
      41              : struct PurseContext;
      42              : #define TALER_AUDITORDB_EXPIRED_PURSE_RESULT_CLOSURE struct PurseContext
      43              : #include "auditor-database/iterate_purse_expired.h"
      44              : #include "auditor-database/update_purse_info.h"
      45              : #include "exchange-database/get_global_fee.h"
      46              : struct PurseContext;
      47              : #define TALER_EXCHANGEDB_ACCOUNT_MERGE_RESULT_CLOSURE struct PurseContext
      48              : #define TALER_EXCHANGEDB_ALL_PURSE_DECISION_RESULT_CLOSURE struct PurseContext
      49              : #define TALER_EXCHANGEDB_ALL_PURSE_DELETIONS_RESULT_CLOSURE struct PurseContext
      50              : #define TALER_EXCHANGEDB_PURSE_DEPOSIT_RESULT_CLOSURE struct PurseContext
      51              : #define TALER_EXCHANGEDB_PURSE_MERGE_RESULT_CLOSURE struct PurseContext
      52              : #define TALER_EXCHANGEDB_PURSE_REQUEST_RESULT_CLOSURE struct PurseContext
      53              : #include "exchange-database/iterate_account_merges_above_serial_id.h"
      54              : #include "exchange-database/iterate_all_purse_decisions_above_serial_id.h"
      55              : #include "exchange-database/iterate_all_purse_deletions_above_serial_id.h"
      56              : #include "exchange-database/get_purse.h"
      57              : #include "exchange-database/iterate_purse_deposits_above_serial_id.h"
      58              : #include "exchange-database/iterate_purse_merges_above_serial_id.h"
      59              : #include "exchange-database/iterate_purse_requests_above_serial_id.h"
      60              : 
      61              : 
      62              : /**
      63              :  * Use a 1 day grace period to deal with clocks not being perfectly synchronized.
      64              :  */
      65              : #define EXPIRATION_GRACE_PERIOD GNUNET_TIME_UNIT_DAYS
      66              : 
      67              : /**
      68              :  * Return value from main().
      69              :  */
      70              : static int global_ret;
      71              : 
      72              : /**
      73              :  * Run in test mode. Exit when idle instead of
      74              :  * going to sleep and waiting for more work.
      75              :  */
      76              : static int test_mode;
      77              : 
      78              : /**
      79              :  * Checkpointing our progress for purses.
      80              :  */
      81              : static TALER_ARL_DEF_PP (purse_account_merge_serial_id);
      82              : static TALER_ARL_DEF_PP (purse_decision_serial_id);
      83              : static TALER_ARL_DEF_PP (purse_deletion_serial_id);
      84              : static TALER_ARL_DEF_PP (purse_deposits_serial_id);
      85              : static TALER_ARL_DEF_PP (purse_merges_serial_id);
      86              : static TALER_ARL_DEF_PP (purse_request_serial_id);
      87              : static TALER_ARL_DEF_PP (purse_open_counter);
      88              : static TALER_ARL_DEF_AB (purse_global_balance);
      89              : 
      90              : /**
      91              :  * Total amount purses were merged with insufficient balance.
      92              :  */
      93              : static TALER_ARL_DEF_AB (purse_total_balance_insufficient_loss);
      94              : 
      95              : /**
      96              :  * Total amount purse decisions are delayed past deadline.
      97              :  */
      98              : static TALER_ARL_DEF_AB (purse_total_delayed_decisions);
      99              : 
     100              : /**
     101              :  * Total amount affected by purses not having been closed on time.
     102              :  */
     103              : static TALER_ARL_DEF_AB (purse_total_balance_purse_not_closed);
     104              : 
     105              : /**
     106              :  * Profits the exchange made by bad amount calculations.
     107              :  */
     108              : static TALER_ARL_DEF_AB (purse_total_arithmetic_delta_plus);
     109              : 
     110              : /**
     111              :  * Losses the exchange made by bad amount calculations.
     112              :  */
     113              : static TALER_ARL_DEF_AB (purse_total_arithmetic_delta_minus);
     114              : 
     115              : /**
     116              :  * Total amount lost by operations for which signatures were invalid.
     117              :  */
     118              : static TALER_ARL_DEF_AB (purse_total_bad_sig_loss);
     119              : 
     120              : /**
     121              :  * Should we run checks that only work for exchange-internal audits?
     122              :  */
     123              : static int internal_checks;
     124              : 
     125              : static struct GNUNET_DB_EventHandler *eh;
     126              : 
     127              : /**
     128              :  * The auditors's configuration.
     129              :  */
     130              : static const struct GNUNET_CONFIGURATION_Handle *cfg;
     131              : 
     132              : /* ***************************** Report logic **************************** */
     133              : 
     134              : 
     135              : /**
     136              :  * Report a (serious) inconsistency in the exchange's database with
     137              :  * respect to calculations involving amounts.
     138              :  *
     139              :  * @param operation what operation had the inconsistency
     140              :  * @param rowid affected row, 0 if row is missing
     141              :  * @param exchange amount calculated by exchange
     142              :  * @param auditor amount calculated by auditor
     143              :  * @param profitable 1 if @a exchange being larger than @a auditor is
     144              :  *           profitable for the exchange for this operation,
     145              :  *           -1 if @a exchange being smaller than @a auditor is
     146              :  *           profitable for the exchange, and 0 if it is unclear
     147              :  * @return transaction status
     148              :  */
     149              : static enum GNUNET_DB_QueryStatus
     150            0 : report_amount_arithmetic_inconsistency (
     151              :   const char *operation,
     152              :   uint64_t rowid,
     153              :   const struct TALER_Amount *exchange,
     154              :   const struct TALER_Amount *auditor,
     155              :   int profitable)
     156              : {
     157              :   struct TALER_Amount delta;
     158              :   struct TALER_Amount *target;
     159              :   enum GNUNET_DB_QueryStatus qs;
     160              : 
     161            0 :   if (0 < TALER_amount_cmp (exchange,
     162              :                             auditor))
     163              :   {
     164              :     /* exchange > auditor */
     165            0 :     TALER_ARL_amount_subtract (&delta,
     166              :                                exchange,
     167              :                                auditor);
     168              :   }
     169              :   else
     170              :   {
     171              :     /* exchange <= auditor */
     172            0 :     profitable = -profitable;
     173            0 :     TALER_ARL_amount_subtract (&delta,
     174              :                                auditor,
     175              :                                exchange);
     176              :   }
     177              : 
     178              :   {
     179            0 :     struct TALER_AUDITORDB_AmountArithmeticInconsistency aai = {
     180              :       .profitable = profitable,
     181              :       .problem_row_id = rowid,
     182              :       .operation = (char *) operation,
     183              :       .exchange_amount = *exchange,
     184              :       .auditor_amount = *auditor
     185              :     };
     186              : 
     187            0 :     qs = TALER_AUDITORDB_insert_amount_arithmetic_inconsistency (
     188              :       TALER_ARL_adb,
     189              :       &aai);
     190              : 
     191            0 :     if (qs < 0)
     192              :     {
     193            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     194            0 :       return qs;
     195              :     }
     196              :   }
     197              : 
     198            0 :   if (0 != profitable)
     199              :   {
     200            0 :     target = (1 == profitable)
     201              :       ? &TALER_ARL_USE_AB (purse_total_arithmetic_delta_plus)
     202            0 :       : &TALER_ARL_USE_AB (purse_total_arithmetic_delta_minus);
     203            0 :     TALER_ARL_amount_add (target,
     204              :                           target,
     205              :                           &delta);
     206              :   }
     207            0 :   return qs;
     208              : }
     209              : 
     210              : 
     211              : /**
     212              :  * Report a (serious) inconsistency in the exchange's database.
     213              :  *
     214              :  * @param table affected table
     215              :  * @param rowid affected row, 0 if row is missing
     216              :  * @param diagnostic message explaining the problem
     217              :  * @return transaction status
     218              :  */
     219              : static enum GNUNET_DB_QueryStatus
     220            0 : report_row_inconsistency (const char *table,
     221              :                           uint64_t rowid,
     222              :                           const char *diagnostic)
     223              : {
     224              :   enum GNUNET_DB_QueryStatus qs;
     225            0 :   struct TALER_AUDITORDB_RowInconsistency ri = {
     226              :     .diagnostic = (char *) diagnostic,
     227              :     .row_table = (char *) table,
     228              :     .problem_row_id = rowid
     229              :   };
     230              : 
     231            0 :   qs = TALER_AUDITORDB_insert_row_inconsistency (
     232              :     TALER_ARL_adb,
     233              :     &ri);
     234              : 
     235            0 :   if (qs < 0)
     236              :   {
     237            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     238            0 :     return qs;
     239              :   }
     240            0 :   return qs;
     241              : }
     242              : 
     243              : 
     244              : /**
     245              :  * Obtain the purse fee for a purse created at @a time.
     246              :  *
     247              :  * @param atime when was the purse created
     248              :  * @param[out] fee set to the purse fee
     249              :  * @return #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT on success
     250              :  */
     251              : static enum GNUNET_DB_QueryStatus
     252            0 : get_purse_fee (struct GNUNET_TIME_Timestamp atime,
     253              :                struct TALER_Amount *fee)
     254              : {
     255              :   enum GNUNET_DB_QueryStatus qs;
     256              :   struct TALER_MasterSignatureP master_sig;
     257              :   struct GNUNET_TIME_Timestamp start_date;
     258              :   struct GNUNET_TIME_Timestamp end_date;
     259              :   struct TALER_GlobalFeeSet fees;
     260              :   struct GNUNET_TIME_Relative ptimeout;
     261              :   struct GNUNET_TIME_Relative hexp;
     262              :   uint32_t pacl;
     263              : 
     264            0 :   qs = TALER_EXCHANGEDB_get_global_fee (TALER_ARL_edb,
     265              :                                         atime,
     266              :                                         &start_date,
     267              :                                         &end_date,
     268              :                                         &fees,
     269              :                                         &ptimeout,
     270              :                                         &hexp,
     271              :                                         &pacl,
     272              :                                         &master_sig);
     273            0 :   if (0 > qs)
     274              :   {
     275            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     276            0 :     return qs;
     277              :   }
     278            0 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
     279              :   {
     280              :     char *diag;
     281              : 
     282            0 :     GNUNET_asprintf (&diag,
     283              :                      "purse fee unavailable at %s\n",
     284              :                      GNUNET_TIME_timestamp2s (atime));
     285            0 :     qs = report_row_inconsistency ("purse-fee",
     286              :                                    atime.abs_time.abs_value_us,
     287              :                                    diag);
     288            0 :     GNUNET_free (diag);
     289            0 :     if (0 > qs)
     290              :     {
     291            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     292            0 :       return qs;
     293              :     }
     294            0 :     return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
     295              :   }
     296            0 :   *fee = fees.purse;
     297            0 :   return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
     298              : }
     299              : 
     300              : 
     301              : /* ***************************** Analyze purses ************************ */
     302              : /* This logic checks the purses_requests, purse_deposits,
     303              :    purse_refunds, purse_merges and account_merges */
     304              : 
     305              : /**
     306              :  * Summary data we keep per purse.
     307              :  */
     308              : struct PurseSummary
     309              : {
     310              :   /**
     311              :    * Public key of the purse.
     312              :    * Always set when the struct is first initialized.
     313              :    */
     314              :   struct TALER_PurseContractPublicKeyP purse_pub;
     315              : 
     316              :   /**
     317              :    * Balance of the purse from deposits (includes purse fee, excludes deposit
     318              :    * fees), as calculated by auditor.
     319              :    */
     320              :   struct TALER_Amount balance;
     321              : 
     322              :   /**
     323              :    * Expected value of the purse, excludes purse fee.
     324              :    */
     325              :   struct TALER_Amount total_value;
     326              : 
     327              :   /**
     328              :    * Purse balance according to exchange DB.
     329              :    */
     330              :   struct TALER_Amount exchange_balance;
     331              : 
     332              :   /**
     333              :    * Contract terms of the purse.
     334              :    */
     335              :   struct TALER_PrivateContractHashP h_contract_terms;
     336              : 
     337              :   /**
     338              :    * Merge timestamp (as per exchange DB).
     339              :    */
     340              :   struct GNUNET_TIME_Timestamp merge_timestamp;
     341              : 
     342              :   /**
     343              :    * Purse creation date.  This is when the merge
     344              :    * fee is applied.
     345              :    */
     346              :   struct GNUNET_TIME_Timestamp creation_date;
     347              : 
     348              :   /**
     349              :    * Purse expiration date.
     350              :    */
     351              :   struct GNUNET_TIME_Timestamp expiration_date;
     352              : 
     353              :   /**
     354              :    * Did we have a previous purse info?  Used to decide between UPDATE and
     355              :    * INSERT later.  Initialized in #load_auditor_purse_summary().
     356              :    */
     357              :   bool had_pi;
     358              : 
     359              :   /**
     360              :    * Was the purse deleted? Note: as this is set via an UPDATE, it
     361              :    * may be false at the auditor even if the purse was deleted. Thus,
     362              :    * this value is only meaningful for *internal* checks.
     363              :    */
     364              :   bool purse_deleted;
     365              : 
     366              :   /**
     367              :    * Was the purse refunded? Note: as this is set via an UPDATE, it
     368              :    * may be false at the auditor even if the purse was deleted. Thus,
     369              :    * this value is only meaningful for *internal* checks.
     370              :    */
     371              :   bool purse_refunded;
     372              : 
     373              : };
     374              : 
     375              : 
     376              : /**
     377              :  * Load the auditor's remembered state about the purse into @a ps.
     378              :  *
     379              :  * @param[in,out] ps purse summary to (fully) initialize
     380              :  * @return transaction status code
     381              :  */
     382              : static enum GNUNET_DB_QueryStatus
     383            0 : load_auditor_purse_summary (struct PurseSummary *ps)
     384              : {
     385              :   enum GNUNET_DB_QueryStatus qs;
     386              :   uint64_t rowid;
     387              : 
     388            0 :   qs = TALER_AUDITORDB_get_purse_info (TALER_ARL_adb,
     389            0 :                                        &ps->purse_pub,
     390              :                                        &rowid,
     391              :                                        &ps->balance,
     392              :                                        &ps->expiration_date);
     393            0 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     394              :               "Loaded purse `%s' info (%d)\n",
     395              :               TALER_B2S (&ps->purse_pub),
     396              :               (int) qs);
     397            0 :   if (0 > qs)
     398              :   {
     399            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     400            0 :     return qs;
     401              :   }
     402            0 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
     403              :   {
     404            0 :     ps->had_pi = false;
     405            0 :     GNUNET_assert (GNUNET_OK ==
     406              :                    TALER_amount_set_zero (TALER_ARL_currency,
     407              :                                           &ps->balance));
     408            0 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     409              :                 "Creating fresh purse `%s'\n",
     410              :                 TALER_B2S (&ps->purse_pub));
     411            0 :     return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
     412              :   }
     413            0 :   ps->had_pi = true;
     414            0 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     415              :               "Auditor remembers purse `%s' has balance %s\n",
     416              :               TALER_B2S (&ps->purse_pub),
     417              :               TALER_amount2s (&ps->balance));
     418            0 :   return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
     419              : }
     420              : 
     421              : 
     422              : /**
     423              :  * Closure to the various callbacks we make while checking a purse.
     424              :  */
     425              : struct PurseContext
     426              : {
     427              :   /**
     428              :    * Map from hash of purse's public key to a `struct PurseSummary`.
     429              :    */
     430              :   struct GNUNET_CONTAINER_MultiHashMap *purses;
     431              : 
     432              :   /**
     433              :    * Transaction status code, set to error codes if applicable.
     434              :    */
     435              :   enum GNUNET_DB_QueryStatus qs;
     436              : 
     437              : };
     438              : 
     439              : 
     440              : /**
     441              :  * Create a new purse for @a purse_pub in @a pc.
     442              :  *
     443              :  * @param[in,out] pc context to update
     444              :  * @param purse_pub key for which to create a purse
     445              :  * @return NULL on error
     446              :  */
     447              : static struct PurseSummary *
     448            0 : setup_purse (struct PurseContext *pc,
     449              :              const struct TALER_PurseContractPublicKeyP *purse_pub)
     450              : {
     451              :   struct PurseSummary *ps;
     452              :   struct GNUNET_HashCode key;
     453              :   enum GNUNET_DB_QueryStatus qs;
     454              : 
     455            0 :   GNUNET_CRYPTO_hash (purse_pub,
     456              :                       sizeof (*purse_pub),
     457              :                       &key);
     458            0 :   ps = GNUNET_CONTAINER_multihashmap_get (pc->purses,
     459              :                                           &key);
     460            0 :   if (NULL != ps)
     461              :   {
     462            0 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     463              :                 "Found purse `%s' summary in cache\n",
     464              :                 TALER_B2S (&ps->purse_pub));
     465            0 :     return ps;
     466              :   }
     467            0 :   ps = GNUNET_new (struct PurseSummary);
     468            0 :   ps->purse_pub = *purse_pub;
     469            0 :   GNUNET_assert (GNUNET_OK ==
     470              :                  TALER_amount_set_zero (TALER_ARL_currency,
     471              :                                         &ps->balance));
     472              :   /* get purse meta-data from exchange DB */
     473            0 :   qs = TALER_EXCHANGEDB_get_purse (TALER_ARL_edb,
     474              :                                    purse_pub,
     475              :                                    &ps->creation_date,
     476              :                                    &ps->expiration_date,
     477              :                                    &ps->total_value,
     478              :                                    &ps->exchange_balance,
     479              :                                    &ps->h_contract_terms,
     480              :                                    &ps->merge_timestamp,
     481              :                                    &ps->purse_deleted,
     482              :                                    &ps->purse_refunded);
     483            0 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     484              :               "Loaded purse `%s' meta-data (%d)\n",
     485              :               TALER_B2S (purse_pub),
     486              :               (int) qs);
     487            0 :   if (0 >= qs)
     488              :   {
     489            0 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     490              :                 "Failed to load meta-data of purse `%s'\n",
     491              :                 TALER_B2S (&ps->purse_pub));
     492            0 :     GNUNET_free (ps);
     493            0 :     pc->qs = qs;
     494            0 :     return NULL;
     495              :   }
     496            0 :   GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs);
     497            0 :   qs = load_auditor_purse_summary (ps);
     498            0 :   if (0 > qs)
     499              :   {
     500            0 :     GNUNET_free (ps);
     501            0 :     pc->qs = qs;
     502            0 :     return NULL;
     503              :   }
     504            0 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     505              :               "Starting purse `%s' analysis\n",
     506              :               TALER_B2S (purse_pub));
     507            0 :   GNUNET_assert (GNUNET_OK ==
     508              :                  GNUNET_CONTAINER_multihashmap_put (pc->purses,
     509              :                                                     &key,
     510              :                                                     ps,
     511              :                                                     GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)
     512              :                  );
     513            0 :   return ps;
     514              : }
     515              : 
     516              : 
     517              : /**
     518              :  * Function called on purse requests.
     519              :  *
     520              :  * @param pc closure
     521              :  * @param rowid which row in the database was the request stored in
     522              :  * @param purse_pub public key of the purse
     523              :  * @param merge_pub public key representing the merge capability
     524              :  * @param purse_creation when was the purse created
     525              :  * @param purse_expiration when would an unmerged purse expire
     526              :  * @param h_contract_terms contract associated with the purse
     527              :  * @param age_limit the age limit for deposits into the purse
     528              :  * @param target_amount amount to be put into the purse
     529              :  * @param purse_sig signature of the purse over the initialization data
     530              :  * @return #GNUNET_OK to continue to iterate
     531              :    */
     532              : static enum GNUNET_GenericReturnValue
     533            0 : handle_purse_requested (
     534              :   struct PurseContext *pc,
     535              :   uint64_t rowid,
     536              :   const struct TALER_PurseContractPublicKeyP *purse_pub,
     537              :   const struct TALER_PurseMergePublicKeyP *merge_pub,
     538              :   struct GNUNET_TIME_Timestamp purse_creation,
     539              :   struct GNUNET_TIME_Timestamp purse_expiration,
     540              :   const struct TALER_PrivateContractHashP *h_contract_terms,
     541              :   uint32_t age_limit,
     542              :   const struct TALER_Amount *target_amount,
     543              :   const struct TALER_PurseContractSignatureP *purse_sig)
     544              : {
     545              :   struct PurseSummary *ps;
     546              :   struct GNUNET_HashCode key;
     547              : 
     548            0 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     549              :               "Handling purse request `%s'\n",
     550              :               TALER_B2S (purse_pub));
     551            0 :   GNUNET_assert (rowid >= TALER_ARL_USE_PP (purse_request_serial_id));
     552            0 :   TALER_ARL_USE_PP (purse_request_serial_id) = rowid + 1;
     553            0 :   if (GNUNET_OK !=
     554            0 :       TALER_wallet_purse_create_verify (purse_expiration,
     555              :                                         h_contract_terms,
     556              :                                         merge_pub,
     557              :                                         age_limit,
     558              :                                         target_amount,
     559              :                                         purse_pub,
     560              :                                         purse_sig))
     561              :   {
     562            0 :     struct TALER_AUDITORDB_BadSigLosses bsl = {
     563              :       .problem_row_id = rowid,
     564              :       .operation = (char *) "purse-request",
     565              :       .loss = *target_amount,
     566              :       .operation_specific_pub = purse_pub->eddsa_pub
     567              :     };
     568              :     enum GNUNET_DB_QueryStatus qs;
     569              : 
     570            0 :     qs = TALER_AUDITORDB_insert_bad_sig_losses (
     571              :       TALER_ARL_adb,
     572              :       &bsl);
     573            0 :     if (qs < 0)
     574              :     {
     575            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     576            0 :       pc->qs = qs;
     577            0 :       return GNUNET_SYSERR;
     578              :     }
     579            0 :     TALER_ARL_amount_add (&TALER_ARL_USE_AB (purse_total_bad_sig_loss),
     580              :                           &TALER_ARL_USE_AB (purse_total_bad_sig_loss),
     581              :                           target_amount);
     582              :   }
     583            0 :   GNUNET_CRYPTO_hash (purse_pub,
     584              :                       sizeof (*purse_pub),
     585              :                       &key);
     586            0 :   ps = GNUNET_new (struct PurseSummary);
     587            0 :   ps->purse_pub = *purse_pub;
     588            0 :   GNUNET_assert (GNUNET_OK ==
     589              :                  TALER_amount_set_zero (TALER_ARL_currency,
     590              :                                         &ps->balance));
     591            0 :   ps->creation_date = purse_creation;
     592            0 :   ps->expiration_date = purse_expiration;
     593            0 :   ps->total_value = *target_amount;
     594            0 :   ps->h_contract_terms = *h_contract_terms;
     595              :   {
     596              :     enum GNUNET_DB_QueryStatus qs;
     597              : 
     598            0 :     qs = load_auditor_purse_summary (ps);
     599            0 :     if (0 > qs)
     600              :     {
     601            0 :       GNUNET_free (ps);
     602            0 :       pc->qs = qs;
     603            0 :       return GNUNET_SYSERR;
     604              :     }
     605              :   }
     606            0 :   GNUNET_assert (GNUNET_OK ==
     607              :                  GNUNET_CONTAINER_multihashmap_put (pc->purses,
     608              :                                                     &key,
     609              :                                                     ps,
     610              :                                                     GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)
     611              :                  );
     612            0 :   return GNUNET_OK;
     613              : }
     614              : 
     615              : 
     616              : /**
     617              :  * Function called with details about purse deposits that have been made, with
     618              :  * the goal of auditing the deposit's execution.
     619              :  *
     620              :  * @param pc closure
     621              :  * @param rowid unique serial ID for the deposit in our DB
     622              :  * @param deposit deposit details
     623              :  * @param reserve_pub which reserve is the purse merged into, NULL if unknown
     624              :  * @param flags purse flags
     625              :  * @param auditor_balance purse balance (according to the
     626              :  *          auditor during auditing)
     627              :  * @param purse_total target amount the purse should reach
     628              :  * @param denom_pub denomination public key of @a coin_pub
     629              :  * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
     630              :  */
     631              : static enum GNUNET_GenericReturnValue
     632            0 : handle_purse_deposits (
     633              :   struct PurseContext *pc,
     634              :   uint64_t rowid,
     635              :   const struct TALER_EXCHANGEDB_PurseDeposit *deposit,
     636              :   const struct TALER_ReservePublicKeyP *reserve_pub,
     637              :   enum TALER_WalletAccountMergeFlags flags,
     638              :   const struct TALER_Amount *auditor_balance,
     639              :   const struct TALER_Amount *purse_total,
     640              :   const struct TALER_DenominationPublicKey *denom_pub)
     641              : {
     642              :   struct TALER_Amount amount_minus_fee;
     643            0 :   const char *base_url
     644            0 :     = (NULL == deposit->exchange_base_url)
     645              :       ? TALER_ARL_exchange_url
     646            0 :       : deposit->exchange_base_url;
     647              :   struct TALER_DenominationHashP h_denom_pub;
     648              : 
     649              :   /* should be monotonically increasing */
     650            0 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     651              :               "Handling purse deposit `%s'\n",
     652              :               TALER_B2S (&deposit->purse_pub));
     653            0 :   GNUNET_assert (rowid >= TALER_ARL_USE_PP (purse_deposits_serial_id));
     654            0 :   TALER_ARL_USE_PP (purse_deposits_serial_id) = rowid + 1;
     655              : 
     656              :   {
     657              :     const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue;
     658              :     enum GNUNET_DB_QueryStatus qs;
     659              : 
     660            0 :     qs = TALER_ARL_get_denomination_info (denom_pub,
     661              :                                           &issue,
     662              :                                           &h_denom_pub);
     663            0 :     if (0 > qs)
     664              :     {
     665            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     666            0 :       if (GNUNET_DB_STATUS_HARD_ERROR == qs)
     667            0 :         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     668              :                     "Hard database error trying to get denomination %s from database!\n",
     669              :                     TALER_B2S (denom_pub));
     670            0 :       pc->qs = qs;
     671            0 :       return GNUNET_SYSERR;
     672              :     }
     673            0 :     if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
     674              :     {
     675            0 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     676              :                   "Failed to find denomination key for purse deposit `%s' in record %llu\n",
     677              :                   TALER_B2S (&deposit->purse_pub),
     678              :                   (unsigned long long) rowid);
     679            0 :       qs = report_row_inconsistency ("purse-deposit",
     680              :                                      rowid,
     681              :                                      "denomination key not found");
     682            0 :       if (0 > qs)
     683              :       {
     684            0 :         GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     685            0 :         pc->qs = qs;
     686            0 :         return GNUNET_SYSERR;
     687              :       }
     688            0 :       return GNUNET_OK;
     689              :     }
     690            0 :     if (TALER_ARL_SR_INVALID_NEGATIVE ==
     691            0 :         TALER_ARL_amount_subtract_neg (&amount_minus_fee,
     692              :                                        &deposit->amount,
     693              :                                        &issue->fees.deposit))
     694              :     {
     695              :       /* The deposited amount does not even cover the deposit fee of the
     696              :          denomination; report the bogus row instead of dying here. */
     697            0 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     698              :                   "Purse deposit `%s' in record %llu is below the deposit fee of its denomination\n",
     699              :                   TALER_B2S (&deposit->purse_pub),
     700              :                   (unsigned long long) rowid);
     701            0 :       qs = report_row_inconsistency ("purse-deposit",
     702              :                                      rowid,
     703              :                                      "deposited amount below deposit fee");
     704            0 :       if (0 > qs)
     705              :       {
     706            0 :         GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     707            0 :         pc->qs = qs;
     708            0 :         return GNUNET_SYSERR;
     709              :       }
     710            0 :       return GNUNET_OK;
     711              :     }
     712              :   }
     713              : 
     714            0 :   if (GNUNET_OK !=
     715            0 :       TALER_wallet_purse_deposit_verify (base_url,
     716              :                                          &deposit->purse_pub,
     717              :                                          &deposit->amount,
     718              :                                          &h_denom_pub,
     719              :                                          &deposit->h_age_commitment,
     720              :                                          &deposit->coin_pub,
     721              :                                          &deposit->coin_sig))
     722              :   {
     723            0 :     struct TALER_AUDITORDB_BadSigLosses bsl = {
     724              :       .problem_row_id = rowid,
     725              :       .operation = (char *) "purse-deposit",
     726              :       .loss = deposit->amount,
     727              :       .operation_specific_pub = deposit->coin_pub.eddsa_pub
     728              :     };
     729              :     enum GNUNET_DB_QueryStatus qs;
     730              : 
     731            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     732              :                 "Failed to verify purse deposit signature on `%s' in record %llu\n",
     733              :                 TALER_B2S (&deposit->purse_pub),
     734              :                 (unsigned long long) rowid);
     735            0 :     qs = TALER_AUDITORDB_insert_bad_sig_losses (
     736              :       TALER_ARL_adb,
     737              :       &bsl);
     738            0 :     if (qs < 0)
     739              :     {
     740            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     741            0 :       pc->qs = qs;
     742            0 :       return GNUNET_SYSERR;
     743              :     }
     744            0 :     TALER_ARL_amount_add (&TALER_ARL_USE_AB (purse_total_bad_sig_loss),
     745              :                           &TALER_ARL_USE_AB (purse_total_bad_sig_loss),
     746              :                           &deposit->amount);
     747            0 :     return GNUNET_OK;
     748              :   }
     749              : 
     750              :   {
     751              :     struct PurseSummary *ps;
     752              : 
     753            0 :     ps = setup_purse (pc,
     754              :                       &deposit->purse_pub);
     755            0 :     if (NULL == ps)
     756              :     {
     757              :       enum GNUNET_DB_QueryStatus qs;
     758              : 
     759            0 :       if (0 > pc->qs)
     760              :       {
     761            0 :         GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == pc->qs);
     762            0 :         return GNUNET_SYSERR;
     763              :       }
     764            0 :       GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == pc->qs);
     765            0 :       qs = report_row_inconsistency ("purse_deposit",
     766              :                                      rowid,
     767              :                                      "purse not found");
     768            0 :       if (0 > qs)
     769              :       {
     770            0 :         GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     771            0 :         pc->qs = qs;
     772            0 :         return GNUNET_SYSERR;
     773              :       }
     774            0 :       return GNUNET_OK;
     775              :     }
     776            0 :     TALER_ARL_amount_add (&ps->balance,
     777              :                           &ps->balance,
     778              :                           &amount_minus_fee);
     779            0 :     TALER_ARL_amount_add (&TALER_ARL_USE_AB (purse_global_balance),
     780              :                           &TALER_ARL_USE_AB (purse_global_balance),
     781              :                           &amount_minus_fee);
     782              :   }
     783            0 :   return GNUNET_OK;
     784              : }
     785              : 
     786              : 
     787              : /**
     788              :  * Function called with details about purse merges that have been made, with
     789              :  * the goal of auditing the purse merge execution.
     790              :  *
     791              :  * @param pc closure
     792              :  * @param rowid unique serial ID for the deposit in our DB
     793              :  * @param partner_base_url where is the reserve, NULL for this exchange
     794              :  * @param amount total amount expected in the purse
     795              :  * @param balance current balance in the purse
     796              :  * @param flags purse flags
     797              :  * @param merge_pub merge capability key
     798              :  * @param reserve_pub reserve the merge affects
     799              :  * @param merge_sig signature affirming the merge
     800              :  * @param purse_pub purse key
     801              :  * @param merge_timestamp when did the merge happen
     802              :  * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
     803              :  */
     804              : static enum GNUNET_GenericReturnValue
     805            0 : handle_purse_merged (
     806              :   struct PurseContext *pc,
     807              :   uint64_t rowid,
     808              :   const char *partner_base_url,
     809              :   const struct TALER_Amount *amount,
     810              :   const struct TALER_Amount *balance,
     811              :   enum TALER_WalletAccountMergeFlags flags,
     812              :   const struct TALER_PurseMergePublicKeyP *merge_pub,
     813              :   const struct TALER_ReservePublicKeyP *reserve_pub,
     814              :   const struct TALER_PurseMergeSignatureP *merge_sig,
     815              :   const struct TALER_PurseContractPublicKeyP *purse_pub,
     816              :   struct GNUNET_TIME_Timestamp merge_timestamp)
     817              : {
     818              :   struct PurseSummary *ps;
     819              :   enum GNUNET_DB_QueryStatus qs;
     820              : 
     821              :   /* should be monotonically increasing */
     822            0 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     823              :               "Handling purse merged `%s'\n",
     824              :               TALER_B2S (purse_pub));
     825            0 :   GNUNET_assert (rowid >= TALER_ARL_USE_PP (purse_merges_serial_id));
     826            0 :   TALER_ARL_USE_PP (purse_merges_serial_id) = rowid + 1;
     827              : 
     828              :   {
     829              :     struct TALER_NormalizedPayto reserve_url;
     830              : 
     831              :     reserve_url
     832            0 :       = TALER_reserve_make_payto (NULL == partner_base_url
     833              :                                   ? TALER_ARL_exchange_url
     834              :                                   : partner_base_url,
     835              :                                   reserve_pub);
     836            0 :     if (GNUNET_OK !=
     837            0 :         TALER_wallet_purse_merge_verify (reserve_url,
     838              :                                          merge_timestamp,
     839              :                                          purse_pub,
     840              :                                          merge_pub,
     841              :                                          merge_sig))
     842              :     {
     843            0 :       struct TALER_AUDITORDB_BadSigLosses bsl = {
     844              :         .problem_row_id = rowid,
     845              :         .operation = (char *) "merge-purse",
     846              :         .loss = *amount,
     847              :         .operation_specific_pub = merge_pub->eddsa_pub
     848              :       };
     849              : 
     850            0 :       GNUNET_free (reserve_url.normalized_payto);
     851            0 :       qs = TALER_AUDITORDB_insert_bad_sig_losses (
     852              :         TALER_ARL_adb,
     853              :         &bsl);
     854            0 :       if (qs < 0)
     855              :       {
     856            0 :         GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     857            0 :         pc->qs = qs;
     858            0 :         return GNUNET_SYSERR;
     859              :       }
     860            0 :       TALER_ARL_amount_add (&TALER_ARL_USE_AB (purse_total_bad_sig_loss),
     861              :                             &TALER_ARL_USE_AB (purse_total_bad_sig_loss),
     862              :                             amount);
     863            0 :       return GNUNET_OK;
     864              :     }
     865            0 :     GNUNET_free (reserve_url.normalized_payto);
     866              :   }
     867              : 
     868            0 :   ps = setup_purse (pc,
     869              :                     purse_pub);
     870            0 :   if (NULL == ps)
     871              :   {
     872            0 :     if (pc->qs < 0)
     873              :     {
     874            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == pc->qs);
     875            0 :       return GNUNET_SYSERR;
     876              :     }
     877            0 :     GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == pc->qs);
     878            0 :     qs = report_row_inconsistency ("purse-merge",
     879              :                                    rowid,
     880              :                                    "purse not found");
     881            0 :     if (qs < 0)
     882              :     {
     883            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     884            0 :       pc->qs = qs;
     885            0 :       return GNUNET_SYSERR;
     886              :     }
     887            0 :     return GNUNET_OK;
     888              :   }
     889            0 :   GNUNET_break (0 ==
     890              :                 GNUNET_TIME_timestamp_cmp (merge_timestamp,
     891              :                                            ==,
     892              :                                            ps->merge_timestamp));
     893            0 :   TALER_ARL_amount_add (&ps->balance,
     894              :                         &ps->balance,
     895              :                         amount);
     896            0 :   return GNUNET_OK;
     897              : }
     898              : 
     899              : 
     900              : /**
     901              :  * Function called with details about account merge requests that have been
     902              :  * made, with the goal of auditing the account merge execution.
     903              :  *
     904              :  * @param pc closure
     905              :  * @param rowid unique serial ID for the deposit in our DB
     906              :  * @param reserve_pub reserve affected by the merge
     907              :  * @param purse_pub purse being merged
     908              :  * @param h_contract_terms hash over contract of the purse
     909              :  * @param purse_expiration when would the purse expire
     910              :  * @param amount total amount in the purse
     911              :  * @param min_age minimum age of all coins deposited into the purse
     912              :  * @param flags how was the purse created
     913              :  * @param purse_fee if a purse fee was paid, how high is it
     914              :  * @param merge_timestamp when was the merge approved
     915              :  * @param reserve_sig signature by reserve approving the merge
     916              :  * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
     917              :  */
     918              : static enum GNUNET_GenericReturnValue
     919            0 : handle_account_merged (
     920              :   struct PurseContext *pc,
     921              :   uint64_t rowid,
     922              :   const struct TALER_ReservePublicKeyP *reserve_pub,
     923              :   const struct TALER_PurseContractPublicKeyP *purse_pub,
     924              :   const struct TALER_PrivateContractHashP *h_contract_terms,
     925              :   struct GNUNET_TIME_Timestamp purse_expiration,
     926              :   const struct TALER_Amount *amount,
     927              :   uint32_t min_age,
     928              :   enum TALER_WalletAccountMergeFlags flags,
     929              :   const struct TALER_Amount *purse_fee,
     930              :   struct GNUNET_TIME_Timestamp merge_timestamp,
     931              :   const struct TALER_ReserveSignatureP *reserve_sig)
     932              : {
     933              :   struct PurseSummary *ps;
     934              :   enum GNUNET_DB_QueryStatus qs;
     935              : 
     936              :   /* should be monotonically increasing */
     937            0 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     938              :               "Handling account merge on purse `%s'\n",
     939              :               TALER_B2S (purse_pub));
     940            0 :   GNUNET_assert (rowid >= TALER_ARL_USE_PP (purse_account_merge_serial_id));
     941            0 :   TALER_ARL_USE_PP (purse_account_merge_serial_id) = rowid + 1;
     942            0 :   if (GNUNET_OK !=
     943            0 :       TALER_wallet_account_merge_verify (merge_timestamp,
     944              :                                          purse_pub,
     945              :                                          purse_expiration,
     946              :                                          h_contract_terms,
     947              :                                          amount,
     948              :                                          purse_fee,
     949              :                                          min_age,
     950              :                                          flags,
     951              :                                          reserve_pub,
     952              :                                          reserve_sig))
     953              :   {
     954            0 :     struct TALER_AUDITORDB_BadSigLosses bsl = {
     955              :       .problem_row_id = rowid,
     956              :       .operation = (char *) "account-merge",
     957              :       .loss = *purse_fee,
     958              :       .operation_specific_pub = reserve_pub->eddsa_pub
     959              :     };
     960              : 
     961            0 :     qs = TALER_AUDITORDB_insert_bad_sig_losses (
     962              :       TALER_ARL_adb,
     963              :       &bsl);
     964            0 :     if (qs < 0)
     965              :     {
     966            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     967            0 :       pc->qs = qs;
     968            0 :       return GNUNET_SYSERR;
     969              :     }
     970            0 :     TALER_ARL_amount_add (&TALER_ARL_USE_AB (purse_total_bad_sig_loss),
     971              :                           &TALER_ARL_USE_AB (purse_total_bad_sig_loss),
     972              :                           purse_fee);
     973            0 :     return GNUNET_OK;
     974              :   }
     975            0 :   ps = setup_purse (pc,
     976              :                     purse_pub);
     977            0 :   if (NULL == ps)
     978              :   {
     979            0 :     if (0 > pc->qs)
     980              :     {
     981            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == pc->qs);
     982            0 :       return GNUNET_SYSERR;
     983              :     }
     984            0 :     GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == pc->qs);
     985            0 :     qs = report_row_inconsistency ("account-merge",
     986              :                                    rowid,
     987              :                                    "purse not found");
     988            0 :     if (0 > qs)
     989              :     {
     990            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     991            0 :       pc->qs = qs;
     992            0 :       return GNUNET_SYSERR;
     993              :     }
     994            0 :     return GNUNET_OK;
     995              :   }
     996            0 :   TALER_ARL_amount_add (&TALER_ARL_USE_AB (purse_global_balance),
     997              :                         &TALER_ARL_USE_AB (purse_global_balance),
     998              :                         purse_fee);
     999            0 :   TALER_ARL_amount_add (&ps->balance,
    1000              :                         &ps->balance,
    1001              :                         purse_fee);
    1002            0 :   return GNUNET_OK;
    1003              : }
    1004              : 
    1005              : 
    1006              : /**
    1007              :  * Function called with details about purse decisions that have been made.
    1008              :  *
    1009              :  * @param pc closure
    1010              :  * @param rowid unique serial ID for the deposit in our DB
    1011              :  * @param purse_pub which purse was the decision made on
    1012              :  * @param refunded true if decision was to refund
    1013              :  * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
    1014              :  */
    1015              : static enum GNUNET_GenericReturnValue
    1016            0 : handle_purse_decision (
    1017              :   struct PurseContext *pc,
    1018              :   uint64_t rowid,
    1019              :   const struct TALER_PurseContractPublicKeyP *purse_pub,
    1020              :   bool refunded)
    1021              : {
    1022              :   struct PurseSummary *ps;
    1023              :   struct GNUNET_HashCode key;
    1024              :   enum GNUNET_DB_QueryStatus qs;
    1025              :   struct TALER_Amount purse_fee;
    1026              :   struct TALER_Amount balance_without_purse_fee;
    1027              : 
    1028            0 :   GNUNET_assert (rowid >= TALER_ARL_USE_PP (purse_decision_serial_id));
    1029            0 :   TALER_ARL_USE_PP (purse_decision_serial_id) = rowid + 1;
    1030            0 :   ps = setup_purse (pc,
    1031              :                     purse_pub);
    1032            0 :   if (NULL == ps)
    1033              :   {
    1034            0 :     if (0 > pc->qs)
    1035              :     {
    1036            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == pc->qs);
    1037            0 :       return GNUNET_SYSERR;
    1038              :     }
    1039            0 :     qs = report_row_inconsistency ("purse-decision",
    1040              :                                    rowid,
    1041              :                                    "purse not found");
    1042            0 :     if (0 > qs)
    1043              :     {
    1044            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1045            0 :       pc->qs = qs;
    1046            0 :       return GNUNET_SYSERR;
    1047              :     }
    1048            0 :     return GNUNET_OK;
    1049              :   }
    1050            0 :   qs = get_purse_fee (ps->creation_date,
    1051              :                       &purse_fee);
    1052            0 :   if (0 > qs)
    1053              :   {
    1054            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1055            0 :     pc->qs = qs;
    1056            0 :     return GNUNET_SYSERR;
    1057              :   }
    1058            0 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    1059            0 :     return GNUNET_OK; /* already reported */
    1060            0 :   if (0 >
    1061            0 :       TALER_amount_subtract (&balance_without_purse_fee,
    1062            0 :                              &ps->balance,
    1063              :                              &purse_fee))
    1064              :   {
    1065            0 :     qs = report_row_inconsistency ("purse-request",
    1066              :                                    rowid,
    1067              :                                    "purse fee higher than balance");
    1068            0 :     if (0 > qs)
    1069              :     {
    1070            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1071            0 :       pc->qs = qs;
    1072            0 :       return GNUNET_SYSERR;
    1073              :     }
    1074            0 :     GNUNET_assert (GNUNET_OK ==
    1075              :                    TALER_amount_set_zero (TALER_ARL_currency,
    1076              :                                           &balance_without_purse_fee));
    1077              :   }
    1078              : 
    1079            0 :   if (refunded)
    1080              :   {
    1081            0 :     if (-1 != TALER_amount_cmp (&balance_without_purse_fee,
    1082            0 :                                 &ps->total_value))
    1083              :     {
    1084            0 :       qs = report_amount_arithmetic_inconsistency ("purse-decision: refund",
    1085              :                                                    rowid,
    1086              :                                                    &balance_without_purse_fee,
    1087            0 :                                                    &ps->total_value,
    1088              :                                                    0);
    1089            0 :       if (0 > qs)
    1090              :       {
    1091            0 :         GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1092            0 :         pc->qs = qs;
    1093            0 :         return GNUNET_SYSERR;
    1094              :       }
    1095              :     }
    1096            0 :     if ( (internal_checks) &&
    1097            0 :          (! ps->purse_refunded) )
    1098              :     {
    1099            0 :       qs = report_row_inconsistency (
    1100              :         "purse-decision",
    1101              :         rowid,
    1102              :         "purse not marked as refunded (internal check)");
    1103            0 :       if (qs < 0)
    1104              :       {
    1105            0 :         GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1106            0 :         pc->qs = qs;
    1107            0 :         return GNUNET_SYSERR;
    1108              :       }
    1109              :     }
    1110              :   }
    1111              :   else
    1112              :   {
    1113            0 :     if (-1 == TALER_amount_cmp (&balance_without_purse_fee,
    1114            0 :                                 &ps->total_value))
    1115              :     {
    1116            0 :       qs = report_amount_arithmetic_inconsistency ("purse-decision: merge",
    1117              :                                                    rowid,
    1118            0 :                                                    &ps->total_value,
    1119              :                                                    &balance_without_purse_fee,
    1120              :                                                    0);
    1121            0 :       if (0 > qs)
    1122              :       {
    1123            0 :         GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1124            0 :         pc->qs = qs;
    1125            0 :         return GNUNET_SYSERR;
    1126              :       }
    1127            0 :       TALER_ARL_amount_add (&TALER_ARL_USE_AB (
    1128              :                               purse_total_balance_insufficient_loss),
    1129              :                             &TALER_ARL_USE_AB (
    1130              :                               purse_total_balance_insufficient_loss),
    1131              :                             &ps->total_value);
    1132              :     }
    1133              :   }
    1134              : 
    1135            0 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1136              :               "Deleting purse with decision `%s'\n",
    1137              :               TALER_B2S (&ps->purse_pub));
    1138              :   /* The purse ceases to exist, so the money it held is no longer
    1139              :      part of the balance the exchange holds in open purses. */
    1140            0 :   if (0 >
    1141            0 :       TALER_amount_subtract (&TALER_ARL_USE_AB (purse_global_balance),
    1142              :                              &TALER_ARL_USE_AB (purse_global_balance),
    1143            0 :                              &ps->balance))
    1144              :   {
    1145              :     /* Should be impossible: we added this balance to the global
    1146              :        balance when we learned about it. */
    1147            0 :     GNUNET_break (0);
    1148            0 :     GNUNET_assert (GNUNET_OK ==
    1149              :                    TALER_amount_set_zero (TALER_ARL_currency,
    1150              :                                           &TALER_ARL_USE_AB (
    1151              :                                             purse_global_balance)));
    1152              :   }
    1153            0 :   qs = TALER_AUDITORDB_delete_purse_info (TALER_ARL_adb,
    1154              :                                           purse_pub);
    1155            0 :   if (qs < 0)
    1156              :   {
    1157            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1158            0 :     pc->qs = qs;
    1159            0 :     return GNUNET_SYSERR;
    1160              :   }
    1161            0 :   GNUNET_CRYPTO_hash (purse_pub,
    1162              :                       sizeof (*purse_pub),
    1163              :                       &key);
    1164            0 :   GNUNET_assert (GNUNET_YES ==
    1165              :                  GNUNET_CONTAINER_multihashmap_remove (pc->purses,
    1166              :                                                        &key,
    1167              :                                                        ps));
    1168            0 :   GNUNET_free (ps);
    1169            0 :   return GNUNET_OK;
    1170              : }
    1171              : 
    1172              : 
    1173              : /**
    1174              :  * Function called on explicitly deleted purses.
    1175              :  *
    1176              :  * @param pc closure
    1177              :  * @param deletion_serial_id row ID with the deletion data
    1178              :  * @param purse_pub public key of the purse
    1179              :  * @param purse_sig signature affirming deletion of the purse
    1180              :  * @return #GNUNET_OK to continue to iterate
    1181              :  */
    1182              : static enum GNUNET_GenericReturnValue
    1183            0 : handle_purse_deletion (
    1184              :   struct PurseContext *pc,
    1185              :   uint64_t deletion_serial_id,
    1186              :   const struct TALER_PurseContractPublicKeyP *purse_pub,
    1187              :   const struct TALER_PurseContractSignatureP *purse_sig)
    1188              : {
    1189              :   struct PurseSummary *ps;
    1190              : 
    1191            0 :   GNUNET_assert (deletion_serial_id >=
    1192              :                  TALER_ARL_USE_PP (purse_deletion_serial_id));
    1193            0 :   TALER_ARL_USE_PP (purse_deletion_serial_id) = deletion_serial_id + 1;
    1194            0 :   ps = setup_purse (pc,
    1195              :                     purse_pub);
    1196            0 :   if (NULL == ps)
    1197              :   {
    1198            0 :     GNUNET_break (0);
    1199            0 :     return GNUNET_SYSERR;
    1200              :   }
    1201            0 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1202              :               "Handling purse `%s' deletion\n",
    1203              :               TALER_B2S (purse_pub));
    1204            0 :   if (GNUNET_OK !=
    1205            0 :       TALER_wallet_purse_delete_verify (purse_pub,
    1206              :                                         purse_sig))
    1207              :   {
    1208              :     enum GNUNET_DB_QueryStatus qs;
    1209              : 
    1210            0 :     qs = report_row_inconsistency (
    1211              :       "purse-delete",
    1212              :       deletion_serial_id,
    1213              :       "purse deletion signature invalid");
    1214            0 :     if (qs < 0)
    1215              :     {
    1216            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1217            0 :       pc->qs = qs;
    1218            0 :       return GNUNET_SYSERR;
    1219              :     }
    1220              :   }
    1221              :   else
    1222              :   {
    1223            0 :     if ( (internal_checks) &&
    1224            0 :          (! ps->purse_deleted) )
    1225              :     {
    1226              :       enum GNUNET_DB_QueryStatus qs;
    1227              : 
    1228            0 :       qs = report_row_inconsistency (
    1229              :         "purse-delete",
    1230              :         deletion_serial_id,
    1231              :         "purse not marked as deleted (internal check)");
    1232            0 :       if (qs < 0)
    1233              :       {
    1234            0 :         GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1235            0 :         pc->qs = qs;
    1236            0 :         return GNUNET_SYSERR;
    1237              :       }
    1238              :     }
    1239              :   }
    1240            0 :   return GNUNET_OK;
    1241              : }
    1242              : 
    1243              : 
    1244              : /**
    1245              :  * Function called on expired purses.
    1246              :  *
    1247              :  * @param pc closure
    1248              :  * @param purse_pub public key of the purse
    1249              :  * @param balance amount of money in the purse
    1250              :  * @param expiration_date when did the purse expire?
    1251              :  * @return #GNUNET_OK to continue to iterate
    1252              :  */
    1253              : static enum GNUNET_GenericReturnValue
    1254            0 : handle_purse_expired (
    1255              :   struct PurseContext *pc,
    1256              :   const struct TALER_PurseContractPublicKeyP *purse_pub,
    1257              :   const struct TALER_Amount *balance,
    1258              :   struct GNUNET_TIME_Timestamp expiration_date)
    1259              : {
    1260              :   enum GNUNET_DB_QueryStatus qs;
    1261            0 :   struct TALER_AUDITORDB_PurseNotClosedInconsistencies pnci = {
    1262              :     .amount = *balance,
    1263              :     .expiration_date = expiration_date.abs_time,
    1264              :     .purse_pub = purse_pub->eddsa_pub
    1265              :   };
    1266              : 
    1267            0 :   qs = TALER_AUDITORDB_insert_purse_not_closed_inconsistencies (
    1268              :     TALER_ARL_adb,
    1269              :     &pnci);
    1270            0 :   if (qs < 0)
    1271              :   {
    1272            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1273            0 :     pc->qs = qs;
    1274            0 :     return GNUNET_SYSERR;
    1275              :   }
    1276            0 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1277              :               "Handling purse expiration `%s'\n",
    1278              :               TALER_B2S (purse_pub));
    1279            0 :   TALER_ARL_amount_add (&TALER_ARL_USE_AB (purse_total_delayed_decisions),
    1280              :                         &TALER_ARL_USE_AB (purse_total_delayed_decisions),
    1281              :                         balance);
    1282            0 :   return GNUNET_OK;
    1283              : }
    1284              : 
    1285              : 
    1286              : /**
    1287              :  * Check that the purse summary matches what the exchange database
    1288              :  * thinks about the purse, and update our own state of the purse.
    1289              :  *
    1290              :  * Remove all purses that we are happy with from the DB.
    1291              :  *
    1292              :  * @param cls our `struct PurseContext`
    1293              :  * @param key hash of the purse public key
    1294              :  * @param value a `struct PurseSummary`
    1295              :  * @return #GNUNET_OK to process more entries
    1296              :  */
    1297              : static enum GNUNET_GenericReturnValue
    1298            0 : verify_purse_balance (void *cls,
    1299              :                       const struct GNUNET_HashCode *key,
    1300              :                       void *value)
    1301              : {
    1302            0 :   struct PurseContext *pc = cls;
    1303            0 :   struct PurseSummary *ps = value;
    1304              :   enum GNUNET_DB_QueryStatus qs;
    1305              : 
    1306            0 :   if (internal_checks)
    1307              :   {
    1308              :     struct TALER_Amount pf;
    1309              :     struct TALER_Amount balance_without_purse_fee;
    1310              : 
    1311              :     /* subtract purse fee from ps->balance to get actual balance we expect, as
    1312              :        we track the balance including purse fee, while the exchange subtracts
    1313              :        the purse fee early on. */
    1314            0 :     qs = get_purse_fee (ps->creation_date,
    1315              :                         &pf);
    1316            0 :     if (qs < 0)
    1317              :     {
    1318            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1319            0 :       pc->qs = qs;
    1320            0 :       return GNUNET_SYSERR;
    1321              :     }
    1322            0 :     if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    1323            0 :       return GNUNET_OK; /* error already reported */
    1324            0 :     if (0 >
    1325            0 :         TALER_amount_subtract (&balance_without_purse_fee,
    1326            0 :                                &ps->balance,
    1327              :                                &pf))
    1328              :     {
    1329            0 :       qs = report_row_inconsistency ("purse",
    1330              :                                      0,
    1331              :                                      "purse fee higher than balance");
    1332            0 :       if (qs < 0)
    1333              :       {
    1334            0 :         GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1335            0 :         pc->qs = qs;
    1336            0 :         return GNUNET_SYSERR;
    1337              :       }
    1338            0 :       GNUNET_assert (GNUNET_OK ==
    1339              :                      TALER_amount_set_zero (TALER_ARL_currency,
    1340              :                                             &balance_without_purse_fee));
    1341              :     }
    1342              : 
    1343            0 :     if (0 != TALER_amount_cmp (&ps->exchange_balance,
    1344              :                                &balance_without_purse_fee))
    1345              :     {
    1346            0 :       qs = report_amount_arithmetic_inconsistency ("purse-balance",
    1347              :                                                    0,
    1348            0 :                                                    &ps->exchange_balance,
    1349              :                                                    &balance_without_purse_fee,
    1350              :                                                    0);
    1351            0 :       if (qs < 0)
    1352              :       {
    1353            0 :         GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1354            0 :         pc->qs = qs;
    1355            0 :         return GNUNET_SYSERR;
    1356              :       }
    1357              :     }
    1358              :   }
    1359              : 
    1360            0 :   if (ps->had_pi)
    1361              :   {
    1362            0 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1363              :                 "Updating purse `%s'\n",
    1364              :                 TALER_B2S (&ps->purse_pub));
    1365            0 :     qs = TALER_AUDITORDB_update_purse_info (TALER_ARL_adb,
    1366            0 :                                             &ps->purse_pub,
    1367            0 :                                             &ps->balance);
    1368              :   }
    1369              :   else
    1370              :   {
    1371            0 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1372              :                 "Inserting purse `%s'\n",
    1373              :                 TALER_B2S (&ps->purse_pub));
    1374            0 :     qs = TALER_AUDITORDB_insert_purse_info (TALER_ARL_adb,
    1375            0 :                                             &ps->purse_pub,
    1376            0 :                                             &ps->balance,
    1377            0 :                                             &ps->total_value,
    1378              :                                             ps->expiration_date);
    1379            0 :     ps->had_pi = true;
    1380              :   }
    1381            0 :   if (qs < 0)
    1382              :   {
    1383            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1384            0 :     pc->qs = qs;
    1385            0 :     return GNUNET_SYSERR;
    1386              :   }
    1387            0 :   GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs);
    1388            0 :   return GNUNET_OK;
    1389              : }
    1390              : 
    1391              : 
    1392              : /**
    1393              :  * Clear memory from the purses hash map.
    1394              :  *
    1395              :  * @param cls our `struct PurseContext`
    1396              :  * @param key hash of the purse public key
    1397              :  * @param value a `struct PurseSummary`
    1398              :  * @return #GNUNET_OK to process more entries
    1399              :  */
    1400              : static enum GNUNET_GenericReturnValue
    1401            0 : release_purse_balance (void *cls,
    1402              :                        const struct GNUNET_HashCode *key,
    1403              :                        void *value)
    1404              : {
    1405            0 :   struct PurseContext *pc = cls;
    1406            0 :   struct PurseSummary *ps = value;
    1407              : 
    1408            0 :   GNUNET_assert (GNUNET_YES ==
    1409              :                  GNUNET_CONTAINER_multihashmap_remove (pc->purses,
    1410              :                                                        key,
    1411              :                                                        ps));
    1412            0 :   GNUNET_free (ps);
    1413            0 :   return GNUNET_OK;
    1414              : }
    1415              : 
    1416              : 
    1417              : /**
    1418              :  * Analyze purses for being well-formed.
    1419              :  *
    1420              :  * @param cls NULL
    1421              :  * @return transaction status code
    1422              :  */
    1423              : static enum GNUNET_DB_QueryStatus
    1424           13 : analyze_purses (void *cls)
    1425              : {
    1426              :   struct PurseContext pc;
    1427              :   enum GNUNET_DB_QueryStatus qs;
    1428              : 
    1429              :   (void) cls;
    1430           13 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1431              :               "Analyzing purses\n");
    1432           13 :   qs = TALER_AUDITORDB_get_auditor_progress (
    1433              :     TALER_ARL_adb,
    1434              :     TALER_ARL_GET_PP (purse_account_merge_serial_id),
    1435              :     TALER_ARL_GET_PP (purse_decision_serial_id),
    1436              :     TALER_ARL_GET_PP (purse_deletion_serial_id),
    1437              :     TALER_ARL_GET_PP (purse_deposits_serial_id),
    1438              :     TALER_ARL_GET_PP (purse_merges_serial_id),
    1439              :     TALER_ARL_GET_PP (purse_request_serial_id),
    1440              :     TALER_ARL_GET_PP (purse_open_counter),
    1441              :     NULL);
    1442           13 :   if (0 > qs)
    1443              :   {
    1444            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1445            0 :     return qs;
    1446              :   }
    1447           13 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1448              :               "Resuming purse audit at %llu/%llu/%llu/%llu/%llu/%llu/%llu\n",
    1449              :               (unsigned long long) TALER_ARL_USE_PP (
    1450              :                 purse_open_counter),
    1451              :               (unsigned long long) TALER_ARL_USE_PP (
    1452              :                 purse_request_serial_id),
    1453              :               (unsigned long long) TALER_ARL_USE_PP (
    1454              :                 purse_decision_serial_id),
    1455              :               (unsigned long long) TALER_ARL_USE_PP (
    1456              :                 purse_deletion_serial_id),
    1457              :               (unsigned long long) TALER_ARL_USE_PP (
    1458              :                 purse_merges_serial_id),
    1459              :               (unsigned long long) TALER_ARL_USE_PP (
    1460              :                 purse_deposits_serial_id),
    1461              :               (unsigned long long) TALER_ARL_USE_PP (
    1462              :                 purse_account_merge_serial_id));
    1463           13 :   pc.qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
    1464           13 :   qs = TALER_AUDITORDB_get_balance (
    1465              :     TALER_ARL_adb,
    1466              :     TALER_ARL_GET_AB (purse_global_balance),
    1467              :     TALER_ARL_GET_AB (purse_total_balance_insufficient_loss),
    1468              :     TALER_ARL_GET_AB (purse_total_delayed_decisions),
    1469              :     TALER_ARL_GET_AB (purse_total_balance_purse_not_closed),
    1470              :     TALER_ARL_GET_AB (purse_total_arithmetic_delta_plus),
    1471              :     TALER_ARL_GET_AB (purse_total_arithmetic_delta_minus),
    1472              :     TALER_ARL_GET_AB (purse_total_bad_sig_loss),
    1473              :     NULL);
    1474           13 :   if (qs < 0)
    1475              :   {
    1476            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1477            0 :     return qs;
    1478              :   }
    1479           13 :   pc.purses = GNUNET_CONTAINER_multihashmap_create (512,
    1480              :                                                     GNUNET_NO);
    1481              : 
    1482           13 :   qs = TALER_EXCHANGEDB_iterate_purse_requests_above_serial_id (
    1483              :     TALER_ARL_edb,
    1484              :     TALER_ARL_USE_PP (purse_request_serial_id),
    1485              :     &handle_purse_requested,
    1486              :     &pc);
    1487           13 :   if (qs < 0)
    1488              :   {
    1489            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1490            0 :     return qs;
    1491              :   }
    1492           13 :   if (pc.qs < 0)
    1493              :   {
    1494            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == pc.qs);
    1495            0 :     return pc.qs;
    1496              :   }
    1497           13 :   qs = TALER_EXCHANGEDB_iterate_purse_merges_above_serial_id (
    1498              :     TALER_ARL_edb,
    1499              :     TALER_ARL_USE_PP (purse_merges_serial_id),
    1500              :     &handle_purse_merged,
    1501              :     &pc);
    1502           13 :   if (qs < 0)
    1503              :   {
    1504            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1505            0 :     return qs;
    1506              :   }
    1507           13 :   if (pc.qs < 0)
    1508              :   {
    1509            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == pc.qs);
    1510            0 :     return pc.qs;
    1511              :   }
    1512              : 
    1513           13 :   qs = TALER_EXCHANGEDB_iterate_purse_deposits_above_serial_id (
    1514              :     TALER_ARL_edb,
    1515              :     TALER_ARL_USE_PP (purse_deposits_serial_id),
    1516              :     &handle_purse_deposits,
    1517              :     &pc);
    1518           13 :   if (qs < 0)
    1519              :   {
    1520            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1521            0 :     return qs;
    1522              :   }
    1523           13 :   if (pc.qs < 0)
    1524              :   {
    1525            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == pc.qs);
    1526            0 :     return pc.qs;
    1527              :   }
    1528              : 
    1529              :   /* Charge purse fee! */
    1530           13 :   qs = TALER_EXCHANGEDB_iterate_account_merges_above_serial_id (
    1531              :     TALER_ARL_edb,
    1532              :     TALER_ARL_USE_PP (purse_account_merge_serial_id),
    1533              :     &handle_account_merged,
    1534              :     &pc);
    1535           13 :   if (qs < 0)
    1536              :   {
    1537            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1538            0 :     return qs;
    1539              :   }
    1540           13 :   if (pc.qs < 0)
    1541              :   {
    1542            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == pc.qs);
    1543            0 :     return pc.qs;
    1544              :   }
    1545              : 
    1546           13 :   qs = TALER_EXCHANGEDB_iterate_all_purse_decisions_above_serial_id (
    1547              :     TALER_ARL_edb,
    1548              :     TALER_ARL_USE_PP (purse_decision_serial_id),
    1549              :     &handle_purse_decision,
    1550              :     &pc);
    1551           13 :   if (qs < 0)
    1552              :   {
    1553            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1554            0 :     return qs;
    1555              :   }
    1556           13 :   if (pc.qs < 0)
    1557              :   {
    1558            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == pc.qs);
    1559            0 :     return pc.qs;
    1560              :   }
    1561              : 
    1562           13 :   qs = TALER_EXCHANGEDB_iterate_all_purse_deletions_above_serial_id (
    1563              :     TALER_ARL_edb,
    1564              :     TALER_ARL_USE_PP (purse_deletion_serial_id),
    1565              :     &handle_purse_deletion,
    1566              :     &pc);
    1567           13 :   if (qs < 0)
    1568              :   {
    1569            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1570            0 :     return qs;
    1571              :   }
    1572           13 :   if (pc.qs < 0)
    1573              :   {
    1574            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == pc.qs);
    1575            0 :     return pc.qs;
    1576              :   }
    1577              : 
    1578           13 :   qs = TALER_AUDITORDB_iterate_purse_expired (
    1579              :     TALER_ARL_adb,
    1580              :     &handle_purse_expired,
    1581              :     &pc);
    1582           13 :   if (qs < 0)
    1583              :   {
    1584            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1585            0 :     return qs;
    1586              :   }
    1587           13 :   if (pc.qs < 0)
    1588              :   {
    1589            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == pc.qs);
    1590            0 :     return pc.qs;
    1591              :   }
    1592              : 
    1593           13 :   GNUNET_CONTAINER_multihashmap_iterate (pc.purses,
    1594              :                                          &verify_purse_balance,
    1595              :                                          &pc);
    1596           13 :   GNUNET_CONTAINER_multihashmap_iterate (pc.purses,
    1597              :                                          &release_purse_balance,
    1598              :                                          &pc);
    1599           13 :   GNUNET_break (0 ==
    1600              :                 GNUNET_CONTAINER_multihashmap_size (pc.purses));
    1601           13 :   GNUNET_CONTAINER_multihashmap_destroy (pc.purses);
    1602           13 :   if (pc.qs < 0)
    1603              :   {
    1604            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == pc.qs);
    1605            0 :     return pc.qs;
    1606              :   }
    1607           13 :   qs = TALER_AUDITORDB_insert_balance (
    1608              :     TALER_ARL_adb,
    1609              :     TALER_ARL_SET_AB (purse_global_balance),
    1610              :     TALER_ARL_SET_AB (purse_total_balance_insufficient_loss),
    1611              :     TALER_ARL_SET_AB (purse_total_delayed_decisions),
    1612              :     TALER_ARL_SET_AB (purse_total_balance_purse_not_closed),
    1613              :     TALER_ARL_SET_AB (purse_total_arithmetic_delta_plus),
    1614              :     TALER_ARL_SET_AB (purse_total_arithmetic_delta_minus),
    1615              :     TALER_ARL_SET_AB (purse_total_bad_sig_loss),
    1616              :     NULL);
    1617           13 :   if (0 > qs)
    1618              :   {
    1619            3 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1620              :                 "Failed to update auditor DB, not recording progress\n");
    1621            3 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1622            3 :     return qs;
    1623              :   }
    1624           10 :   qs = TALER_AUDITORDB_insert_auditor_progress (
    1625              :     TALER_ARL_adb,
    1626              :     TALER_ARL_SET_PP (purse_account_merge_serial_id),
    1627              :     TALER_ARL_SET_PP (purse_decision_serial_id),
    1628              :     TALER_ARL_SET_PP (purse_deletion_serial_id),
    1629              :     TALER_ARL_SET_PP (purse_deposits_serial_id),
    1630              :     TALER_ARL_SET_PP (purse_merges_serial_id),
    1631              :     TALER_ARL_SET_PP (purse_request_serial_id),
    1632              :     TALER_ARL_SET_PP (purse_open_counter),
    1633              :     NULL);
    1634           10 :   if (0 > qs)
    1635              :   {
    1636            0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1637              :                 "Failed to update auditor DB, not recording progress\n");
    1638            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1639            0 :     return qs;
    1640              :   }
    1641           10 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1642              :               "Concluded purse audit step at %llu/%llu/%llu/%llu/%llu/%llu\n",
    1643              :               (unsigned long long) TALER_ARL_USE_PP (
    1644              :                 purse_request_serial_id),
    1645              :               (unsigned long long) TALER_ARL_USE_PP (
    1646              :                 purse_decision_serial_id),
    1647              :               (unsigned long long) TALER_ARL_USE_PP (
    1648              :                 purse_deletion_serial_id),
    1649              :               (unsigned long long) TALER_ARL_USE_PP (
    1650              :                 purse_merges_serial_id),
    1651              :               (unsigned long long) TALER_ARL_USE_PP (
    1652              :                 purse_deposits_serial_id),
    1653              :               (unsigned long long) TALER_ARL_USE_PP (
    1654              :                 purse_account_merge_serial_id));
    1655           10 :   return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
    1656              : }
    1657              : 
    1658              : 
    1659              : /**
    1660              :  * Function called on events received from Postgres.
    1661              :  *
    1662              :  * @param cls closure, NULL
    1663              :  * @param extra additional event data provided
    1664              :  * @param extra_size number of bytes in @a extra
    1665              :  */
    1666              : static void
    1667            0 : db_notify (void *cls,
    1668              :            const void *extra,
    1669              :            size_t extra_size)
    1670              : {
    1671              :   (void) cls;
    1672              :   (void) extra;
    1673              :   (void) extra_size;
    1674              : 
    1675            0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1676              :               "Received notification to wake purses\n");
    1677            0 :   if (GNUNET_OK !=
    1678            0 :       TALER_ARL_setup_sessions_and_run (&analyze_purses,
    1679              :                                         NULL))
    1680              :   {
    1681            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1682              :                 "Audit failed\n");
    1683            0 :     GNUNET_SCHEDULER_shutdown ();
    1684            0 :     global_ret = EXIT_FAILURE;
    1685            0 :     return;
    1686              :   }
    1687              : }
    1688              : 
    1689              : 
    1690              : /**
    1691              :  * Function called on shutdown.
    1692              :  */
    1693              : static void
    1694           10 : do_shutdown (void *cls)
    1695              : {
    1696              :   (void) cls;
    1697              : 
    1698           10 :   if (NULL != eh)
    1699              :   {
    1700            4 :     TALER_AUDITORDB_event_listen_cancel (eh);
    1701            4 :     eh = NULL;
    1702              :   }
    1703           10 :   TALER_ARL_done ();
    1704           10 : }
    1705              : 
    1706              : 
    1707              : /**
    1708              :  * Main function that will be run.
    1709              :  *
    1710              :  * @param cls closure
    1711              :  * @param args remaining command-line arguments
    1712              :  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
    1713              :  * @param c configuration
    1714              :  */
    1715              : static void
    1716           10 : run (void *cls,
    1717              :      char *const *args,
    1718              :      const char *cfgfile,
    1719              :      const struct GNUNET_CONFIGURATION_Handle *c)
    1720              : {
    1721              :   (void) cls;
    1722              :   (void) args;
    1723              :   (void) cfgfile;
    1724              : 
    1725           10 :   cfg = c;
    1726           10 :   GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
    1727              :                                  NULL);
    1728           10 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1729              :               "Launching purses auditor\n");
    1730           10 :   if (EXIT_SUCCESS !=
    1731           10 :       (global_ret = TALER_ARL_init (c)))
    1732              :   {
    1733            0 :     return;
    1734              :   }
    1735           10 :   if (test_mode != 1)
    1736              :   {
    1737            4 :     struct GNUNET_DB_EventHeaderP es = {
    1738            4 :       .size = htons (sizeof (es)),
    1739            4 :       .type = htons (TALER_DBEVENT_EXCHANGE_AUDITOR_WAKE_HELPER_PURSES)
    1740              :     };
    1741              : 
    1742            4 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1743              :                 "Running helper indefinitely\n");
    1744            4 :     eh = TALER_AUDITORDB_event_listen (TALER_ARL_adb,
    1745              :                                        &es,
    1746            4 :                                        GNUNET_TIME_UNIT_FOREVER_REL,
    1747              :                                        &db_notify,
    1748              :                                        NULL);
    1749              :   }
    1750           10 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1751              :               "Starting audit\n");
    1752           10 :   if (GNUNET_OK !=
    1753           10 :       TALER_ARL_setup_sessions_and_run (&analyze_purses,
    1754              :                                         NULL))
    1755              :   {
    1756            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1757              :                 "Audit failed\n");
    1758            0 :     GNUNET_SCHEDULER_shutdown ();
    1759            0 :     global_ret = EXIT_FAILURE;
    1760            0 :     return;
    1761              :   }
    1762              : }
    1763              : 
    1764              : 
    1765              : /**
    1766              :  * The main function to check the database's handling of purses.
    1767              :  *
    1768              :  * @param argc number of arguments from the command line
    1769              :  * @param argv command line arguments
    1770              :  * @return 0 ok, 1 on error
    1771              :  */
    1772              : int
    1773           10 : main (int argc,
    1774              :       char *const *argv)
    1775              : {
    1776           10 :   const struct GNUNET_GETOPT_CommandLineOption options[] = {
    1777           10 :     GNUNET_GETOPT_option_flag ('i',
    1778              :                                "internal",
    1779              :                                "perform checks only applicable for exchange-internal audits",
    1780              :                                &internal_checks),
    1781           10 :     GNUNET_GETOPT_option_flag ('t',
    1782              :                                "test",
    1783              :                                "run in test mode and exit when idle",
    1784              :                                &test_mode),
    1785           10 :     GNUNET_GETOPT_option_timetravel ('T',
    1786              :                                      "timetravel"),
    1787              :     GNUNET_GETOPT_OPTION_END
    1788              :   };
    1789              :   enum GNUNET_GenericReturnValue ret;
    1790              : 
    1791           10 :   ret = GNUNET_PROGRAM_run (
    1792              :     TALER_AUDITOR_project_data (),
    1793              :     argc,
    1794              :     argv,
    1795              :     "taler-helper-auditor-purses",
    1796              :     gettext_noop ("Audit Taler exchange purse handling"),
    1797              :     options,
    1798              :     &run,
    1799              :     NULL);
    1800           10 :   if (GNUNET_SYSERR == ret)
    1801            0 :     return EXIT_NOTCONFIGURED;
    1802           10 :   if (GNUNET_NO == ret)
    1803            0 :     return EXIT_SUCCESS;
    1804           10 :   return global_ret;
    1805              : }
    1806              : 
    1807              : 
    1808              : /* end of taler-helper-auditor-purses.c */
        

Generated by: LCOV version 2.0-1