LCOV - code coverage report
Current view: top level - auditor - taler-helper-auditor-purses.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 91 499 18.2 %
Date: 2025-06-05 21:03:14 Functions: 4 19 21.1 %

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

Generated by: LCOV version 1.16