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

Generated by: LCOV version 2.0-1