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

Generated by: LCOV version 2.0-1