LCOV - code coverage report
Current view: top level - auditor - taler-helper-auditor-purses.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 15.1 % 496 75
Test Date: 2026-09-09 15:11:34 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            0 :       .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              :     .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 :     TALER_ARL_amount_subtract (&amount_minus_fee,
     691              :                                &deposit->amount,
     692              :                                &issue->fees.deposit);
     693              :   }
     694              : 
     695            0 :   if (GNUNET_OK !=
     696            0 :       TALER_wallet_purse_deposit_verify (base_url,
     697              :                                          &deposit->purse_pub,
     698              :                                          &deposit->amount,
     699              :                                          &h_denom_pub,
     700              :                                          &deposit->h_age_commitment,
     701              :                                          &deposit->coin_pub,
     702              :                                          &deposit->coin_sig))
     703              :   {
     704            0 :     struct TALER_AUDITORDB_BadSigLosses bsl = {
     705              :       .problem_row_id = rowid,
     706              :       .operation = (char *) "purse-deposit",
     707              :       .loss = deposit->amount,
     708              :       .operation_specific_pub = deposit->coin_pub.eddsa_pub
     709              :     };
     710              :     enum GNUNET_DB_QueryStatus qs;
     711              : 
     712            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     713              :                 "Failed to verify purse deposit signature on `%s' in record %llu\n",
     714              :                 TALER_B2S (&deposit->purse_pub),
     715              :                 (unsigned long long) rowid);
     716            0 :     qs = TALER_AUDITORDB_insert_bad_sig_losses (
     717              :       TALER_ARL_adb,
     718              :       &bsl);
     719            0 :     if (qs < 0)
     720              :     {
     721            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     722            0 :       pc->qs = qs;
     723            0 :       return GNUNET_SYSERR;
     724              :     }
     725            0 :     TALER_ARL_amount_add (&TALER_ARL_USE_AB (purse_total_bad_sig_loss),
     726              :                           &TALER_ARL_USE_AB (purse_total_bad_sig_loss),
     727              :                           &deposit->amount);
     728            0 :     return GNUNET_OK;
     729              :   }
     730              : 
     731              :   {
     732              :     struct PurseSummary *ps;
     733              : 
     734            0 :     ps = setup_purse (pc,
     735              :                       &deposit->purse_pub);
     736            0 :     if (NULL == ps)
     737              :     {
     738              :       enum GNUNET_DB_QueryStatus qs;
     739              : 
     740            0 :       if (0 > pc->qs)
     741              :       {
     742            0 :         GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == pc->qs);
     743            0 :         return GNUNET_SYSERR;
     744              :       }
     745            0 :       GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == pc->qs);
     746            0 :       qs = report_row_inconsistency ("purse_deposit",
     747              :                                      rowid,
     748              :                                      "purse not found");
     749            0 :       if (0 > qs)
     750              :       {
     751            0 :         GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     752            0 :         pc->qs = qs;
     753            0 :         return GNUNET_SYSERR;
     754              :       }
     755            0 :       return GNUNET_OK;
     756              :     }
     757            0 :     TALER_ARL_amount_add (&ps->balance,
     758              :                           &ps->balance,
     759              :                           &amount_minus_fee);
     760            0 :     TALER_ARL_amount_add (&TALER_ARL_USE_AB (purse_global_balance),
     761              :                           &TALER_ARL_USE_AB (purse_global_balance),
     762              :                           &amount_minus_fee);
     763              :   }
     764            0 :   return GNUNET_OK;
     765              : }
     766              : 
     767              : 
     768              : /**
     769              :  * Function called with details about purse merges that have been made, with
     770              :  * the goal of auditing the purse merge execution.
     771              :  *
     772              :  * @param pc closure
     773              :  * @param rowid unique serial ID for the deposit in our DB
     774              :  * @param partner_base_url where is the reserve, NULL for this exchange
     775              :  * @param amount total amount expected in the purse
     776              :  * @param balance current balance in the purse
     777              :  * @param flags purse flags
     778              :  * @param merge_pub merge capability key
     779              :  * @param reserve_pub reserve the merge affects
     780              :  * @param merge_sig signature affirming the merge
     781              :  * @param purse_pub purse key
     782              :  * @param merge_timestamp when did the merge happen
     783              :  * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
     784              :  */
     785              : static enum GNUNET_GenericReturnValue
     786            0 : handle_purse_merged (
     787              :   struct PurseContext *pc,
     788              :   uint64_t rowid,
     789              :   const char *partner_base_url,
     790              :   const struct TALER_Amount *amount,
     791              :   const struct TALER_Amount *balance,
     792              :   enum TALER_WalletAccountMergeFlags flags,
     793              :   const struct TALER_PurseMergePublicKeyP *merge_pub,
     794              :   const struct TALER_ReservePublicKeyP *reserve_pub,
     795              :   const struct TALER_PurseMergeSignatureP *merge_sig,
     796              :   const struct TALER_PurseContractPublicKeyP *purse_pub,
     797              :   struct GNUNET_TIME_Timestamp merge_timestamp)
     798              : {
     799              :   struct PurseSummary *ps;
     800              :   enum GNUNET_DB_QueryStatus qs;
     801              : 
     802              :   /* should be monotonically increasing */
     803            0 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     804              :               "Handling purse merged `%s'\n",
     805              :               TALER_B2S (purse_pub));
     806            0 :   GNUNET_assert (rowid >= TALER_ARL_USE_PP (purse_merges_serial_id));
     807            0 :   TALER_ARL_USE_PP (purse_merges_serial_id) = rowid + 1;
     808              : 
     809              :   {
     810              :     struct TALER_NormalizedPayto reserve_url;
     811              : 
     812              :     reserve_url
     813            0 :       = TALER_reserve_make_payto (NULL == partner_base_url
     814              :                                   ? TALER_ARL_exchange_url
     815              :                                   : partner_base_url,
     816              :                                   reserve_pub);
     817            0 :     if (GNUNET_OK !=
     818            0 :         TALER_wallet_purse_merge_verify (reserve_url,
     819              :                                          merge_timestamp,
     820              :                                          purse_pub,
     821              :                                          merge_pub,
     822              :                                          merge_sig))
     823              :     {
     824            0 :       struct TALER_AUDITORDB_BadSigLosses bsl = {
     825              :         .problem_row_id = rowid,
     826              :         .operation = (char *) "merge-purse",
     827              :         .loss = *amount,
     828              :         .operation_specific_pub = merge_pub->eddsa_pub
     829              :       };
     830              : 
     831            0 :       GNUNET_free (reserve_url.normalized_payto);
     832            0 :       qs = TALER_AUDITORDB_insert_bad_sig_losses (
     833              :         TALER_ARL_adb,
     834              :         &bsl);
     835            0 :       if (qs < 0)
     836              :       {
     837            0 :         GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     838            0 :         pc->qs = qs;
     839            0 :         return GNUNET_SYSERR;
     840              :       }
     841            0 :       TALER_ARL_amount_add (&TALER_ARL_USE_AB (purse_total_bad_sig_loss),
     842              :                             &TALER_ARL_USE_AB (purse_total_bad_sig_loss),
     843              :                             amount);
     844            0 :       return GNUNET_OK;
     845              :     }
     846            0 :     GNUNET_free (reserve_url.normalized_payto);
     847              :   }
     848              : 
     849            0 :   ps = setup_purse (pc,
     850              :                     purse_pub);
     851            0 :   if (NULL == ps)
     852              :   {
     853            0 :     if (pc->qs < 0)
     854              :     {
     855            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == pc->qs);
     856            0 :       return GNUNET_SYSERR;
     857              :     }
     858            0 :     GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == pc->qs);
     859            0 :     qs = report_row_inconsistency ("purse-merge",
     860              :                                    rowid,
     861              :                                    "purse not found");
     862            0 :     if (qs < 0)
     863              :     {
     864            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     865            0 :       pc->qs = qs;
     866            0 :       return GNUNET_SYSERR;
     867              :     }
     868            0 :     return GNUNET_OK;
     869              :   }
     870            0 :   GNUNET_break (0 ==
     871              :                 GNUNET_TIME_timestamp_cmp (merge_timestamp,
     872              :                                            ==,
     873              :                                            ps->merge_timestamp));
     874            0 :   TALER_ARL_amount_add (&ps->balance,
     875              :                         &ps->balance,
     876              :                         amount);
     877            0 :   return GNUNET_OK;
     878              : }
     879              : 
     880              : 
     881              : /**
     882              :  * Function called with details about account merge requests that have been
     883              :  * made, with the goal of auditing the account merge execution.
     884              :  *
     885              :  * @param pc closure
     886              :  * @param rowid unique serial ID for the deposit in our DB
     887              :  * @param reserve_pub reserve affected by the merge
     888              :  * @param purse_pub purse being merged
     889              :  * @param h_contract_terms hash over contract of the purse
     890              :  * @param purse_expiration when would the purse expire
     891              :  * @param amount total amount in the purse
     892              :  * @param min_age minimum age of all coins deposited into the purse
     893              :  * @param flags how was the purse created
     894              :  * @param purse_fee if a purse fee was paid, how high is it
     895              :  * @param merge_timestamp when was the merge approved
     896              :  * @param reserve_sig signature by reserve approving the merge
     897              :  * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
     898              :  */
     899              : static enum GNUNET_GenericReturnValue
     900            0 : handle_account_merged (
     901              :   struct PurseContext *pc,
     902              :   uint64_t rowid,
     903              :   const struct TALER_ReservePublicKeyP *reserve_pub,
     904              :   const struct TALER_PurseContractPublicKeyP *purse_pub,
     905              :   const struct TALER_PrivateContractHashP *h_contract_terms,
     906              :   struct GNUNET_TIME_Timestamp purse_expiration,
     907              :   const struct TALER_Amount *amount,
     908              :   uint32_t min_age,
     909              :   enum TALER_WalletAccountMergeFlags flags,
     910              :   const struct TALER_Amount *purse_fee,
     911              :   struct GNUNET_TIME_Timestamp merge_timestamp,
     912              :   const struct TALER_ReserveSignatureP *reserve_sig)
     913              : {
     914              :   struct PurseSummary *ps;
     915              :   enum GNUNET_DB_QueryStatus qs;
     916              : 
     917              :   /* should be monotonically increasing */
     918            0 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     919              :               "Handling account merge on purse `%s'\n",
     920              :               TALER_B2S (purse_pub));
     921            0 :   GNUNET_assert (rowid >= TALER_ARL_USE_PP (purse_account_merge_serial_id));
     922            0 :   TALER_ARL_USE_PP (purse_account_merge_serial_id) = rowid + 1;
     923            0 :   if (GNUNET_OK !=
     924            0 :       TALER_wallet_account_merge_verify (merge_timestamp,
     925              :                                          purse_pub,
     926              :                                          purse_expiration,
     927              :                                          h_contract_terms,
     928              :                                          amount,
     929              :                                          purse_fee,
     930              :                                          min_age,
     931              :                                          flags,
     932              :                                          reserve_pub,
     933              :                                          reserve_sig))
     934              :   {
     935            0 :     struct TALER_AUDITORDB_BadSigLosses bsl = {
     936              :       .problem_row_id = rowid,
     937              :       .operation = (char *) "account-merge",
     938              :       .loss = *purse_fee,
     939              :       .operation_specific_pub = reserve_pub->eddsa_pub
     940              :     };
     941              : 
     942            0 :     qs = TALER_AUDITORDB_insert_bad_sig_losses (
     943              :       TALER_ARL_adb,
     944              :       &bsl);
     945            0 :     if (qs < 0)
     946              :     {
     947            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     948            0 :       pc->qs = qs;
     949            0 :       return GNUNET_SYSERR;
     950              :     }
     951            0 :     TALER_ARL_amount_add (&TALER_ARL_USE_AB (purse_total_bad_sig_loss),
     952              :                           &TALER_ARL_USE_AB (purse_total_bad_sig_loss),
     953              :                           purse_fee);
     954            0 :     return GNUNET_OK;
     955              :   }
     956            0 :   ps = setup_purse (pc,
     957              :                     purse_pub);
     958            0 :   if (NULL == ps)
     959              :   {
     960            0 :     if (0 > pc->qs)
     961              :     {
     962            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == pc->qs);
     963            0 :       return GNUNET_SYSERR;
     964              :     }
     965            0 :     GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == pc->qs);
     966            0 :     qs = report_row_inconsistency ("account-merge",
     967              :                                    rowid,
     968              :                                    "purse not found");
     969            0 :     if (0 > qs)
     970              :     {
     971            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     972            0 :       pc->qs = qs;
     973            0 :       return GNUNET_SYSERR;
     974              :     }
     975            0 :     return GNUNET_OK;
     976              :   }
     977            0 :   TALER_ARL_amount_add (&TALER_ARL_USE_AB (purse_global_balance),
     978              :                         &TALER_ARL_USE_AB (purse_global_balance),
     979              :                         purse_fee);
     980            0 :   TALER_ARL_amount_add (&ps->balance,
     981              :                         &ps->balance,
     982              :                         purse_fee);
     983            0 :   return GNUNET_OK;
     984              : }
     985              : 
     986              : 
     987              : /**
     988              :  * Function called with details about purse decisions that have been made.
     989              :  *
     990              :  * @param pc closure
     991              :  * @param rowid unique serial ID for the deposit in our DB
     992              :  * @param purse_pub which purse was the decision made on
     993              :  * @param refunded true if decision was to refund
     994              :  * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
     995              :  */
     996              : static enum GNUNET_GenericReturnValue
     997            0 : handle_purse_decision (
     998              :   struct PurseContext *pc,
     999              :   uint64_t rowid,
    1000              :   const struct TALER_PurseContractPublicKeyP *purse_pub,
    1001              :   bool refunded)
    1002              : {
    1003              :   struct PurseSummary *ps;
    1004              :   struct GNUNET_HashCode key;
    1005              :   enum GNUNET_DB_QueryStatus qs;
    1006              :   struct TALER_Amount purse_fee;
    1007              :   struct TALER_Amount balance_without_purse_fee;
    1008              : 
    1009            0 :   GNUNET_assert (rowid >= TALER_ARL_USE_PP (purse_decision_serial_id));
    1010            0 :   TALER_ARL_USE_PP (purse_decision_serial_id) = rowid + 1;
    1011            0 :   ps = setup_purse (pc,
    1012              :                     purse_pub);
    1013            0 :   if (NULL == ps)
    1014              :   {
    1015            0 :     if (0 > pc->qs)
    1016              :     {
    1017            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == pc->qs);
    1018            0 :       return GNUNET_SYSERR;
    1019              :     }
    1020            0 :     qs = report_row_inconsistency ("purse-decision",
    1021              :                                    rowid,
    1022              :                                    "purse not found");
    1023            0 :     if (0 > qs)
    1024              :     {
    1025            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1026            0 :       pc->qs = qs;
    1027            0 :       return GNUNET_SYSERR;
    1028              :     }
    1029            0 :     return GNUNET_OK;
    1030              :   }
    1031            0 :   qs = get_purse_fee (ps->creation_date,
    1032              :                       &purse_fee);
    1033            0 :   if (0 > qs)
    1034              :   {
    1035            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1036            0 :     pc->qs = qs;
    1037            0 :     return GNUNET_SYSERR;
    1038              :   }
    1039            0 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    1040            0 :     return GNUNET_OK; /* already reported */
    1041            0 :   if (0 >
    1042            0 :       TALER_amount_subtract (&balance_without_purse_fee,
    1043            0 :                              &ps->balance,
    1044              :                              &purse_fee))
    1045              :   {
    1046            0 :     qs = report_row_inconsistency ("purse-request",
    1047              :                                    rowid,
    1048              :                                    "purse fee higher than balance");
    1049            0 :     if (0 > qs)
    1050              :     {
    1051            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1052            0 :       pc->qs = qs;
    1053            0 :       return GNUNET_SYSERR;
    1054              :     }
    1055            0 :     GNUNET_assert (GNUNET_OK ==
    1056              :                    TALER_amount_set_zero (TALER_ARL_currency,
    1057              :                                           &balance_without_purse_fee));
    1058              :   }
    1059              : 
    1060            0 :   if (refunded)
    1061              :   {
    1062            0 :     if (-1 != TALER_amount_cmp (&balance_without_purse_fee,
    1063            0 :                                 &ps->total_value))
    1064              :     {
    1065            0 :       qs = report_amount_arithmetic_inconsistency ("purse-decision: refund",
    1066              :                                                    rowid,
    1067              :                                                    &balance_without_purse_fee,
    1068            0 :                                                    &ps->total_value,
    1069              :                                                    0);
    1070            0 :       if (0 > qs)
    1071              :       {
    1072            0 :         GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1073            0 :         pc->qs = qs;
    1074            0 :         return GNUNET_SYSERR;
    1075              :       }
    1076              :     }
    1077            0 :     if ( (internal_checks) &&
    1078            0 :          (! ps->purse_refunded) )
    1079              :     {
    1080            0 :       qs = report_row_inconsistency (
    1081              :         "purse-decision",
    1082              :         rowid,
    1083              :         "purse not marked as refunded (internal check)");
    1084            0 :       if (qs < 0)
    1085              :       {
    1086            0 :         GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1087            0 :         pc->qs = qs;
    1088            0 :         return GNUNET_SYSERR;
    1089              :       }
    1090              :     }
    1091              :   }
    1092              :   else
    1093              :   {
    1094            0 :     if (-1 == TALER_amount_cmp (&balance_without_purse_fee,
    1095            0 :                                 &ps->total_value))
    1096              :     {
    1097            0 :       qs = report_amount_arithmetic_inconsistency ("purse-decision: merge",
    1098              :                                                    rowid,
    1099            0 :                                                    &ps->total_value,
    1100              :                                                    &balance_without_purse_fee,
    1101              :                                                    0);
    1102            0 :       if (0 > qs)
    1103              :       {
    1104            0 :         GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1105            0 :         pc->qs = qs;
    1106            0 :         return GNUNET_SYSERR;
    1107              :       }
    1108            0 :       TALER_ARL_amount_add (&TALER_ARL_USE_AB (
    1109              :                               purse_total_balance_insufficient_loss),
    1110              :                             &TALER_ARL_USE_AB (
    1111              :                               purse_total_balance_insufficient_loss),
    1112              :                             &ps->total_value);
    1113              :     }
    1114              :   }
    1115              : 
    1116            0 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1117              :               "Deleting purse with decision `%s'\n",
    1118              :               TALER_B2S (&ps->purse_pub));
    1119              :   /* The purse ceases to exist, so the money it held is no longer
    1120              :      part of the balance the exchange holds in open purses. */
    1121            0 :   if (0 >
    1122            0 :       TALER_amount_subtract (&TALER_ARL_USE_AB (purse_global_balance),
    1123              :                              &TALER_ARL_USE_AB (purse_global_balance),
    1124            0 :                              &ps->balance))
    1125              :   {
    1126              :     /* Should be impossible: we added this balance to the global
    1127              :        balance when we learned about it. */
    1128            0 :     GNUNET_break (0);
    1129            0 :     GNUNET_assert (GNUNET_OK ==
    1130              :                    TALER_amount_set_zero (TALER_ARL_currency,
    1131              :                                           &TALER_ARL_USE_AB (
    1132              :                                             purse_global_balance)));
    1133              :   }
    1134            0 :   qs = TALER_AUDITORDB_delete_purse_info (TALER_ARL_adb,
    1135              :                                           purse_pub);
    1136            0 :   if (qs < 0)
    1137              :   {
    1138            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1139            0 :     pc->qs = qs;
    1140            0 :     return GNUNET_SYSERR;
    1141              :   }
    1142            0 :   GNUNET_CRYPTO_hash (purse_pub,
    1143              :                       sizeof (*purse_pub),
    1144              :                       &key);
    1145            0 :   GNUNET_assert (GNUNET_YES ==
    1146              :                  GNUNET_CONTAINER_multihashmap_remove (pc->purses,
    1147              :                                                        &key,
    1148              :                                                        ps));
    1149            0 :   GNUNET_free (ps);
    1150            0 :   return GNUNET_OK;
    1151              : }
    1152              : 
    1153              : 
    1154              : /**
    1155              :  * Function called on explicitly deleted purses.
    1156              :  *
    1157              :  * @param pc closure
    1158              :  * @param deletion_serial_id row ID with the deletion data
    1159              :  * @param purse_pub public key of the purse
    1160              :  * @param purse_sig signature affirming deletion of the purse
    1161              :  * @return #GNUNET_OK to continue to iterate
    1162              :  */
    1163              : static enum GNUNET_GenericReturnValue
    1164            0 : handle_purse_deletion (
    1165              :   struct PurseContext *pc,
    1166              :   uint64_t deletion_serial_id,
    1167              :   const struct TALER_PurseContractPublicKeyP *purse_pub,
    1168              :   const struct TALER_PurseContractSignatureP *purse_sig)
    1169              : {
    1170              :   struct PurseSummary *ps;
    1171              : 
    1172            0 :   GNUNET_assert (deletion_serial_id >=
    1173              :                  TALER_ARL_USE_PP (purse_deletion_serial_id));
    1174            0 :   TALER_ARL_USE_PP (purse_deletion_serial_id) = deletion_serial_id + 1;
    1175            0 :   ps = setup_purse (pc,
    1176              :                     purse_pub);
    1177            0 :   if (NULL == ps)
    1178              :   {
    1179            0 :     GNUNET_break (0);
    1180            0 :     return GNUNET_SYSERR;
    1181              :   }
    1182            0 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1183              :               "Handling purse `%s' deletion\n",
    1184              :               TALER_B2S (purse_pub));
    1185            0 :   if (GNUNET_OK !=
    1186            0 :       TALER_wallet_purse_delete_verify (purse_pub,
    1187              :                                         purse_sig))
    1188              :   {
    1189              :     enum GNUNET_DB_QueryStatus qs;
    1190              : 
    1191            0 :     qs = report_row_inconsistency (
    1192              :       "purse-delete",
    1193              :       deletion_serial_id,
    1194              :       "purse deletion signature invalid");
    1195            0 :     if (qs < 0)
    1196              :     {
    1197            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1198            0 :       pc->qs = qs;
    1199            0 :       return GNUNET_SYSERR;
    1200              :     }
    1201              :   }
    1202              :   else
    1203              :   {
    1204            0 :     if ( (internal_checks) &&
    1205            0 :          (! ps->purse_deleted) )
    1206              :     {
    1207              :       enum GNUNET_DB_QueryStatus qs;
    1208              : 
    1209            0 :       qs = report_row_inconsistency (
    1210              :         "purse-delete",
    1211              :         deletion_serial_id,
    1212              :         "purse not marked as deleted (internal check)");
    1213            0 :       if (qs < 0)
    1214              :       {
    1215            0 :         GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1216            0 :         pc->qs = qs;
    1217            0 :         return GNUNET_SYSERR;
    1218              :       }
    1219              :     }
    1220              :   }
    1221            0 :   return GNUNET_OK;
    1222              : }
    1223              : 
    1224              : 
    1225              : /**
    1226              :  * Function called on expired purses.
    1227              :  *
    1228              :  * @param pc closure
    1229              :  * @param purse_pub public key of the purse
    1230              :  * @param balance amount of money in the purse
    1231              :  * @param expiration_date when did the purse expire?
    1232              :  * @return #GNUNET_OK to continue to iterate
    1233              :  */
    1234              : static enum GNUNET_GenericReturnValue
    1235            0 : handle_purse_expired (
    1236              :   struct PurseContext *pc,
    1237              :   const struct TALER_PurseContractPublicKeyP *purse_pub,
    1238              :   const struct TALER_Amount *balance,
    1239              :   struct GNUNET_TIME_Timestamp expiration_date)
    1240              : {
    1241              :   enum GNUNET_DB_QueryStatus qs;
    1242            0 :   struct TALER_AUDITORDB_PurseNotClosedInconsistencies pnci = {
    1243              :     .amount = *balance,
    1244              :     .expiration_date = expiration_date.abs_time,
    1245              :     .purse_pub = purse_pub->eddsa_pub
    1246              :   };
    1247              : 
    1248            0 :   qs = TALER_AUDITORDB_insert_purse_not_closed_inconsistencies (
    1249              :     TALER_ARL_adb,
    1250              :     &pnci);
    1251            0 :   if (qs < 0)
    1252              :   {
    1253            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1254            0 :     pc->qs = qs;
    1255            0 :     return GNUNET_SYSERR;
    1256              :   }
    1257            0 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1258              :               "Handling purse expiration `%s'\n",
    1259              :               TALER_B2S (purse_pub));
    1260            0 :   TALER_ARL_amount_add (&TALER_ARL_USE_AB (purse_total_delayed_decisions),
    1261              :                         &TALER_ARL_USE_AB (purse_total_delayed_decisions),
    1262              :                         balance);
    1263            0 :   return GNUNET_OK;
    1264              : }
    1265              : 
    1266              : 
    1267              : /**
    1268              :  * Check that the purse summary matches what the exchange database
    1269              :  * thinks about the purse, and update our own state of the purse.
    1270              :  *
    1271              :  * Remove all purses that we are happy with from the DB.
    1272              :  *
    1273              :  * @param cls our `struct PurseContext`
    1274              :  * @param key hash of the purse public key
    1275              :  * @param value a `struct PurseSummary`
    1276              :  * @return #GNUNET_OK to process more entries
    1277              :  */
    1278              : static enum GNUNET_GenericReturnValue
    1279            0 : verify_purse_balance (void *cls,
    1280              :                       const struct GNUNET_HashCode *key,
    1281              :                       void *value)
    1282              : {
    1283            0 :   struct PurseContext *pc = cls;
    1284            0 :   struct PurseSummary *ps = value;
    1285              :   enum GNUNET_DB_QueryStatus qs;
    1286              : 
    1287            0 :   if (internal_checks)
    1288              :   {
    1289              :     struct TALER_Amount pf;
    1290              :     struct TALER_Amount balance_without_purse_fee;
    1291              : 
    1292              :     /* subtract purse fee from ps->balance to get actual balance we expect, as
    1293              :        we track the balance including purse fee, while the exchange subtracts
    1294              :        the purse fee early on. */
    1295            0 :     qs = get_purse_fee (ps->creation_date,
    1296              :                         &pf);
    1297            0 :     if (qs < 0)
    1298              :     {
    1299            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1300            0 :       pc->qs = qs;
    1301            0 :       return GNUNET_SYSERR;
    1302              :     }
    1303            0 :     if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    1304            0 :       return GNUNET_OK; /* error already reported */
    1305            0 :     if (0 >
    1306            0 :         TALER_amount_subtract (&balance_without_purse_fee,
    1307            0 :                                &ps->balance,
    1308              :                                &pf))
    1309              :     {
    1310            0 :       qs = report_row_inconsistency ("purse",
    1311              :                                      0,
    1312              :                                      "purse fee higher than balance");
    1313            0 :       if (qs < 0)
    1314              :       {
    1315            0 :         GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1316            0 :         pc->qs = qs;
    1317            0 :         return GNUNET_SYSERR;
    1318              :       }
    1319            0 :       GNUNET_assert (GNUNET_OK ==
    1320              :                      TALER_amount_set_zero (TALER_ARL_currency,
    1321              :                                             &balance_without_purse_fee));
    1322              :     }
    1323              : 
    1324            0 :     if (0 != TALER_amount_cmp (&ps->exchange_balance,
    1325              :                                &balance_without_purse_fee))
    1326              :     {
    1327            0 :       qs = report_amount_arithmetic_inconsistency ("purse-balance",
    1328              :                                                    0,
    1329            0 :                                                    &ps->exchange_balance,
    1330              :                                                    &balance_without_purse_fee,
    1331              :                                                    0);
    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              :     }
    1339              :   }
    1340              : 
    1341            0 :   if (ps->had_pi)
    1342              :   {
    1343            0 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1344              :                 "Updating purse `%s'\n",
    1345              :                 TALER_B2S (&ps->purse_pub));
    1346            0 :     qs = TALER_AUDITORDB_update_purse_info (TALER_ARL_adb,
    1347            0 :                                             &ps->purse_pub,
    1348            0 :                                             &ps->balance);
    1349              :   }
    1350              :   else
    1351              :   {
    1352            0 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1353              :                 "Inserting purse `%s'\n",
    1354              :                 TALER_B2S (&ps->purse_pub));
    1355            0 :     qs = TALER_AUDITORDB_insert_purse_info (TALER_ARL_adb,
    1356            0 :                                             &ps->purse_pub,
    1357            0 :                                             &ps->balance,
    1358            0 :                                             &ps->total_value,
    1359              :                                             ps->expiration_date);
    1360            0 :     ps->had_pi = true;
    1361              :   }
    1362            0 :   if (qs < 0)
    1363              :   {
    1364            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1365            0 :     pc->qs = qs;
    1366            0 :     return GNUNET_SYSERR;
    1367              :   }
    1368            0 :   GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs);
    1369            0 :   return GNUNET_OK;
    1370              : }
    1371              : 
    1372              : 
    1373              : /**
    1374              :  * Clear memory from the purses hash map.
    1375              :  *
    1376              :  * @param cls our `struct PurseContext`
    1377              :  * @param key hash of the purse public key
    1378              :  * @param value a `struct PurseSummary`
    1379              :  * @return #GNUNET_OK to process more entries
    1380              :  */
    1381              : static enum GNUNET_GenericReturnValue
    1382            0 : release_purse_balance (void *cls,
    1383              :                        const struct GNUNET_HashCode *key,
    1384              :                        void *value)
    1385              : {
    1386            0 :   struct PurseContext *pc = cls;
    1387            0 :   struct PurseSummary *ps = value;
    1388              : 
    1389            0 :   GNUNET_assert (GNUNET_YES ==
    1390              :                  GNUNET_CONTAINER_multihashmap_remove (pc->purses,
    1391              :                                                        key,
    1392              :                                                        ps));
    1393            0 :   GNUNET_free (ps);
    1394            0 :   return GNUNET_OK;
    1395              : }
    1396              : 
    1397              : 
    1398              : /**
    1399              :  * Analyze purses for being well-formed.
    1400              :  *
    1401              :  * @param cls NULL
    1402              :  * @return transaction status code
    1403              :  */
    1404              : static enum GNUNET_DB_QueryStatus
    1405            7 : analyze_purses (void *cls)
    1406              : {
    1407              :   struct PurseContext pc;
    1408              :   enum GNUNET_DB_QueryStatus qs;
    1409              : 
    1410              :   (void) cls;
    1411            7 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1412              :               "Analyzing purses\n");
    1413            7 :   qs = TALER_AUDITORDB_get_auditor_progress (
    1414              :     TALER_ARL_adb,
    1415              :     TALER_ARL_GET_PP (purse_account_merge_serial_id),
    1416              :     TALER_ARL_GET_PP (purse_decision_serial_id),
    1417              :     TALER_ARL_GET_PP (purse_deletion_serial_id),
    1418              :     TALER_ARL_GET_PP (purse_deposits_serial_id),
    1419              :     TALER_ARL_GET_PP (purse_merges_serial_id),
    1420              :     TALER_ARL_GET_PP (purse_request_serial_id),
    1421              :     TALER_ARL_GET_PP (purse_open_counter),
    1422              :     NULL);
    1423            7 :   if (0 > qs)
    1424              :   {
    1425            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1426            0 :     return qs;
    1427              :   }
    1428            7 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1429              :               "Resuming purse audit at %llu/%llu/%llu/%llu/%llu/%llu/%llu\n",
    1430              :               (unsigned long long) TALER_ARL_USE_PP (
    1431              :                 purse_open_counter),
    1432              :               (unsigned long long) TALER_ARL_USE_PP (
    1433              :                 purse_request_serial_id),
    1434              :               (unsigned long long) TALER_ARL_USE_PP (
    1435              :                 purse_decision_serial_id),
    1436              :               (unsigned long long) TALER_ARL_USE_PP (
    1437              :                 purse_deletion_serial_id),
    1438              :               (unsigned long long) TALER_ARL_USE_PP (
    1439              :                 purse_merges_serial_id),
    1440              :               (unsigned long long) TALER_ARL_USE_PP (
    1441              :                 purse_deposits_serial_id),
    1442              :               (unsigned long long) TALER_ARL_USE_PP (
    1443              :                 purse_account_merge_serial_id));
    1444            7 :   pc.qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
    1445            7 :   qs = TALER_AUDITORDB_get_balance (
    1446              :     TALER_ARL_adb,
    1447              :     TALER_ARL_GET_AB (purse_global_balance),
    1448              :     TALER_ARL_GET_AB (purse_total_balance_insufficient_loss),
    1449              :     TALER_ARL_GET_AB (purse_total_delayed_decisions),
    1450              :     TALER_ARL_GET_AB (purse_total_balance_purse_not_closed),
    1451              :     TALER_ARL_GET_AB (purse_total_arithmetic_delta_plus),
    1452              :     TALER_ARL_GET_AB (purse_total_arithmetic_delta_minus),
    1453              :     TALER_ARL_GET_AB (purse_total_bad_sig_loss),
    1454              :     NULL);
    1455            7 :   if (qs < 0)
    1456              :   {
    1457            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1458            0 :     return qs;
    1459              :   }
    1460            7 :   pc.purses = GNUNET_CONTAINER_multihashmap_create (512,
    1461              :                                                     GNUNET_NO);
    1462              : 
    1463            7 :   qs = TALER_EXCHANGEDB_iterate_purse_requests_above_serial_id (
    1464              :     TALER_ARL_edb,
    1465              :     TALER_ARL_USE_PP (purse_request_serial_id),
    1466              :     &handle_purse_requested,
    1467              :     &pc);
    1468            7 :   if (qs < 0)
    1469              :   {
    1470            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1471            0 :     return qs;
    1472              :   }
    1473            7 :   if (pc.qs < 0)
    1474              :   {
    1475            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == pc.qs);
    1476            0 :     return pc.qs;
    1477              :   }
    1478            7 :   qs = TALER_EXCHANGEDB_iterate_purse_merges_above_serial_id (
    1479              :     TALER_ARL_edb,
    1480              :     TALER_ARL_USE_PP (purse_merges_serial_id),
    1481              :     &handle_purse_merged,
    1482              :     &pc);
    1483            7 :   if (qs < 0)
    1484              :   {
    1485            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1486            0 :     return qs;
    1487              :   }
    1488            7 :   if (pc.qs < 0)
    1489              :   {
    1490            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == pc.qs);
    1491            0 :     return pc.qs;
    1492              :   }
    1493              : 
    1494            7 :   qs = TALER_EXCHANGEDB_iterate_purse_deposits_above_serial_id (
    1495              :     TALER_ARL_edb,
    1496              :     TALER_ARL_USE_PP (purse_deposits_serial_id),
    1497              :     &handle_purse_deposits,
    1498              :     &pc);
    1499            7 :   if (qs < 0)
    1500              :   {
    1501            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1502            0 :     return qs;
    1503              :   }
    1504            7 :   if (pc.qs < 0)
    1505              :   {
    1506            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == pc.qs);
    1507            0 :     return pc.qs;
    1508              :   }
    1509              : 
    1510              :   /* Charge purse fee! */
    1511            7 :   qs = TALER_EXCHANGEDB_iterate_account_merges_above_serial_id (
    1512              :     TALER_ARL_edb,
    1513              :     TALER_ARL_USE_PP (purse_account_merge_serial_id),
    1514              :     &handle_account_merged,
    1515              :     &pc);
    1516            7 :   if (qs < 0)
    1517              :   {
    1518            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1519            0 :     return qs;
    1520              :   }
    1521            7 :   if (pc.qs < 0)
    1522              :   {
    1523            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == pc.qs);
    1524            0 :     return pc.qs;
    1525              :   }
    1526              : 
    1527            7 :   qs = TALER_EXCHANGEDB_iterate_all_purse_decisions_above_serial_id (
    1528              :     TALER_ARL_edb,
    1529              :     TALER_ARL_USE_PP (purse_decision_serial_id),
    1530              :     &handle_purse_decision,
    1531              :     &pc);
    1532            7 :   if (qs < 0)
    1533              :   {
    1534            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1535            0 :     return qs;
    1536              :   }
    1537            7 :   if (pc.qs < 0)
    1538              :   {
    1539            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == pc.qs);
    1540            0 :     return pc.qs;
    1541              :   }
    1542              : 
    1543            7 :   qs = TALER_EXCHANGEDB_iterate_all_purse_deletions_above_serial_id (
    1544              :     TALER_ARL_edb,
    1545              :     TALER_ARL_USE_PP (purse_deletion_serial_id),
    1546              :     &handle_purse_deletion,
    1547              :     &pc);
    1548            7 :   if (qs < 0)
    1549              :   {
    1550            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1551            0 :     return qs;
    1552              :   }
    1553            7 :   if (pc.qs < 0)
    1554              :   {
    1555            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == pc.qs);
    1556            0 :     return pc.qs;
    1557              :   }
    1558              : 
    1559            7 :   qs = TALER_AUDITORDB_iterate_purse_expired (
    1560              :     TALER_ARL_adb,
    1561              :     &handle_purse_expired,
    1562              :     &pc);
    1563            7 :   if (qs < 0)
    1564              :   {
    1565            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1566            0 :     return qs;
    1567              :   }
    1568            7 :   if (pc.qs < 0)
    1569              :   {
    1570            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == pc.qs);
    1571            0 :     return pc.qs;
    1572              :   }
    1573              : 
    1574            7 :   GNUNET_CONTAINER_multihashmap_iterate (pc.purses,
    1575              :                                          &verify_purse_balance,
    1576              :                                          &pc);
    1577            7 :   GNUNET_CONTAINER_multihashmap_iterate (pc.purses,
    1578              :                                          &release_purse_balance,
    1579              :                                          &pc);
    1580            7 :   GNUNET_break (0 ==
    1581              :                 GNUNET_CONTAINER_multihashmap_size (pc.purses));
    1582            7 :   GNUNET_CONTAINER_multihashmap_destroy (pc.purses);
    1583            7 :   if (pc.qs < 0)
    1584              :   {
    1585            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == pc.qs);
    1586            0 :     return pc.qs;
    1587              :   }
    1588            7 :   qs = TALER_AUDITORDB_insert_balance (
    1589              :     TALER_ARL_adb,
    1590              :     TALER_ARL_SET_AB (purse_global_balance),
    1591              :     TALER_ARL_SET_AB (purse_total_balance_insufficient_loss),
    1592              :     TALER_ARL_SET_AB (purse_total_delayed_decisions),
    1593              :     TALER_ARL_SET_AB (purse_total_balance_purse_not_closed),
    1594              :     TALER_ARL_SET_AB (purse_total_arithmetic_delta_plus),
    1595              :     TALER_ARL_SET_AB (purse_total_arithmetic_delta_minus),
    1596              :     TALER_ARL_SET_AB (purse_total_bad_sig_loss),
    1597              :     NULL);
    1598            7 :   if (0 > qs)
    1599              :   {
    1600            3 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1601              :                 "Failed to update auditor DB, not recording progress\n");
    1602            3 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1603            3 :     return qs;
    1604              :   }
    1605            4 :   qs = TALER_AUDITORDB_insert_auditor_progress (
    1606              :     TALER_ARL_adb,
    1607              :     TALER_ARL_SET_PP (purse_account_merge_serial_id),
    1608              :     TALER_ARL_SET_PP (purse_decision_serial_id),
    1609              :     TALER_ARL_SET_PP (purse_deletion_serial_id),
    1610              :     TALER_ARL_SET_PP (purse_deposits_serial_id),
    1611              :     TALER_ARL_SET_PP (purse_merges_serial_id),
    1612              :     TALER_ARL_SET_PP (purse_request_serial_id),
    1613              :     TALER_ARL_SET_PP (purse_open_counter),
    1614              :     NULL);
    1615            4 :   if (0 > qs)
    1616              :   {
    1617            0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1618              :                 "Failed to update auditor DB, not recording progress\n");
    1619            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1620            0 :     return qs;
    1621              :   }
    1622            4 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1623              :               "Concluded purse audit step at %llu/%llu/%llu/%llu/%llu/%llu\n",
    1624              :               (unsigned long long) TALER_ARL_USE_PP (
    1625              :                 purse_request_serial_id),
    1626              :               (unsigned long long) TALER_ARL_USE_PP (
    1627              :                 purse_decision_serial_id),
    1628              :               (unsigned long long) TALER_ARL_USE_PP (
    1629              :                 purse_deletion_serial_id),
    1630              :               (unsigned long long) TALER_ARL_USE_PP (
    1631              :                 purse_merges_serial_id),
    1632              :               (unsigned long long) TALER_ARL_USE_PP (
    1633              :                 purse_deposits_serial_id),
    1634              :               (unsigned long long) TALER_ARL_USE_PP (
    1635              :                 purse_account_merge_serial_id));
    1636            4 :   return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
    1637              : }
    1638              : 
    1639              : 
    1640              : /**
    1641              :  * Function called on events received from Postgres.
    1642              :  *
    1643              :  * @param cls closure, NULL
    1644              :  * @param extra additional event data provided
    1645              :  * @param extra_size number of bytes in @a extra
    1646              :  */
    1647              : static void
    1648            0 : db_notify (void *cls,
    1649              :            const void *extra,
    1650              :            size_t extra_size)
    1651              : {
    1652              :   (void) cls;
    1653              :   (void) extra;
    1654              :   (void) extra_size;
    1655              : 
    1656            0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1657              :               "Received notification to wake purses\n");
    1658            0 :   if (GNUNET_OK !=
    1659            0 :       TALER_ARL_setup_sessions_and_run (&analyze_purses,
    1660              :                                         NULL))
    1661              :   {
    1662            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1663              :                 "Audit failed\n");
    1664            0 :     GNUNET_SCHEDULER_shutdown ();
    1665            0 :     global_ret = EXIT_FAILURE;
    1666            0 :     return;
    1667              :   }
    1668              : }
    1669              : 
    1670              : 
    1671              : /**
    1672              :  * Function called on shutdown.
    1673              :  */
    1674              : static void
    1675            4 : do_shutdown (void *cls)
    1676              : {
    1677              :   (void) cls;
    1678              : 
    1679            4 :   if (NULL != eh)
    1680              :   {
    1681            4 :     TALER_AUDITORDB_event_listen_cancel (eh);
    1682            4 :     eh = NULL;
    1683              :   }
    1684            4 :   TALER_ARL_done ();
    1685            4 : }
    1686              : 
    1687              : 
    1688              : /**
    1689              :  * Main function that will be run.
    1690              :  *
    1691              :  * @param cls closure
    1692              :  * @param args remaining command-line arguments
    1693              :  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
    1694              :  * @param c configuration
    1695              :  */
    1696              : static void
    1697            4 : run (void *cls,
    1698              :      char *const *args,
    1699              :      const char *cfgfile,
    1700              :      const struct GNUNET_CONFIGURATION_Handle *c)
    1701              : {
    1702              :   (void) cls;
    1703              :   (void) args;
    1704              :   (void) cfgfile;
    1705              : 
    1706            4 :   cfg = c;
    1707            4 :   GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
    1708              :                                  NULL);
    1709            4 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1710              :               "Launching purses auditor\n");
    1711            4 :   if (EXIT_SUCCESS !=
    1712            4 :       (global_ret = TALER_ARL_init (c)))
    1713              :   {
    1714            0 :     return;
    1715              :   }
    1716            4 :   if (test_mode != 1)
    1717              :   {
    1718            4 :     struct GNUNET_DB_EventHeaderP es = {
    1719            4 :       .size = htons (sizeof (es)),
    1720            4 :       .type = htons (TALER_DBEVENT_EXCHANGE_AUDITOR_WAKE_HELPER_PURSES)
    1721              :     };
    1722              : 
    1723            4 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1724              :                 "Running helper indefinitely\n");
    1725            4 :     eh = TALER_AUDITORDB_event_listen (TALER_ARL_adb,
    1726              :                                        &es,
    1727            4 :                                        GNUNET_TIME_UNIT_FOREVER_REL,
    1728              :                                        &db_notify,
    1729              :                                        NULL);
    1730              :   }
    1731            4 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1732              :               "Starting audit\n");
    1733            4 :   if (GNUNET_OK !=
    1734            4 :       TALER_ARL_setup_sessions_and_run (&analyze_purses,
    1735              :                                         NULL))
    1736              :   {
    1737            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1738              :                 "Audit failed\n");
    1739            0 :     GNUNET_SCHEDULER_shutdown ();
    1740            0 :     global_ret = EXIT_FAILURE;
    1741            0 :     return;
    1742              :   }
    1743              : }
    1744              : 
    1745              : 
    1746              : /**
    1747              :  * The main function to check the database's handling of purses.
    1748              :  *
    1749              :  * @param argc number of arguments from the command line
    1750              :  * @param argv command line arguments
    1751              :  * @return 0 ok, 1 on error
    1752              :  */
    1753              : int
    1754            4 : main (int argc,
    1755              :       char *const *argv)
    1756              : {
    1757            4 :   const struct GNUNET_GETOPT_CommandLineOption options[] = {
    1758            4 :     GNUNET_GETOPT_option_flag ('i',
    1759              :                                "internal",
    1760              :                                "perform checks only applicable for exchange-internal audits",
    1761              :                                &internal_checks),
    1762            4 :     GNUNET_GETOPT_option_flag ('t',
    1763              :                                "test",
    1764              :                                "run in test mode and exit when idle",
    1765              :                                &test_mode),
    1766            4 :     GNUNET_GETOPT_option_timetravel ('T',
    1767              :                                      "timetravel"),
    1768              :     GNUNET_GETOPT_OPTION_END
    1769              :   };
    1770              :   enum GNUNET_GenericReturnValue ret;
    1771              : 
    1772            4 :   ret = GNUNET_PROGRAM_run (
    1773              :     TALER_AUDITOR_project_data (),
    1774              :     argc,
    1775              :     argv,
    1776              :     "taler-helper-auditor-purses",
    1777              :     gettext_noop ("Audit Taler exchange purse handling"),
    1778              :     options,
    1779              :     &run,
    1780              :     NULL);
    1781            4 :   if (GNUNET_SYSERR == ret)
    1782            0 :     return EXIT_NOTCONFIGURED;
    1783            4 :   if (GNUNET_NO == ret)
    1784            0 :     return EXIT_SUCCESS;
    1785            4 :   return global_ret;
    1786              : }
    1787              : 
    1788              : 
    1789              : /* end of taler-helper-auditor-purses.c */
        

Generated by: LCOV version 2.0-1