LCOV - code coverage report
Current view: top level - auditor - taler-helper-auditor-coins.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 10.6 % 821 87
Test Date: 2026-09-09 15:11:34 Functions: 17.2 % 29 5

            Line data    Source code
       1              : /*
       2              :   This file is part of TALER
       3              :   Copyright (C) 2016-2025 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-coins.c
      18              :  * @brief audits coins in an exchange database.
      19              :  * @author Christian Grothoff
      20              :  */
      21              : #include "platform.h"
      22              : #include "auditordb_lib.h"
      23              : #include "report-lib.h"
      24              : #include "taler/taler_dbevents.h"
      25              : #include "exchangedb_lib.h"
      26              : #include "auditor-database/delete_denomination_balance.h"
      27              : #include "auditor-database/event_listen.h"
      28              : #include "auditor-database/get_auditor_progress.h"
      29              : #include "auditor-database/get_balance.h"
      30              : #include "auditor-database/get_denomination_balance.h"
      31              : #include "auditor-database/insert_amount_arithmetic_inconsistency.h"
      32              : #include "auditor-database/insert_auditor_progress.h"
      33              : #include "auditor-database/insert_bad_sig_losses.h"
      34              : #include "auditor-database/insert_balance.h"
      35              : #include "auditor-database/insert_denomination_balance.h"
      36              : #include "auditor-database/insert_denominations_without_sigs.h"
      37              : #include "auditor-database/insert_emergency.h"
      38              : #include "auditor-database/insert_emergency_by_count.h"
      39              : #include "auditor-database/insert_historic_denom_revenue.h"
      40              : #include "auditor-database/insert_row_inconsistency.h"
      41              : #include "auditor-database/update_denomination_balance.h"
      42              : #include "exchange-database/get_count_known_coins.h"
      43              : #include "exchange-database/get_coin_transactions.h"
      44              : #include "exchange-database/get_denomination_revocation.h"
      45              : #include "exchange-database/get_known_coin.h"
      46              : struct CoinContext;
      47              : #define TALER_EXCHANGEDB_DENOMINATION_RESULT_CLOSURE enum GNUNET_DB_QueryStatus
      48              : #define TALER_EXCHANGEDB_DEPOSIT_RESULT_CLOSURE struct CoinContext
      49              : #define TALER_EXCHANGEDB_PURSE_DECISION_RESULT_CLOSURE struct CoinContext
      50              : #define TALER_EXCHANGEDB_PURSE_DEPOSIT_RESULT_CLOSURE struct CoinContext
      51              : #define TALER_EXCHANGEDB_PURSE_REFUND_COIN_RESULT_CLOSURE struct CoinContext
      52              : #define TALER_EXCHANGEDB_RECOUP_RESULT_CLOSURE struct CoinContext
      53              : #define TALER_EXCHANGEDB_RECOUP_REFRESH_RESULT_CLOSURE struct CoinContext
      54              : #define TALER_EXCHANGEDB_REFRESHES_RESULT_CLOSURE struct CoinContext
      55              : #define TALER_EXCHANGEDB_REFUND_RESULT_CLOSURE struct CoinContext
      56              : #define TALER_EXCHANGEDB_WITHDRAW_RESULT_CLOSURE struct CoinContext
      57              : #include "exchange-database/iterate_denomination_info.h"
      58              : #include "exchange-database/get_auditor_denom_sig.h"
      59              : #include "exchange-database/iterate_coin_deposits_above_serial_id.h"
      60              : #include "exchange-database/iterate_purse_decisions_above_serial_id.h"
      61              : #include "exchange-database/iterate_purse_deposits_above_serial_id.h"
      62              : #include "exchange-database/iterate_purse_deposits_by_purse.h"
      63              : #include "exchange-database/iterate_recoups_above_serial_id.h"
      64              : #include "exchange-database/iterate_recoup_refreshes_above_serial_id.h"
      65              : #include "exchange-database/iterate_refreshes_above_serial_id.h"
      66              : #include "exchange-database/iterate_refunds_above_serial_id.h"
      67              : #include "exchange-database/iterate_withdrawals_above_serial_id.h"
      68              : 
      69              : 
      70              : /**
      71              :  * How many coin histories do we keep in RAM at any given point in time?
      72              :  * Expect a few kB per coin history to be used. Used bound memory consumption
      73              :  * of the auditor. Larger values reduce database accesses.
      74              :  */
      75              : #define MAX_COIN_HISTORIES (16 * 1024 * 1024)
      76              : 
      77              : /**
      78              :  * Use a 1 day grace period to deal with clocks not being perfectly synchronized.
      79              :  */
      80              : #define DEPOSIT_GRACE_PERIOD GNUNET_TIME_UNIT_DAYS
      81              : 
      82              : /**
      83              :  * Return value from main().
      84              :  */
      85              : static int global_ret;
      86              : 
      87              : /**
      88              :  * Run in test mode. Exit when idle instead of
      89              :  * going to sleep and waiting for more work.
      90              :  */
      91              : static int test_mode;
      92              : 
      93              : /**
      94              :  * Checkpointing our progress for coins.
      95              :  */
      96              : static TALER_ARL_DEF_PP (coins_withdraw_serial_id);
      97              : static TALER_ARL_DEF_PP (coins_deposit_serial_id);
      98              : static TALER_ARL_DEF_PP (coins_melt_serial_id);
      99              : static TALER_ARL_DEF_PP (coins_refund_serial_id);
     100              : static TALER_ARL_DEF_PP (coins_recoup_serial_id);
     101              : static TALER_ARL_DEF_PP (coins_recoup_refresh_serial_id);
     102              : static TALER_ARL_DEF_PP (coins_purse_deposits_serial_id);
     103              : static TALER_ARL_DEF_PP (coins_purse_refunds_serial_id);
     104              : 
     105              : 
     106              : /**
     107              :  * Global coin balance sheet (for coins).
     108              :  */
     109              : static TALER_ARL_DEF_AB (coin_balance_risk);
     110              : static TALER_ARL_DEF_AB (total_escrowed);
     111              : static TALER_ARL_DEF_AB (coin_irregular_loss);
     112              : static TALER_ARL_DEF_AB (coin_melt_fee_revenue);
     113              : static TALER_ARL_DEF_AB (coin_deposit_fee_revenue);
     114              : static TALER_ARL_DEF_AB (coin_deposit_fee_loss);
     115              : static TALER_ARL_DEF_AB (coin_refund_fee_revenue);
     116              : static TALER_ARL_DEF_AB (total_recoup_loss);
     117              : 
     118              : /**
     119              :  * Profits the exchange made by bad amount calculations.
     120              :  */
     121              : static TALER_ARL_DEF_AB (coins_total_arithmetic_delta_plus);
     122              : 
     123              : /**
     124              :  * Losses the exchange made by bad amount calculations.
     125              :  */
     126              : static TALER_ARL_DEF_AB (coins_total_arithmetic_delta_minus);
     127              : 
     128              : /**
     129              :  * Total amount reported in all calls to #report_emergency_by_count().
     130              :  */
     131              : static TALER_ARL_DEF_AB (coins_reported_emergency_risk_by_count);
     132              : 
     133              : /**
     134              :  * Total amount reported in all calls to #report_emergency_by_amount().
     135              :  */
     136              : static TALER_ARL_DEF_AB (coins_reported_emergency_risk_by_amount);
     137              : 
     138              : /**
     139              :  * Total amount in losses reported in all calls to #report_emergency_by_amount().
     140              :  */
     141              : static TALER_ARL_DEF_AB (coins_emergencies_loss);
     142              : 
     143              : /**
     144              :  * Total amount in losses reported in all calls to #report_emergency_by_count().
     145              :  */
     146              : static TALER_ARL_DEF_AB (coins_emergencies_loss_by_count);
     147              : 
     148              : 
     149              : /**
     150              :  * Coin and associated transaction history.
     151              :  */
     152              : struct CoinHistory
     153              : {
     154              :   /**
     155              :    * Public key of the coin.
     156              :    */
     157              :   struct TALER_CoinSpendPublicKeyP coin_pub;
     158              : 
     159              :   /**
     160              :    * The transaction list for the @a coin_pub.
     161              :    */
     162              :   struct TALER_EXCHANGEDB_TransactionList *tl;
     163              : };
     164              : 
     165              : /**
     166              :  * Array of transaction histories for coins.  The index is based on the coin's
     167              :  * public key.  Entries are replaced whenever we have a collision.
     168              :  */
     169              : static struct CoinHistory coin_histories[MAX_COIN_HISTORIES];
     170              : 
     171              : /**
     172              :  * Should we run checks that only work for exchange-internal audits?
     173              :  */
     174              : static int internal_checks;
     175              : 
     176              : static struct GNUNET_DB_EventHandler *eh;
     177              : 
     178              : /**
     179              :  * The auditors's configuration.
     180              :  */
     181              : static const struct GNUNET_CONFIGURATION_Handle *cfg;
     182              : 
     183              : 
     184              : /**
     185              :  * Return the index we should use for @a coin_pub in #coin_histories.
     186              :  *
     187              :  * @param coin_pub a coin's public key
     188              :  * @return index for caching this coin's history in #coin_histories
     189              :  */
     190              : static unsigned int
     191            0 : coin_history_index (const struct TALER_CoinSpendPublicKeyP *coin_pub)
     192              : {
     193              :   uint32_t i;
     194              : 
     195            0 :   GNUNET_memcpy (&i,
     196              :                  coin_pub,
     197              :                  sizeof (i));
     198            0 :   return i % MAX_COIN_HISTORIES;
     199              : }
     200              : 
     201              : 
     202              : /**
     203              :  * Add a coin history to our in-memory cache.
     204              :  *
     205              :  * @param coin_pub public key of the coin to cache
     206              :  * @param tl history to store
     207              :  */
     208              : static void
     209            0 : cache_history (const struct TALER_CoinSpendPublicKeyP *coin_pub,
     210              :                struct TALER_EXCHANGEDB_TransactionList *tl)
     211              : {
     212            0 :   unsigned int i = coin_history_index (coin_pub);
     213              : 
     214            0 :   if (NULL != coin_histories[i].tl)
     215            0 :     TALER_EXCHANGEDB_free_coin_transaction_list (coin_histories[i].tl);
     216            0 :   coin_histories[i].coin_pub = *coin_pub;
     217            0 :   coin_histories[i].tl = tl;
     218            0 : }
     219              : 
     220              : 
     221              : /**
     222              :  * Obtain a coin's history from our in-memory cache.
     223              :  *
     224              :  * @param coin_pub public key of the coin to cache
     225              :  * @return NULL if @a coin_pub is not in the cache
     226              :  */
     227              : static struct TALER_EXCHANGEDB_TransactionList *
     228            0 : get_cached_history (const struct TALER_CoinSpendPublicKeyP *coin_pub)
     229              : {
     230            0 :   unsigned int i = coin_history_index (coin_pub);
     231              : 
     232            0 :   if (0 ==
     233            0 :       GNUNET_memcmp (coin_pub,
     234              :                      &coin_histories[i].coin_pub))
     235              :   {
     236            0 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     237              :                 "Found verification of %s in cache\n",
     238              :                 TALER_B2S (coin_pub));
     239            0 :     return coin_histories[i].tl;
     240              :   }
     241            0 :   return NULL;
     242              : }
     243              : 
     244              : 
     245              : /* ***************************** Report logic **************************** */
     246              : 
     247              : /**
     248              :  * Called in case we detect an emergency situation where the exchange
     249              :  * is paying out a larger amount on a denomination than we issued in
     250              :  * that denomination.  This means that the exchange's private keys
     251              :  * might have gotten compromised, and that we need to trigger an
     252              :  * emergency request to all wallets to deposit pending coins for the
     253              :  * denomination (and as an exchange suffer a huge financial loss).
     254              :  *
     255              :  * @param issue denomination key where the loss was detected
     256              :  * @param risk maximum risk that might have just become real (coins created by this @a issue)
     257              :  * @param loss actual losses already (actualized before denomination was revoked)
     258              :  * @return transaction status
     259              :  */
     260              : static enum GNUNET_DB_QueryStatus
     261            0 : report_emergency_by_amount (
     262              :   const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue,
     263              :   const struct TALER_Amount *risk,
     264              :   const struct TALER_Amount *loss)
     265              : {
     266              :   enum GNUNET_DB_QueryStatus qs;
     267            0 :   struct TALER_AUDITORDB_Emergency emergency = {
     268              :     .denom_loss = *loss,
     269              :     .denompub_h = *&issue->denom_hash,
     270              :     .denom_risk = *risk,
     271              :     .deposit_start = *&issue->start.abs_time,
     272              :     .deposit_end = *&issue->expire_deposit.abs_time,
     273              :     .value = *&issue->value
     274              :   };
     275              : 
     276            0 :   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     277              :               "Reporting emergency on denomination `%s' over loss of %s\n",
     278              :               GNUNET_h2s (&issue->denom_hash.hash),
     279              :               TALER_amount2s (loss));
     280              : 
     281            0 :   qs = TALER_AUDITORDB_insert_emergency (
     282              :     TALER_ARL_adb,
     283              :     &emergency);
     284            0 :   if (qs < 0)
     285              :   {
     286            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     287            0 :     return qs;
     288              :   }
     289            0 :   TALER_ARL_amount_add (&TALER_ARL_USE_AB (
     290              :                           coins_reported_emergency_risk_by_amount),
     291              :                         &TALER_ARL_USE_AB (
     292              :                           coins_reported_emergency_risk_by_amount),
     293              :                         risk);
     294            0 :   TALER_ARL_amount_add (&TALER_ARL_USE_AB (coins_emergencies_loss),
     295              :                         &TALER_ARL_USE_AB (coins_emergencies_loss),
     296              :                         loss);
     297            0 :   return qs;
     298              : }
     299              : 
     300              : 
     301              : /**
     302              :  * Called in case we detect an emergency situation where the exchange
     303              :  * is paying out a larger NUMBER of coins of a denomination than we
     304              :  * issued in that denomination.  This means that the exchange's
     305              :  * private keys might have gotten compromised, and that we need to
     306              :  * trigger an emergency request to all wallets to deposit pending
     307              :  * coins for the denomination (and as an exchange suffer a huge
     308              :  * financial loss).
     309              :  *
     310              :  * @param issue denomination key where the loss was detected
     311              :  * @param num_issued number of coins that were issued
     312              :  * @param num_known number of coins that have been deposited
     313              :  * @param risk amount that is at risk
     314              :  * @return transaction status
     315              :  */
     316              : static enum GNUNET_DB_QueryStatus
     317            0 : report_emergency_by_count (
     318              :   const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue,
     319              :   uint64_t num_issued,
     320              :   uint64_t num_known,
     321              :   const struct TALER_Amount *risk)
     322              : {
     323              :   enum GNUNET_DB_QueryStatus qs;
     324            0 :   struct TALER_AUDITORDB_EmergenciesByCount emergenciesByCount = {
     325              :     .denompub_h = issue->denom_hash,
     326              :     .num_issued = num_issued,
     327              :     .num_known = num_known,
     328              :     .start = issue->start.abs_time,
     329              :     .deposit_end = issue->expire_deposit.abs_time,
     330              :     .value = issue->value
     331              :   };
     332              : 
     333            0 :   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     334              :               "Reporting emergency on denomination `%s' with issued %lu vs known %lu over risk of %s\n",
     335              :               GNUNET_h2s (&issue->denom_hash.hash),
     336              :               num_issued,
     337              :               num_known,
     338              :               TALER_amount2s (risk));
     339              : 
     340            0 :   qs = TALER_AUDITORDB_insert_emergency_by_count (
     341              :     TALER_ARL_adb,
     342              :     &emergenciesByCount);
     343              : 
     344            0 :   if (qs < 0)
     345              :   {
     346            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     347            0 :     return qs;
     348              :   }
     349            0 :   TALER_ARL_amount_add (&TALER_ARL_USE_AB (
     350              :                           coins_reported_emergency_risk_by_count),
     351              :                         &TALER_ARL_USE_AB (
     352              :                           coins_reported_emergency_risk_by_count),
     353              :                         risk);
     354            0 :   for (uint64_t i = num_issued; i < num_known; i++)
     355            0 :     TALER_ARL_amount_add (&TALER_ARL_USE_AB (coins_emergencies_loss_by_count),
     356              :                           &TALER_ARL_USE_AB (coins_emergencies_loss_by_count),
     357              :                           &issue->value);
     358            0 :   return qs;
     359              : }
     360              : 
     361              : 
     362              : /**
     363              :  * Report a (serious) inconsistency in the exchange's database with
     364              :  * respect to calculations involving amounts.
     365              :  *
     366              :  * @param operation what operation had the inconsistency
     367              :  * @param rowid affected row, 0 if row is missing
     368              :  * @param exchange amount calculated by exchange
     369              :  * @param auditor amount calculated by auditor
     370              :  * @param profitable 1 if @a exchange being larger than @a auditor is
     371              :  *           profitable for the exchange for this operation
     372              :  *           (and thus @a exchange being smaller than @ auditor
     373              :  *            representing a loss for the exchange);
     374              :  *           -1 if @a exchange being smaller than @a auditor is
     375              :  *           profitable for the exchange; and 0 if it is unclear
     376              :  * @return transaction status
     377              :  */
     378              : static enum GNUNET_DB_QueryStatus
     379            0 : report_amount_arithmetic_inconsistency (
     380              :   const char *operation,
     381              :   uint64_t rowid,
     382              :   const struct TALER_Amount *exchange,
     383              :   const struct TALER_Amount *auditor,
     384              :   int profitable)
     385              : {
     386              :   struct TALER_Amount delta;
     387              :   struct TALER_Amount *target;
     388              : 
     389            0 :   if (0 < TALER_amount_cmp (exchange,
     390              :                             auditor))
     391              :   {
     392              :     /* exchange > auditor */
     393            0 :     TALER_ARL_amount_subtract (&delta,
     394              :                                exchange,
     395              :                                auditor);
     396              :   }
     397              :   else
     398              :   {
     399              :     /* exchange <= auditor */
     400            0 :     profitable = -profitable;
     401            0 :     TALER_ARL_amount_subtract (&delta,
     402              :                                auditor,
     403              :                                exchange);
     404              :   }
     405              : 
     406              :   {
     407            0 :     struct TALER_AUDITORDB_AmountArithmeticInconsistency aai = {
     408            0 :       .profitable = profitable,
     409              :       .problem_row_id = rowid,
     410              :       .operation = (char *) operation,
     411              :       .exchange_amount = *exchange,
     412              :       .auditor_amount = *auditor
     413              :     };
     414              :     enum GNUNET_DB_QueryStatus qs;
     415              : 
     416            0 :     qs = TALER_AUDITORDB_insert_amount_arithmetic_inconsistency (
     417              :       TALER_ARL_adb,
     418              :       &aai);
     419            0 :     if (qs < 0)
     420              :     {
     421            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     422            0 :       return qs;
     423              :     }
     424              :   }
     425            0 :   if (0 != profitable)
     426              :   {
     427            0 :     target = (1 == profitable)
     428              :       ? &TALER_ARL_USE_AB (coins_total_arithmetic_delta_plus)
     429            0 :       : &TALER_ARL_USE_AB (coins_total_arithmetic_delta_minus);
     430            0 :     TALER_ARL_amount_add (target,
     431              :                           target,
     432              :                           &delta);
     433              :   }
     434            0 :   return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
     435              : }
     436              : 
     437              : 
     438              : /**
     439              :  * Report a (serious) inconsistency in the exchange's database.
     440              :  *
     441              :  * @param table affected table
     442              :  * @param rowid affected row, 0 if row is missing
     443              :  * @param diagnostic message explaining the problem
     444              :  * @return transaction status
     445              :  */
     446              : static enum GNUNET_DB_QueryStatus
     447            0 : report_row_inconsistency (const char *table,
     448              :                           uint64_t rowid,
     449              :                           const char *diagnostic)
     450              : {
     451              : 
     452              :   enum GNUNET_DB_QueryStatus qs;
     453            0 :   struct TALER_AUDITORDB_RowInconsistency ri = {
     454              :     .row_table = (char *) table,
     455              :     .row_id = rowid,
     456              :     .diagnostic = (char *) diagnostic
     457              :   };
     458              : 
     459            0 :   qs = TALER_AUDITORDB_insert_row_inconsistency (
     460              :     TALER_ARL_adb,
     461              :     &ri);
     462            0 :   if (qs < 0)
     463              :   {
     464            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     465            0 :     return qs;
     466              :   }
     467            0 :   return qs;
     468              : }
     469              : 
     470              : 
     471              : /* ************* Analyze history of a coin ******************** */
     472              : 
     473              : 
     474              : /**
     475              :  * Obtain @a coin_pub's history, verify it, report inconsistencies
     476              :  * and store the result in our cache.
     477              :  *
     478              :  * @param coin_pub public key of the coin to check the history of
     479              :  * @param rowid a row identifying the transaction
     480              :  * @param operation operation matching @a rowid
     481              :  * @param value value of the respective coin's denomination
     482              :  * @return database status code, negative on failures
     483              :  */
     484              : static enum GNUNET_DB_QueryStatus
     485            0 : check_coin_history (const struct TALER_CoinSpendPublicKeyP *coin_pub,
     486              :                     uint64_t rowid,
     487              :                     const char *operation,
     488              :                     const struct TALER_Amount *value)
     489              : {
     490              :   struct TALER_EXCHANGEDB_TransactionList *tl;
     491            0 :   enum GNUNET_DB_QueryStatus qs = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
     492              :   struct TALER_Amount total;
     493              :   struct TALER_Amount spent;
     494              :   struct TALER_Amount refunded;
     495              :   struct TALER_Amount deposit_fee;
     496              :   bool have_refund;
     497              :   uint64_t etag_out;
     498              : 
     499              :   /* FIXME-Optimization: could use 'etag' mechanism to only fetch transactions
     500              :      we did not yet process, instead of going over them
     501              :      again and again. */
     502              :   {
     503              :     struct TALER_Amount balance;
     504              :     struct TALER_DenominationHashP h_denom_pub;
     505              : 
     506            0 :     qs = TALER_EXCHANGEDB_get_coin_transactions (TALER_ARL_edb,
     507              :                                                  false,
     508              :                                                  coin_pub,
     509              :                                                  0,
     510              :                                                  0,
     511              :                                                  &etag_out,
     512              :                                                  &balance,
     513              :                                                  &h_denom_pub,
     514              :                                                  &tl);
     515              :   }
     516            0 :   if (0 > qs)
     517            0 :     return qs;
     518            0 :   GNUNET_assert (GNUNET_OK ==
     519              :                  TALER_amount_set_zero (value->currency,
     520              :                                         &refunded));
     521            0 :   GNUNET_assert (GNUNET_OK ==
     522              :                  TALER_amount_set_zero (value->currency,
     523              :                                         &spent));
     524            0 :   GNUNET_assert (GNUNET_OK ==
     525              :                  TALER_amount_set_zero (value->currency,
     526              :                                         &deposit_fee));
     527            0 :   have_refund = false;
     528            0 :   for (struct TALER_EXCHANGEDB_TransactionList *pos = tl;
     529            0 :        NULL != pos;
     530            0 :        pos = pos->next)
     531              :   {
     532            0 :     switch (pos->type)
     533              :     {
     534            0 :     case TALER_EXCHANGEDB_TT_DEPOSIT:
     535              :       /* spent += pos->amount_with_fee */
     536            0 :       TALER_ARL_amount_add (&spent,
     537              :                             &spent,
     538              :                             &pos->details.deposit->amount_with_fee);
     539            0 :       deposit_fee = pos->details.deposit->deposit_fee;
     540            0 :       break;
     541            0 :     case TALER_EXCHANGEDB_TT_MELT:
     542              :       /* spent += pos->amount_with_fee */
     543            0 :       TALER_ARL_amount_add (&spent,
     544              :                             &spent,
     545              :                             &pos->details.melt->amount_with_fee);
     546            0 :       break;
     547            0 :     case TALER_EXCHANGEDB_TT_REFUND:
     548              :       /* refunded += pos->refund_amount - pos->refund_fee */
     549            0 :       TALER_ARL_amount_add (&refunded,
     550              :                             &refunded,
     551              :                             &pos->details.refund->refund_amount);
     552            0 :       TALER_ARL_amount_add (&spent,
     553              :                             &spent,
     554              :                             &pos->details.refund->refund_fee);
     555            0 :       have_refund = true;
     556            0 :       break;
     557            0 :     case TALER_EXCHANGEDB_TT_RECOUP_REFRESH_RECEIVER:
     558              :       /* refunded += pos->value */
     559            0 :       TALER_ARL_amount_add (&refunded,
     560              :                             &refunded,
     561              :                             &pos->details.old_coin_recoup->value);
     562            0 :       break;
     563            0 :     case TALER_EXCHANGEDB_TT_RECOUP_WITHDRAW:
     564              :       /* spent += pos->value */
     565            0 :       TALER_ARL_amount_add (&spent,
     566              :                             &spent,
     567              :                             &pos->details.recoup->value);
     568            0 :       break;
     569            0 :     case TALER_EXCHANGEDB_TT_RECOUP_REFRESH:
     570              :       /* spent += pos->value */
     571            0 :       TALER_ARL_amount_add (&spent,
     572              :                             &spent,
     573              :                             &pos->details.recoup_refresh->value);
     574            0 :       break;
     575            0 :     case TALER_EXCHANGEDB_TT_PURSE_DEPOSIT:
     576              :       /* spent += pos->value */
     577            0 :       TALER_ARL_amount_add (&spent,
     578              :                             &spent,
     579              :                             &pos->details.purse_deposit->amount);
     580            0 :       break;
     581            0 :     case TALER_EXCHANGEDB_TT_PURSE_REFUND:
     582            0 :       TALER_ARL_amount_add (&refunded,
     583              :                             &refunded,
     584              :                             &pos->details.purse_refund->refund_amount);
     585            0 :       TALER_ARL_amount_add (&spent,
     586              :                             &spent,
     587              :                             &pos->details.purse_refund->refund_fee);
     588            0 :       have_refund = true;
     589            0 :       break;
     590            0 :     case TALER_EXCHANGEDB_TT_RESERVE_OPEN:
     591            0 :       TALER_ARL_amount_add (&spent,
     592              :                             &spent,
     593              :                             &pos->details.reserve_open->coin_contribution);
     594            0 :       break;
     595              :     } /* switch (pos->type) */
     596              :   } /* for (...) */
     597            0 :   if (have_refund)
     598              :   {
     599              :     /* If we gave any refund, also discount ONE deposit fee */
     600            0 :     TALER_ARL_amount_add (&refunded,
     601              :                           &refunded,
     602              :                           &deposit_fee);
     603              :   }
     604              :   /* total coin value = original value plus refunds */
     605            0 :   TALER_ARL_amount_add (&total,
     606              :                         &refunded,
     607              :                         value);
     608            0 :   if (1 ==
     609            0 :       TALER_amount_cmp (&spent,
     610              :                         &total))
     611              :   {
     612              :     /* spent > total: bad */
     613              :     struct TALER_Amount loss;
     614              : 
     615            0 :     TALER_ARL_amount_subtract (&loss,
     616              :                                &spent,
     617              :                                &total);
     618            0 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     619              :                 "Loss detected for coin %s - %s\n",
     620              :                 TALER_B2S (coin_pub),
     621              :                 TALER_amount2s (&loss));
     622            0 :     qs = report_amount_arithmetic_inconsistency (operation,
     623              :                                                  rowid,
     624              :                                                  &spent,
     625              :                                                  &total,
     626              :                                                  -1);
     627            0 :     if (qs < 0)
     628              :     {
     629            0 :       TALER_EXCHANGEDB_free_coin_transaction_list (tl);
     630            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     631            0 :       return qs;
     632              :     }
     633              :   }
     634            0 :   cache_history (coin_pub,
     635              :                  tl);
     636            0 :   return qs;
     637              : }
     638              : 
     639              : 
     640              : /* ************************* Analyze coins ******************** */
     641              : /* This logic checks that the exchange did the right thing for each
     642              :    coin, checking deposits, refunds, refresh* and known_coins
     643              :    tables */
     644              : 
     645              : 
     646              : /**
     647              :  * Summary data we keep per denomination.
     648              :  */
     649              : struct DenominationSummary
     650              : {
     651              :   /**
     652              :    * Information about the circulation.
     653              :    */
     654              :   struct TALER_AUDITORDB_DenominationCirculationData dcd;
     655              : 
     656              :   /**
     657              :    * Denomination key information for this denomination.
     658              :    */
     659              :   const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue;
     660              : 
     661              :   /**
     662              :    * True if this record already existed in the DB.
     663              :    * Used to decide between insert/update in
     664              :    * #sync_denomination().
     665              :    */
     666              :   bool in_db;
     667              : 
     668              :   /**
     669              :    * Should we report an emergency for this denomination, causing it to be
     670              :    * revoked (because more coins were deposited than issued)?
     671              :    */
     672              :   bool report_emergency;
     673              : 
     674              :   /**
     675              :    * True if this denomination was revoked.
     676              :    */
     677              :   bool was_revoked;
     678              : };
     679              : 
     680              : 
     681              : /**
     682              :  * Closure for callbacks during #analyze_coins().
     683              :  */
     684              : struct CoinContext
     685              : {
     686              : 
     687              :   /**
     688              :    * Map for tracking information about denominations.
     689              :    */
     690              :   struct GNUNET_CONTAINER_MultiHashMap *denom_summaries;
     691              : 
     692              :   /**
     693              :    * Transaction status code.
     694              :    */
     695              :   enum GNUNET_DB_QueryStatus qs;
     696              : 
     697              : };
     698              : 
     699              : 
     700              : /**
     701              :  * Initialize information about denomination from the database.
     702              :  *
     703              :  * @param denom_hash hash of the public key of the denomination
     704              :  * @param[out] ds summary to initialize
     705              :  * @return transaction status code
     706              :  */
     707              : static enum GNUNET_DB_QueryStatus
     708            0 : init_denomination (const struct TALER_DenominationHashP *denom_hash,
     709              :                    struct DenominationSummary *ds)
     710              : {
     711              :   enum GNUNET_DB_QueryStatus qs;
     712              :   struct TALER_MasterSignatureP msig;
     713              :   uint64_t rowid;
     714              : 
     715            0 :   qs = TALER_AUDITORDB_get_denomination_balance (TALER_ARL_adb,
     716              :                                                  denom_hash,
     717              :                                                  &ds->dcd);
     718            0 :   if (0 > qs)
     719              :   {
     720            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     721            0 :     return qs;
     722              :   }
     723            0 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
     724              :   {
     725            0 :     ds->in_db = true;
     726              :   }
     727              :   else
     728              :   {
     729            0 :     GNUNET_assert (GNUNET_OK ==
     730              :                    TALER_amount_set_zero (TALER_ARL_currency,
     731              :                                           &ds->dcd.denom_balance));
     732            0 :     GNUNET_assert (GNUNET_OK ==
     733              :                    TALER_amount_set_zero (TALER_ARL_currency,
     734              :                                           &ds->dcd.denom_loss));
     735            0 :     GNUNET_assert (GNUNET_OK ==
     736              :                    TALER_amount_set_zero (TALER_ARL_currency,
     737              :                                           &ds->dcd.denom_risk));
     738            0 :     GNUNET_assert (GNUNET_OK ==
     739              :                    TALER_amount_set_zero (TALER_ARL_currency,
     740              :                                           &ds->dcd.recoup_loss));
     741              :   }
     742            0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     743              :               "Starting balance for denomination `%s' is %s (%llu)\n",
     744              :               GNUNET_h2s (&denom_hash->hash),
     745              :               TALER_amount2s (&ds->dcd.denom_balance),
     746              :               (unsigned long long) ds->dcd.num_issued);
     747            0 :   qs = TALER_EXCHANGEDB_get_denomination_revocation (TALER_ARL_edb,
     748              :                                                      denom_hash,
     749              :                                                      &msig,
     750              :                                                      &rowid);
     751            0 :   if (0 > qs)
     752              :   {
     753            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     754            0 :     return qs;
     755              :   }
     756            0 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
     757              :   {
     758              :     /* check revocation signature */
     759            0 :     if (GNUNET_OK !=
     760            0 :         TALER_exchange_offline_denomination_revoke_verify (
     761              :           denom_hash,
     762              :           &TALER_ARL_master_pub,
     763              :           &msig))
     764              :     {
     765            0 :       qs = report_row_inconsistency ("denomination revocations",
     766              :                                      rowid,
     767              :                                      "revocation signature invalid");
     768            0 :       if (qs < 0)
     769              :       {
     770            0 :         GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     771            0 :         return qs;
     772              :       }
     773              :     }
     774              :     else
     775              :     {
     776            0 :       ds->was_revoked = true;
     777              :     }
     778              :   }
     779            0 :   return ds->in_db
     780              :     ? GNUNET_DB_STATUS_SUCCESS_ONE_RESULT
     781            0 :     : GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
     782              : }
     783              : 
     784              : 
     785              : /**
     786              :  * Obtain the denomination summary for the given @a dh
     787              :  *
     788              :  * @param cc our execution context
     789              :  * @param issue denomination key information for @a dh
     790              :  * @return NULL on error
     791              :  */
     792              : static struct DenominationSummary *
     793            0 : get_denomination_summary (
     794              :   struct CoinContext *cc,
     795              :   const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue)
     796              : {
     797              :   struct DenominationSummary *ds;
     798            0 :   const struct TALER_DenominationHashP *dh = &issue->denom_hash;
     799              : 
     800            0 :   ds = GNUNET_CONTAINER_multihashmap_get (cc->denom_summaries,
     801              :                                           &dh->hash);
     802            0 :   if (NULL != ds)
     803            0 :     return ds;
     804            0 :   ds = GNUNET_new (struct DenominationSummary);
     805            0 :   ds->issue = issue;
     806            0 :   if (0 > (cc->qs = init_denomination (dh,
     807              :                                        ds)))
     808              :   {
     809            0 :     GNUNET_break (0);
     810            0 :     GNUNET_free (ds);
     811            0 :     return NULL;
     812              :   }
     813            0 :   GNUNET_assert (GNUNET_OK ==
     814              :                  GNUNET_CONTAINER_multihashmap_put (cc->denom_summaries,
     815              :                                                     &dh->hash,
     816              :                                                     ds,
     817              :                                                     GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)
     818              :                  );
     819            0 :   return ds;
     820              : }
     821              : 
     822              : 
     823              : /**
     824              :  * Write information about the current knowledge about a denomination key
     825              :  * back to the database and update our global reporting data about the
     826              :  * denomination.
     827              :  *
     828              :  * @param cls the `struct CoinContext`
     829              :  * @param denom_hash the hash of the denomination key
     830              :  * @param value a `struct DenominationSummary`
     831              :  * @return #GNUNET_OK (continue to iterate)
     832              :  *         #GNUNET_SYSERR (stop to iterate)
     833              :  */
     834              : static enum GNUNET_GenericReturnValue
     835            0 : sync_denomination (void *cls,
     836              :                    const struct GNUNET_HashCode *denom_hash,
     837              :                    void *value)
     838              : {
     839            0 :   struct CoinContext *cc = cls;
     840            0 :   struct TALER_DenominationHashP denom_h = {
     841              :     .hash = *denom_hash
     842              :   };
     843            0 :   struct DenominationSummary *ds = value;
     844            0 :   const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue = ds->issue;
     845              :   struct GNUNET_TIME_Absolute now;
     846              :   struct GNUNET_TIME_Timestamp expire_deposit;
     847              :   struct GNUNET_TIME_Absolute expire_deposit_grace;
     848              :   enum GNUNET_DB_QueryStatus qs;
     849              : 
     850            0 :   now = GNUNET_TIME_absolute_get ();
     851            0 :   expire_deposit = issue->expire_deposit;
     852              :   /* add day grace period to deal with clocks not being perfectly synchronized */
     853            0 :   expire_deposit_grace = GNUNET_TIME_absolute_add (expire_deposit.abs_time,
     854              :                                                    DEPOSIT_GRACE_PERIOD);
     855            0 :   if (GNUNET_TIME_absolute_cmp (now,
     856              :                                 >,
     857              :                                 expire_deposit_grace))
     858              :   {
     859              :     /* Denomination key has expired, book remaining balance of
     860              :        outstanding coins as revenue; and reduce cc->risk exposure. */
     861            0 :     if (ds->in_db)
     862            0 :       qs = TALER_AUDITORDB_delete_denomination_balance (TALER_ARL_adb,
     863              :                                                         &denom_h);
     864              :     else
     865            0 :       qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
     866            0 :     if (qs < 0)
     867              :     {
     868            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     869            0 :       cc->qs = qs;
     870            0 :       return GNUNET_SYSERR;
     871              :     }
     872            0 :     if ( (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) &&
     873            0 :          (! TALER_amount_is_zero (&ds->dcd.denom_risk)) )
     874              :     {
     875              :       /* The denomination expired and carried a balance; we can now
     876              :          book the remaining balance as profit, and reduce our risk
     877              :          exposure by the accumulated risk of the denomination. */
     878            0 :       TALER_ARL_amount_subtract (&TALER_ARL_USE_AB (coin_balance_risk),
     879              :                                  &TALER_ARL_USE_AB (coin_balance_risk),
     880              :                                  &ds->dcd.denom_risk);
     881              :       /* If the above fails, our risk assessment is inconsistent!
     882              :          This is really, really bad (auditor-internal invariant
     883              :          would be violated). Hence we can "safely" assert.  If
     884              :          this assertion fails, well, good luck: there is a bug
     885              :          in the auditor _or_ the auditor's database is corrupt. */
     886              :     }
     887            0 :     if ( (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) &&
     888            0 :          (! TALER_amount_is_zero (&ds->dcd.denom_balance)) )
     889              :     {
     890              :       /* book denom_balance coin expiration profits! */
     891            0 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     892              :                   "Denomination `%s' expired, booking %s in expiration profits\n",
     893              :                   GNUNET_h2s (denom_hash),
     894              :                   TALER_amount2s (&ds->dcd.denom_balance));
     895            0 :       qs = TALER_AUDITORDB_insert_historic_denom_revenue (
     896              :         TALER_ARL_adb,
     897              :         &denom_h,
     898              :         expire_deposit,
     899            0 :         &ds->dcd.denom_balance,
     900            0 :         &ds->dcd.recoup_loss);
     901            0 :       if (qs < 0)
     902              :       {
     903              :         /* Failed to store profits? Bad database */
     904            0 :         GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     905            0 :         cc->qs = qs;
     906            0 :         return GNUNET_SYSERR;
     907              :       }
     908              :     }
     909              :   }
     910              :   else
     911              :   {
     912              :     /* Not expired, just store current denomination summary
     913              :        to auditor database for next iteration */
     914              :     long long cnt;
     915              : 
     916            0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     917              :                 "Final balance for denomination `%s' is %s (%llu)\n",
     918              :                 GNUNET_h2s (denom_hash),
     919              :                 TALER_amount2s (&ds->dcd.denom_balance),
     920              :                 (unsigned long long) ds->dcd.num_issued);
     921            0 :     cnt = TALER_EXCHANGEDB_get_count_known_coins (TALER_ARL_edb,
     922              :                                                   &denom_h);
     923            0 :     if (0 > cnt)
     924              :     {
     925              :       /* Failed to obtain count? Bad database */
     926            0 :       qs = (enum GNUNET_DB_QueryStatus) cnt;
     927            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     928            0 :       cc->qs = qs;
     929            0 :       return GNUNET_SYSERR;
     930              :     }
     931            0 :     if (ds->dcd.num_issued < (uint64_t) cnt)
     932              :     {
     933              :       /* more coins deposited than issued! very bad */
     934            0 :       qs = report_emergency_by_count (issue,
     935              :                                       ds->dcd.num_issued,
     936              :                                       cnt,
     937            0 :                                       &ds->dcd.denom_risk);
     938            0 :       if (qs < 0)
     939              :       {
     940            0 :         GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     941            0 :         cc->qs = qs;
     942            0 :         return GNUNET_SYSERR;
     943              :       }
     944              :     }
     945            0 :     if (ds->report_emergency)
     946              :     {
     947              :       /* Value of coins deposited exceed value of coins
     948              :          issued! Also very bad! */
     949            0 :       qs = report_emergency_by_amount (issue,
     950            0 :                                        &ds->dcd.denom_risk,
     951            0 :                                        &ds->dcd.denom_loss);
     952            0 :       if (qs < 0)
     953              :       {
     954            0 :         GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     955            0 :         cc->qs = qs;
     956            0 :         return GNUNET_SYSERR;
     957              :       }
     958              :     }
     959            0 :     if (ds->in_db)
     960            0 :       qs = TALER_AUDITORDB_update_denomination_balance (TALER_ARL_adb,
     961              :                                                         &denom_h,
     962            0 :                                                         &ds->dcd);
     963              :     else
     964            0 :       qs = TALER_AUDITORDB_insert_denomination_balance (TALER_ARL_adb,
     965              :                                                         &denom_h,
     966            0 :                                                         &ds->dcd);
     967              : 
     968            0 :     if (qs < 0)
     969              :     {
     970            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     971            0 :       cc->qs = qs;
     972            0 :       return GNUNET_SYSERR;
     973              :     }
     974              :   }
     975            0 :   return GNUNET_OK;
     976              : }
     977              : 
     978              : 
     979              : /**
     980              :  * Remove and free the memory of @a value from the
     981              :  * denomination summaries.
     982              :  *
     983              :  * @param cls the `struct CoinContext`
     984              :  * @param denom_hash the hash of the denomination key
     985              :  * @param value a `struct DenominationSummary`
     986              :  * @return #GNUNET_OK (continue to iterate)
     987              :  */
     988              : static enum GNUNET_GenericReturnValue
     989            0 : cleanup_denomination (void *cls,
     990              :                       const struct GNUNET_HashCode *denom_hash,
     991              :                       void *value)
     992              : {
     993            0 :   struct CoinContext *cc = cls;
     994            0 :   struct DenominationSummary *ds = value;
     995              : 
     996            0 :   GNUNET_assert (GNUNET_YES ==
     997              :                  GNUNET_CONTAINER_multihashmap_remove (cc->denom_summaries,
     998              :                                                        denom_hash,
     999              :                                                        ds));
    1000            0 :   GNUNET_free (ds);
    1001            0 :   return GNUNET_OK;
    1002              : }
    1003              : 
    1004              : 
    1005              : /**
    1006              :  * Function called with details about all withdraw operations.
    1007              :  * Updates the denomination balance and the overall balance as
    1008              :  * we now have additional coins that have been issued.
    1009              :  *
    1010              :  * Note that the signature was already checked in
    1011              :  * taler-helper-auditor-reserves.c::#handle_withdrawals(), so we do not check
    1012              :  * it again here.
    1013              :  *
    1014              :  * @param cc our `struct CoinContext`
    1015              :  * @param rowid unique serial ID for the refresh session in our DB
    1016              :  * @param num_denom_serials number of elements in @e denom_serials array
    1017              :  * @param denom_serials array with length @e num_denom_serials of serial ID's of denominations in our DB
    1018              :  * @param selected_h hash over the gamma-selected planchets
    1019              :  * @param h_planchets running hash over all hashes of blinded planchets in the original withdraw request
    1020              :  * @param blinding_seed the blinding seed for CS denominations that was provided during withdraw; might be NULL
    1021              :  * @param age_proof_required true if the withdraw request required an age proof.
    1022              :  * @param max_age if @e age_proof_required is true, the maximum age that was set on the coins.
    1023              :  * @param noreveal_index if @e age_proof_required is true, the index that was returned by the exchange for the reveal phase.
    1024              :  * @param reserve_pub public key of the reserve
    1025              :  * @param reserve_sig signature over the withdraw operation
    1026              :  * @param execution_date when did the wallet withdraw the coin
    1027              :  * @param amount_with_fee amount that was withdrawn
    1028              :  * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
    1029              :  */
    1030              : static enum GNUNET_GenericReturnValue
    1031            0 : withdraw_cb (
    1032              :   struct CoinContext *cc,
    1033              :   uint64_t rowid,
    1034              :   size_t num_denom_serials,
    1035              :   const uint64_t *denom_serials,
    1036              :   const struct TALER_HashBlindedPlanchetsP *selected_h,
    1037              :   const struct TALER_HashBlindedPlanchetsP *h_planchets,
    1038              :   const struct TALER_BlindingMasterSeedP *blinding_seed,
    1039              :   bool age_proof_required,
    1040              :   uint8_t max_age,
    1041              :   uint8_t noreveal_index,
    1042              :   const struct TALER_ReservePublicKeyP *reserve_pub,
    1043              :   const struct TALER_ReserveSignatureP *reserve_sig,
    1044              :   struct GNUNET_TIME_Timestamp execution_date,
    1045              :   const struct TALER_Amount *amount_with_fee)
    1046              : {
    1047              : 
    1048              :   /* Note: some optimization potential here: lots of fields we
    1049              :      could avoid fetching from the database with a custom function. */
    1050              :   (void) h_planchets;
    1051              :   (void) blinding_seed;
    1052              :   (void) reserve_pub;
    1053              :   (void) reserve_sig;
    1054              :   (void) execution_date;
    1055              :   (void) amount_with_fee;
    1056              : 
    1057            0 :   GNUNET_assert (rowid >=
    1058              :                  TALER_ARL_USE_PP (coins_withdraw_serial_id)); /* should be monotonically increasing */
    1059            0 :   TALER_ARL_USE_PP (coins_withdraw_serial_id) = rowid + 1;
    1060              : 
    1061            0 :   for (size_t i=0; i < num_denom_serials; i++)
    1062              :   {
    1063              :     struct DenominationSummary *ds;
    1064              :     const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue;
    1065              :     enum GNUNET_DB_QueryStatus qs;
    1066              : 
    1067            0 :     qs = TALER_ARL_get_denomination_info_by_serial (denom_serials[i],
    1068              :                                                     &issue);
    1069            0 :     if (0 > qs)
    1070              :     {
    1071            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1072            0 :       cc->qs = qs;
    1073            0 :       return GNUNET_SYSERR;
    1074              :     }
    1075            0 :     if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    1076              :     {
    1077            0 :       qs = report_row_inconsistency ("withdraw",
    1078              :                                      rowid,
    1079              :                                      "denomination key not found");
    1080            0 :       if (0 > qs)
    1081              :       {
    1082            0 :         GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1083            0 :         cc->qs = qs;
    1084            0 :         return GNUNET_SYSERR;
    1085              :       }
    1086            0 :       return GNUNET_OK;
    1087              :     }
    1088            0 :     ds = get_denomination_summary (cc,
    1089              :                                    issue);
    1090            0 :     if (NULL == ds)
    1091              :     {
    1092              :       /* cc->qs is set by #get_denomination_summary() */
    1093            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == cc->qs);
    1094            0 :       return GNUNET_SYSERR;
    1095              :     }
    1096            0 :     ds->dcd.num_issued++;
    1097            0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1098              :                 "Issued coin in denomination `%s' of total value %s\n",
    1099              :                 GNUNET_h2s (&issue->denom_hash.hash),
    1100              :                 TALER_amount2s (&issue->value));
    1101            0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1102              :                 "New balance of denomination `%s' after withdraw is %s\n",
    1103              :                 GNUNET_h2s (&issue->denom_hash.hash),
    1104              :                 TALER_amount2s (&ds->dcd.denom_balance));
    1105            0 :     TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_escrowed),
    1106              :                           &TALER_ARL_USE_AB (total_escrowed),
    1107              :                           &issue->value);
    1108            0 :     TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_balance_risk),
    1109              :                           &TALER_ARL_USE_AB (coin_balance_risk),
    1110              :                           &issue->value);
    1111            0 :     TALER_ARL_amount_add (&ds->dcd.denom_balance,
    1112              :                           &ds->dcd.denom_balance,
    1113              :                           &issue->value);
    1114            0 :     TALER_ARL_amount_add (&ds->dcd.denom_risk,
    1115              :                           &ds->dcd.denom_risk,
    1116              :                           &issue->value);
    1117              :   }
    1118            0 :   return GNUNET_OK;
    1119              : }
    1120              : 
    1121              : 
    1122              : /**
    1123              :  * Check that the @a coin_pub is a known coin with a proper
    1124              :  * signature for denominatinon @a denom_pub. If not, report
    1125              :  * a loss of @a loss_potential.
    1126              :  *
    1127              :  * @param operation which operation is this about
    1128              :  * @param issue denomination key information about the coin
    1129              :  * @param rowid which row is this operation in
    1130              :  * @param coin_pub public key of a coin
    1131              :  * @param denom_pub expected denomination of the coin
    1132              :  * @param loss_potential how big could the loss be if the coin is
    1133              :  *        not properly signed
    1134              :  * @return database transaction status; on success
    1135              :  *  #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT, or
    1136              :  *  #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if the coin is not known to
    1137              :  *  the exchange at all (which was reported to the auditor database)
    1138              :  */
    1139              : static enum GNUNET_DB_QueryStatus
    1140            0 : check_known_coin (
    1141              :   const char *operation,
    1142              :   const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue,
    1143              :   uint64_t rowid,
    1144              :   const struct TALER_CoinSpendPublicKeyP *coin_pub,
    1145              :   const struct TALER_DenominationPublicKey *denom_pub,
    1146              :   const struct TALER_Amount *loss_potential)
    1147              : {
    1148              :   struct TALER_CoinPublicInfo ci;
    1149              :   enum GNUNET_DB_QueryStatus qs;
    1150              : 
    1151            0 :   if (NULL == get_cached_history (coin_pub))
    1152              :   {
    1153            0 :     qs = check_coin_history (coin_pub,
    1154              :                              rowid,
    1155              :                              operation,
    1156              :                              &issue->value);
    1157            0 :     if (0 > qs)
    1158              :     {
    1159            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1160            0 :       return qs;
    1161              :     }
    1162            0 :     GNUNET_break (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != qs);
    1163              :   }
    1164              : 
    1165            0 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1166              :               "Checking denomination signature on %s\n",
    1167              :               TALER_B2S (coin_pub));
    1168            0 :   qs = TALER_EXCHANGEDB_get_known_coin (TALER_ARL_edb,
    1169              :                                         coin_pub,
    1170              :                                         &ci);
    1171            0 :   if (0 > qs)
    1172              :   {
    1173            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1174            0 :     return qs;
    1175              :   }
    1176            0 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    1177              :   {
    1178              :     /* The exchange has no denomination signature on file for this
    1179              :        coin at all; that is the strongest evidence of a forged coin
    1180              :        and must be persisted, not merely logged. */
    1181            0 :     struct TALER_AUDITORDB_BadSigLosses bsl = {
    1182              :       .problem_row_id = rowid,
    1183              :       .operation = (char *) operation,
    1184              :       .loss = *loss_potential,
    1185              :       .operation_specific_pub = coin_pub->eddsa_pub
    1186              :     };
    1187              :     enum GNUNET_DB_QueryStatus rqs;
    1188              : 
    1189            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    1190              :                 "Coin %s used in %s in row %llu is not known to the exchange\n",
    1191              :                 TALER_B2S (coin_pub),
    1192              :                 operation,
    1193              :                 (unsigned long long) rowid);
    1194            0 :     rqs = report_row_inconsistency ("known_coins",
    1195              :                                     rowid,
    1196              :                                     "coin not known to exchange");
    1197            0 :     if (0 > rqs)
    1198              :     {
    1199            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == rqs);
    1200            0 :       return rqs;
    1201              :     }
    1202            0 :     rqs = TALER_AUDITORDB_insert_bad_sig_losses (
    1203              :       TALER_ARL_adb,
    1204              :       &bsl);
    1205            0 :     if (0 > rqs)
    1206              :     {
    1207            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == rqs);
    1208            0 :       return rqs;
    1209              :     }
    1210            0 :     TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_irregular_loss),
    1211              :                           &TALER_ARL_USE_AB (coin_irregular_loss),
    1212              :                           loss_potential);
    1213            0 :     return qs;
    1214              :   }
    1215            0 :   if (GNUNET_YES !=
    1216            0 :       TALER_test_coin_valid (&ci,
    1217              :                              denom_pub))
    1218              :   {
    1219            0 :     struct TALER_AUDITORDB_BadSigLosses bsl = {
    1220              :       .problem_row_id = rowid,
    1221              :       .operation = (char *) operation,
    1222              :       .loss = *loss_potential,
    1223              :       .operation_specific_pub = coin_pub->eddsa_pub
    1224              :     };
    1225              : 
    1226            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    1227              :                 "Failed to verify coin denomination signature in row %llu\n",
    1228              :                 (unsigned long long) rowid);
    1229            0 :     qs = TALER_AUDITORDB_insert_bad_sig_losses (
    1230              :       TALER_ARL_adb,
    1231              :       &bsl);
    1232            0 :     if (qs < 0)
    1233              :     {
    1234            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1235            0 :       return qs;
    1236              :     }
    1237            0 :     TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_irregular_loss),
    1238              :                           &TALER_ARL_USE_AB (coin_irregular_loss),
    1239              :                           loss_potential);
    1240              :   }
    1241            0 :   TALER_denom_sig_free (&ci.denom_sig);
    1242            0 :   return qs;
    1243              : }
    1244              : 
    1245              : 
    1246              : /**
    1247              :  * Update the denom balance in @a dso reducing it by
    1248              :  * @a amount_with_fee. If this is not possible, report
    1249              :  * an emergency.  Also updates the balance.
    1250              :  *
    1251              :  * @param dso denomination summary to update
    1252              :  * @param rowid responsible row (for logging)
    1253              :  * @param amount_with_fee amount to subtract
    1254              :  * @return transaction status
    1255              :  */
    1256              : static enum GNUNET_DB_QueryStatus
    1257            0 : reduce_denom_balance (struct DenominationSummary *dso,
    1258              :                       uint64_t rowid,
    1259              :                       const struct TALER_Amount *amount_with_fee)
    1260              : {
    1261              :   struct TALER_Amount tmp;
    1262              :   enum GNUNET_DB_QueryStatus qs;
    1263              : 
    1264            0 :   if (TALER_ARL_SR_INVALID_NEGATIVE ==
    1265            0 :       TALER_ARL_amount_subtract_neg (&tmp,
    1266              :                                      &dso->dcd.denom_balance,
    1267              :                                      amount_with_fee))
    1268              :   {
    1269            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    1270              :                 "Emergency: failed to reduce balance of denomination `%s' by %s\n",
    1271              :                 GNUNET_h2s (&dso->issue->denom_hash.hash),
    1272              :                 TALER_amount2s (amount_with_fee));
    1273            0 :     TALER_ARL_amount_add (&dso->dcd.denom_loss,
    1274              :                           &dso->dcd.denom_loss,
    1275              :                           amount_with_fee);
    1276            0 :     dso->report_emergency = true;
    1277              :   }
    1278              :   else
    1279              :   {
    1280            0 :     dso->dcd.denom_balance = tmp;
    1281              :   }
    1282            0 :   if (-1 == TALER_amount_cmp (&TALER_ARL_USE_AB (total_escrowed),
    1283              :                               amount_with_fee))
    1284              :   {
    1285            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    1286              :                 "Failed to total escrow by %s\n",
    1287              :                 TALER_amount2s (amount_with_fee));
    1288              :     /* This can theoretically happen if for example the exchange
    1289              :        never issued any coins (i.e. escrow balance is zero), but
    1290              :        accepted a forged coin (i.e. emergency situation after
    1291              :        private key compromise). In that case, we cannot even
    1292              :        subtract the profit we make from the fee from the escrow
    1293              :        balance. Tested as part of test-auditor.sh, case #18 */
    1294            0 :     qs = report_amount_arithmetic_inconsistency (
    1295              :       "subtracting amount from escrow balance",
    1296              :       rowid,
    1297              :       &TALER_ARL_USE_AB (total_escrowed),
    1298              :       amount_with_fee,
    1299              :       0);
    1300            0 :     if (0 > qs)
    1301              :     {
    1302            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1303            0 :       return qs;
    1304              :     }
    1305              :   }
    1306              :   else
    1307              :   {
    1308            0 :     TALER_ARL_amount_subtract (&TALER_ARL_USE_AB (total_escrowed),
    1309              :                                &TALER_ARL_USE_AB (total_escrowed),
    1310              :                                amount_with_fee);
    1311              :   }
    1312            0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1313              :               "New balance of denomination `%s' is %s\n",
    1314              :               GNUNET_h2s (&dso->issue->denom_hash.hash),
    1315              :               TALER_amount2s (&dso->dcd.denom_balance));
    1316            0 :   return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
    1317              : }
    1318              : 
    1319              : 
    1320              : /**
    1321              :  * Function called with details about coins that were melted, with the
    1322              :  * goal of auditing the refresh's execution.  Verifies the signature
    1323              :  * and updates our information about coins outstanding (the old coin's
    1324              :  * denomination has less, the fresh coins increased outstanding
    1325              :  * balances).
    1326              :  *
    1327              :  * @param cc closure
    1328              :  * @param rowid unique serial ID for the refresh session in our DB
    1329              :  * @param old_denom_pub denomination public key of @a coin_pub
    1330              :  * @param coin_pub public key of the coin
    1331              :  * @param coin_sig signature from the coin
    1332              :  * @param h_age_commitment hash of the age commitment for the coin
    1333              :  * @param amount_with_fee amount that was deposited including fee
    1334              :  * @param num_nds length of the @a new_denom_serials array
    1335              :  * @param new_denom_serials array of denomination serials of fresh coins
    1336              :  * @param rc what the refresh commitment
    1337              :  * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
    1338              :  */
    1339              : static enum GNUNET_GenericReturnValue
    1340            0 : refresh_session_cb (struct CoinContext *cc,
    1341              :                     uint64_t rowid,
    1342              :                     const struct TALER_DenominationPublicKey *old_denom_pub,
    1343              :                     const struct TALER_CoinSpendPublicKeyP *coin_pub,
    1344              :                     const struct TALER_CoinSpendSignatureP *coin_sig,
    1345              :                     const struct TALER_AgeCommitmentHashP *h_age_commitment,
    1346              :                     const struct TALER_Amount *amount_with_fee,
    1347              :                     size_t num_nds,
    1348              :                     uint64_t new_denom_serials[static num_nds],
    1349              :                     const struct TALER_RefreshCommitmentP *rc)
    1350            0 : {
    1351              :   const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue;
    1352              :   struct DenominationSummary *dso;
    1353              :   enum GNUNET_DB_QueryStatus qs;
    1354              :   struct TALER_DenominationHashP h_denom_pub;
    1355              : 
    1356            0 :   GNUNET_assert (rowid >=
    1357              :                  TALER_ARL_USE_PP (coins_melt_serial_id)); /* should be monotonically increasing */
    1358            0 :   TALER_ARL_USE_PP (coins_melt_serial_id) = rowid + 1;
    1359            0 :   qs = TALER_ARL_get_denomination_info (old_denom_pub,
    1360              :                                         &issue,
    1361              :                                         &h_denom_pub);
    1362            0 :   if (0 > qs)
    1363              :   {
    1364            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1365            0 :     cc->qs = qs;
    1366            0 :     return GNUNET_SYSERR;
    1367              :   }
    1368            0 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    1369              :   {
    1370            0 :     qs = report_row_inconsistency ("melt",
    1371              :                                    rowid,
    1372              :                                    "denomination key not found");
    1373            0 :     if (0 > qs)
    1374              :     {
    1375            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1376            0 :       cc->qs = qs;
    1377            0 :       return GNUNET_SYSERR;
    1378              :     }
    1379            0 :     return GNUNET_OK;
    1380              :   }
    1381            0 :   qs = check_known_coin ("melt",
    1382              :                          issue,
    1383              :                          rowid,
    1384              :                          coin_pub,
    1385              :                          old_denom_pub,
    1386              :                          amount_with_fee);
    1387            0 :   if (0 > qs)
    1388              :   {
    1389            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1390            0 :     cc->qs = qs;
    1391            0 :     return GNUNET_SYSERR;
    1392              :   }
    1393              : 
    1394              :   /* verify melt signature */
    1395            0 :   if (GNUNET_OK !=
    1396            0 :       TALER_wallet_melt_verify (amount_with_fee,
    1397            0 :                                 &issue->fees.refresh,
    1398              :                                 rc,
    1399              :                                 &h_denom_pub,
    1400              :                                 h_age_commitment,
    1401              :                                 coin_pub,
    1402              :                                 coin_sig))
    1403              :   {
    1404            0 :     struct TALER_AUDITORDB_BadSigLosses bsl = {
    1405              :       .problem_row_id = rowid,
    1406              :       .operation = (char *) "melt",
    1407              :       .loss = *amount_with_fee,
    1408              :       .operation_specific_pub = coin_pub->eddsa_pub
    1409              :     };
    1410              : 
    1411            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    1412              :                 "Failed to verify coin melt signature in row %llu\n",
    1413              :                 (unsigned long long) rowid);
    1414            0 :     qs = TALER_AUDITORDB_insert_bad_sig_losses (
    1415              :       TALER_ARL_adb,
    1416              :       &bsl);
    1417            0 :     if (qs < 0)
    1418              :     {
    1419            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1420            0 :       cc->qs = qs;
    1421            0 :       return GNUNET_SYSERR;
    1422              :     }
    1423            0 :     TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_irregular_loss),
    1424              :                           &TALER_ARL_USE_AB (coin_irregular_loss),
    1425              :                           amount_with_fee);
    1426              :   }
    1427            0 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1428              :               "Melting coin %s in denomination `%s' of value %s\n",
    1429              :               TALER_B2S (coin_pub),
    1430              :               GNUNET_h2s (&issue->denom_hash.hash),
    1431              :               TALER_amount2s (amount_with_fee));
    1432              : 
    1433            0 :   {
    1434              :     struct TALER_Amount refresh_cost;
    1435              :     struct TALER_Amount amount_without_fee;
    1436            0 :     const struct TALER_EXCHANGEDB_DenominationKeyInformation *nis[num_nds];
    1437              : 
    1438              :     /* Check that the resulting amounts are consistent with the value being
    1439              :      refreshed by calculating the total refresh cost */
    1440            0 :     GNUNET_assert (GNUNET_OK ==
    1441              :                    TALER_amount_set_zero (amount_with_fee->currency,
    1442              :                                           &refresh_cost));
    1443            0 :     for (size_t i = 0; i < num_nds; i++)
    1444              :     {
    1445            0 :       qs = TALER_ARL_get_denomination_info_by_serial (new_denom_serials[i],
    1446              :                                                       &nis[i]);
    1447            0 :       if (0 > qs)
    1448              :       {
    1449            0 :         GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1450            0 :         cc->qs = qs;
    1451            0 :         return GNUNET_SYSERR;
    1452              :       }
    1453            0 :       if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    1454              :       {
    1455              :         /* nis[i] was not set; we cannot audit this refresh at all */
    1456            0 :         qs = report_row_inconsistency ("refresh_reveal",
    1457              :                                        rowid,
    1458              :                                        "denomination key for fresh coin not found");
    1459            0 :         if (0 > qs)
    1460              :         {
    1461            0 :           GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1462            0 :           cc->qs = qs;
    1463            0 :           return GNUNET_SYSERR;
    1464              :         }
    1465            0 :         return GNUNET_OK;
    1466              :       }
    1467              :       /* update cost of refresh */
    1468            0 :       TALER_ARL_amount_add (&refresh_cost,
    1469              :                             &refresh_cost,
    1470              :                             &nis[i]->fees.withdraw);
    1471            0 :       TALER_ARL_amount_add (&refresh_cost,
    1472              :                             &refresh_cost,
    1473              :                             &nis[i]->value);
    1474              :     }
    1475              : 
    1476              :     /* compute contribution of old coin */
    1477            0 :     if (TALER_ARL_SR_POSITIVE !=
    1478            0 :         TALER_ARL_amount_subtract_neg (&amount_without_fee,
    1479              :                                        amount_with_fee,
    1480              :                                        &issue->fees.refresh))
    1481              :     {
    1482              :       /* Melt fee higher than contribution of melted coin; this makes
    1483              :          no sense (exchange should never have accepted the operation) */
    1484            0 :       qs = report_amount_arithmetic_inconsistency ("melt contribution vs. fee",
    1485              :                                                    rowid,
    1486              :                                                    amount_with_fee,
    1487            0 :                                                    &issue->fees.refresh,
    1488              :                                                    -1);
    1489            0 :       if (0 > qs)
    1490              :       {
    1491            0 :         GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1492            0 :         cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
    1493            0 :         return GNUNET_SYSERR;
    1494              :       }
    1495              :       /* To continue, best assumption is the melted coin contributed
    1496              :          nothing (=> all withdrawal amounts will be counted as losses) */
    1497            0 :       GNUNET_assert (GNUNET_OK ==
    1498              :                      TALER_amount_set_zero (TALER_ARL_currency,
    1499              :                                             &amount_without_fee));
    1500              :     }
    1501              : 
    1502              :     /* check old coin covers complete expenses (of refresh operation) */
    1503            0 :     if (1 == TALER_amount_cmp (&refresh_cost,
    1504              :                                &amount_without_fee))
    1505              :     {
    1506              :       /* refresh_cost > amount_without_fee, which is bad (exchange lost) */
    1507            0 :       GNUNET_break_op (0);
    1508            0 :       qs = report_amount_arithmetic_inconsistency ("melt (cost)",
    1509              :                                                    rowid,
    1510              :                                                    &amount_without_fee, /* 'exchange' */
    1511              :                                                    &refresh_cost, /* 'auditor' */
    1512              :                                                    1);
    1513            0 :       if (0 > qs)
    1514              :       {
    1515            0 :         GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1516            0 :         cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
    1517            0 :         return GNUNET_SYSERR;
    1518              :       }
    1519              :     }
    1520              : 
    1521              :     /* update outstanding denomination amounts for fresh coins withdrawn */
    1522            0 :     for (size_t i = 0; i < num_nds; i++)
    1523              :     {
    1524            0 :       const struct TALER_EXCHANGEDB_DenominationKeyInformation *ni
    1525              :         = nis[i];
    1526              :       struct DenominationSummary *dsi;
    1527              : 
    1528            0 :       dsi = get_denomination_summary (cc,
    1529              :                                       ni);
    1530            0 :       if (NULL == dsi)
    1531              :       {
    1532            0 :         qs = report_row_inconsistency ("refresh_reveal",
    1533              :                                        rowid,
    1534              :                                        "denomination key for fresh coin unknown to auditor");
    1535            0 :         if (0 > qs)
    1536              :         {
    1537            0 :           GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1538            0 :           cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
    1539            0 :           return GNUNET_SYSERR;
    1540              :         }
    1541              :       }
    1542              :       else
    1543              :       {
    1544            0 :         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1545              :                     "Created fresh coin in denomination `%s' of value %s\n",
    1546              :                     GNUNET_h2s (&ni->denom_hash.hash),
    1547              :                     TALER_amount2s (&ni->value));
    1548            0 :         dsi->dcd.num_issued++;
    1549            0 :         TALER_ARL_amount_add (&dsi->dcd.denom_balance,
    1550              :                               &dsi->dcd.denom_balance,
    1551              :                               &ni->value);
    1552            0 :         TALER_ARL_amount_add (&dsi->dcd.denom_risk,
    1553              :                               &dsi->dcd.denom_risk,
    1554              :                               &ni->value);
    1555            0 :         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1556              :                     "New balance of denomination `%s' after refresh_reveal is %s\n",
    1557              :                     GNUNET_h2s (&ni->denom_hash.hash),
    1558              :                     TALER_amount2s (&dsi->dcd.denom_balance));
    1559            0 :         TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_escrowed),
    1560              :                               &TALER_ARL_USE_AB (total_escrowed),
    1561              :                               &ni->value);
    1562            0 :         TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_balance_risk),
    1563              :                               &TALER_ARL_USE_AB (coin_balance_risk),
    1564              :                               &ni->value);
    1565              :       }
    1566              :     }
    1567              :   }
    1568              : 
    1569              :   /* update old coin's denomination balance */
    1570            0 :   dso = get_denomination_summary (cc,
    1571              :                                   issue);
    1572            0 :   if (NULL == dso)
    1573              :   {
    1574            0 :     qs = report_row_inconsistency ("refresh_reveal",
    1575              :                                    rowid,
    1576              :                                    "denomination key for dirty coin unknown to auditor");
    1577            0 :     if (0 > qs)
    1578              :     {
    1579            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1580            0 :       cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
    1581            0 :       return GNUNET_SYSERR;
    1582              :     }
    1583              :   }
    1584              :   else
    1585              :   {
    1586            0 :     qs = reduce_denom_balance (dso,
    1587              :                                rowid,
    1588              :                                amount_with_fee);
    1589            0 :     if (0 > qs)
    1590              :     {
    1591            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1592            0 :       cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
    1593            0 :       return GNUNET_SYSERR;
    1594              :     }
    1595              :   }
    1596              : 
    1597              :   /* update global melt fees */
    1598            0 :   TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_melt_fee_revenue),
    1599              :                         &TALER_ARL_USE_AB (coin_melt_fee_revenue),
    1600              :                         &issue->fees.refresh);
    1601            0 :   return GNUNET_OK;
    1602              : }
    1603              : 
    1604              : 
    1605              : /**
    1606              :  * Function called with details about deposits that have been made,
    1607              :  * with the goal of auditing the deposit's execution.
    1608              :  *
    1609              :  * @param cc closure
    1610              :  * @param rowid unique serial ID for the deposit in our DB
    1611              :  * @param exchange_timestamp when did the exchange get the deposit
    1612              :  * @param deposit deposit details
    1613              :  * @param denom_pub denomination public key of @a coin_pub
    1614              :  * @param done flag set if the deposit was already executed (or not)
    1615              :  * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
    1616              :  */
    1617              : static enum GNUNET_GenericReturnValue
    1618            0 : deposit_cb (struct CoinContext *cc,
    1619              :             uint64_t rowid,
    1620              :             struct GNUNET_TIME_Timestamp exchange_timestamp,
    1621              :             const struct TALER_EXCHANGEDB_Deposit *deposit,
    1622              :             const struct TALER_DenominationPublicKey *denom_pub,
    1623              :             bool done)
    1624              : {
    1625              :   const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue;
    1626              :   struct DenominationSummary *ds;
    1627              :   enum GNUNET_DB_QueryStatus qs;
    1628              : 
    1629              :   (void) done;
    1630              :   (void) exchange_timestamp;
    1631            0 :   GNUNET_assert (rowid >=
    1632              :                  TALER_ARL_USE_PP (coins_deposit_serial_id)); /* should be monotonically increasing */
    1633            0 :   TALER_ARL_USE_PP (coins_deposit_serial_id) = rowid + 1;
    1634              : 
    1635            0 :   qs = TALER_ARL_get_denomination_info (denom_pub,
    1636              :                                         &issue,
    1637              :                                         NULL);
    1638            0 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    1639              :   {
    1640            0 :     qs = report_row_inconsistency ("deposits",
    1641              :                                    rowid,
    1642              :                                    "denomination key not found");
    1643            0 :     if (0 > qs)
    1644              :     {
    1645            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1646            0 :       cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
    1647            0 :       return GNUNET_SYSERR;
    1648              :     }
    1649            0 :     return GNUNET_OK;
    1650              :   }
    1651            0 :   if (GNUNET_TIME_timestamp_cmp (deposit->refund_deadline,
    1652              :                                  >,
    1653              :                                  deposit->wire_deadline))
    1654              :   {
    1655            0 :     qs = report_row_inconsistency ("deposits",
    1656              :                                    rowid,
    1657              :                                    "refund deadline past wire deadline");
    1658            0 :     if (0 > qs)
    1659              :     {
    1660            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1661            0 :       cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
    1662            0 :       return GNUNET_SYSERR;
    1663              :     }
    1664              :   }
    1665              : 
    1666            0 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
    1667              :   {
    1668            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1669            0 :     cc->qs = qs;
    1670            0 :     return GNUNET_SYSERR;
    1671              :   }
    1672            0 :   qs = check_known_coin ("deposit",
    1673              :                          issue,
    1674              :                          rowid,
    1675              :                          &deposit->coin.coin_pub,
    1676              :                          denom_pub,
    1677              :                          &deposit->amount_with_fee);
    1678            0 :   if (0 > qs)
    1679              :   {
    1680            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1681            0 :     cc->qs = qs;
    1682            0 :     return GNUNET_SYSERR;
    1683              :   }
    1684              : 
    1685              :   /* Verify deposit signature */
    1686              :   {
    1687              :     struct TALER_MerchantWireHashP h_wire;
    1688              :     struct TALER_DenominationHashP h_denom_pub;
    1689              : 
    1690            0 :     TALER_denom_pub_hash (denom_pub,
    1691              :                           &h_denom_pub);
    1692            0 :     TALER_merchant_wire_signature_hash (deposit->receiver_wire_account,
    1693              :                                         &deposit->wire_salt,
    1694              :                                         &h_wire);
    1695              :     /* NOTE: This is one of the operations we might eventually
    1696              :        want to do in parallel in the background to improve
    1697              :        auditor performance! */
    1698            0 :     if (GNUNET_OK !=
    1699            0 :         TALER_wallet_deposit_verify (&deposit->amount_with_fee,
    1700            0 :                                      &issue->fees.deposit,
    1701              :                                      &h_wire,
    1702              :                                      &deposit->h_contract_terms,
    1703            0 :                                      deposit->no_wallet_data_hash
    1704              :                                      ? NULL
    1705              :                                      : &deposit->wallet_data_hash,
    1706              :                                      &deposit->coin.h_age_commitment,
    1707              :                                      &deposit->h_policy,
    1708              :                                      &h_denom_pub,
    1709              :                                      deposit->timestamp,
    1710              :                                      &deposit->merchant_pub,
    1711              :                                      deposit->refund_deadline,
    1712              :                                      &deposit->coin.coin_pub,
    1713              :                                      &deposit->csig))
    1714              :     {
    1715            0 :       struct TALER_AUDITORDB_BadSigLosses bsl = {
    1716              :         .problem_row_id = rowid,
    1717              :         .operation = (char *) "deposit",
    1718              :         .loss = deposit->amount_with_fee,
    1719              :         .operation_specific_pub = deposit->coin.coin_pub.eddsa_pub
    1720              :       };
    1721              : 
    1722            0 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    1723              :                   "Failed to verify coin deposit signature in row %llu\n",
    1724              :                   (unsigned long long) rowid);
    1725            0 :       qs = TALER_AUDITORDB_insert_bad_sig_losses (
    1726              :         TALER_ARL_adb,
    1727              :         &bsl);
    1728            0 :       if (0 > qs)
    1729              :       {
    1730            0 :         GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1731            0 :         cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
    1732            0 :         return GNUNET_SYSERR;
    1733              :       }
    1734            0 :       TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_irregular_loss),
    1735              :                             &TALER_ARL_USE_AB (coin_irregular_loss),
    1736              :                             &deposit->amount_with_fee);
    1737            0 :       return GNUNET_OK;
    1738              :     }
    1739              :   }
    1740            0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1741              :               "Deposited coin %s in denomination `%s' of value %s\n",
    1742              :               TALER_B2S (&deposit->coin.coin_pub),
    1743              :               GNUNET_h2s (&issue->denom_hash.hash),
    1744              :               TALER_amount2s (&deposit->amount_with_fee));
    1745              : 
    1746              :   /* update old coin's denomination balance */
    1747            0 :   ds = get_denomination_summary (cc,
    1748              :                                  issue);
    1749            0 :   if (NULL == ds)
    1750              :   {
    1751            0 :     qs = report_row_inconsistency ("deposit",
    1752              :                                    rowid,
    1753              :                                    "denomination key for deposited coin unknown to auditor");
    1754            0 :     if (0 > qs)
    1755              :     {
    1756            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1757            0 :       cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
    1758            0 :       return GNUNET_SYSERR;
    1759              :     }
    1760              :   }
    1761              :   else
    1762              :   {
    1763            0 :     qs = reduce_denom_balance (ds,
    1764              :                                rowid,
    1765              :                                &deposit->amount_with_fee);
    1766            0 :     if (0 > qs)
    1767              :     {
    1768            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1769            0 :       cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
    1770            0 :       return GNUNET_SYSERR;
    1771              :     }
    1772              :   }
    1773              : 
    1774              :   /* update global deposit fees */
    1775            0 :   TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_deposit_fee_revenue),
    1776              :                         &TALER_ARL_USE_AB (coin_deposit_fee_revenue),
    1777              :                         &issue->fees.deposit);
    1778            0 :   return GNUNET_OK;
    1779              : }
    1780              : 
    1781              : 
    1782              : /**
    1783              :  * Function called with details about coins that were refunding,
    1784              :  * with the goal of auditing the refund's execution.  Adds the
    1785              :  * refunded amount back to the outstanding balance of the respective
    1786              :  * denomination.
    1787              :  *
    1788              :  * @param cc closure
    1789              :  * @param rowid unique serial ID for the refund in our DB
    1790              :  * @param denom_pub denomination public key of @a coin_pub
    1791              :  * @param coin_pub public key of the coin
    1792              :  * @param merchant_pub public key of the merchant
    1793              :  * @param merchant_sig signature of the merchant
    1794              :  * @param h_contract_terms hash of the proposal data known to merchant and customer
    1795              :  * @param rtransaction_id refund transaction ID chosen by the merchant
    1796              :  * @param full_refund true if the refunds total up to the entire deposited value
    1797              :  * @param amount_with_fee amount that was deposited including fee
    1798              :  * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
    1799              :  */
    1800              : static enum GNUNET_GenericReturnValue
    1801            0 : refund_cb (struct CoinContext *cc,
    1802              :            uint64_t rowid,
    1803              :            const struct TALER_DenominationPublicKey *denom_pub,
    1804              :            const struct TALER_CoinSpendPublicKeyP *coin_pub,
    1805              :            const struct TALER_MerchantPublicKeyP *merchant_pub,
    1806              :            const struct TALER_MerchantSignatureP *merchant_sig,
    1807              :            const struct TALER_PrivateContractHashP *h_contract_terms,
    1808              :            uint64_t rtransaction_id,
    1809              :            bool full_refund,
    1810              :            const struct TALER_Amount *amount_with_fee)
    1811              : {
    1812              :   const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue;
    1813              :   struct DenominationSummary *ds;
    1814              :   struct TALER_Amount amount_without_fee;
    1815              :   enum GNUNET_DB_QueryStatus qs;
    1816              : 
    1817            0 :   GNUNET_assert (rowid >= TALER_ARL_USE_PP (coins_refund_serial_id)); /* should be monotonically increasing */
    1818            0 :   TALER_ARL_USE_PP (coins_refund_serial_id) = rowid + 1;
    1819              : 
    1820            0 :   qs = TALER_ARL_get_denomination_info (denom_pub,
    1821              :                                         &issue,
    1822              :                                         NULL);
    1823            0 :   if (0 > qs)
    1824              :   {
    1825            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1826            0 :     cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
    1827            0 :     return GNUNET_SYSERR;
    1828              :   }
    1829            0 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    1830              :   {
    1831            0 :     qs = report_row_inconsistency ("refunds",
    1832              :                                    rowid,
    1833              :                                    "denomination key not found");
    1834            0 :     if (0 > qs)
    1835              :     {
    1836            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1837            0 :       cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
    1838            0 :       return GNUNET_SYSERR;
    1839              :     }
    1840            0 :     return GNUNET_OK;
    1841              :   }
    1842              : 
    1843              :   /* verify refund signature */
    1844            0 :   if (GNUNET_OK !=
    1845            0 :       TALER_merchant_refund_verify (coin_pub,
    1846              :                                     h_contract_terms,
    1847              :                                     rtransaction_id,
    1848              :                                     amount_with_fee,
    1849              :                                     merchant_pub,
    1850              :                                     merchant_sig))
    1851              :   {
    1852            0 :     struct TALER_AUDITORDB_BadSigLosses bsl = {
    1853              :       .problem_row_id = rowid,
    1854              :       .operation = (char *) "refund",
    1855              :       .loss = *amount_with_fee,
    1856              :       .operation_specific_pub = coin_pub->eddsa_pub
    1857              :     };
    1858              : 
    1859            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    1860              :                 "Failed to verify merchant refund signature in row %llu\n",
    1861              :                 (unsigned long long) rowid);
    1862            0 :     qs = TALER_AUDITORDB_insert_bad_sig_losses (
    1863              :       TALER_ARL_adb,
    1864              :       &bsl);
    1865            0 :     if (0 > qs)
    1866              :     {
    1867            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1868            0 :       cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
    1869            0 :       return GNUNET_SYSERR;
    1870              :     }
    1871            0 :     TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_irregular_loss),
    1872              :                           &TALER_ARL_USE_AB (coin_irregular_loss),
    1873              :                           amount_with_fee);
    1874            0 :     return GNUNET_OK;
    1875              :   }
    1876              : 
    1877            0 :   if (TALER_ARL_SR_INVALID_NEGATIVE ==
    1878            0 :       TALER_ARL_amount_subtract_neg (&amount_without_fee,
    1879              :                                      amount_with_fee,
    1880              :                                      &issue->fees.refund))
    1881              :   {
    1882            0 :     qs = report_amount_arithmetic_inconsistency ("refund (fee)",
    1883              :                                                  rowid,
    1884              :                                                  amount_with_fee,
    1885            0 :                                                  &issue->fees.refund,
    1886              :                                                  -1);
    1887            0 :     if (0 > qs)
    1888              :     {
    1889            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1890            0 :       cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
    1891            0 :       return GNUNET_SYSERR;
    1892              :     }
    1893            0 :     return GNUNET_OK;
    1894              :   }
    1895              : 
    1896            0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1897              :               "Refunding coin %s in denomination `%s' value %s\n",
    1898              :               TALER_B2S (coin_pub),
    1899              :               GNUNET_h2s (&issue->denom_hash.hash),
    1900              :               TALER_amount2s (amount_with_fee));
    1901              : 
    1902              :   /* update coin's denomination balance */
    1903            0 :   ds = get_denomination_summary (cc,
    1904              :                                  issue);
    1905            0 :   if (NULL == ds)
    1906              :   {
    1907            0 :     qs = report_row_inconsistency ("refund",
    1908              :                                    rowid,
    1909              :                                    "denomination key for refunded coin unknown to auditor");
    1910            0 :     if (0 > qs)
    1911              :     {
    1912            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1913            0 :       cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
    1914            0 :       return GNUNET_SYSERR;
    1915              :     }
    1916              :   }
    1917              :   else
    1918              :   {
    1919            0 :     TALER_ARL_amount_add (&ds->dcd.denom_balance,
    1920              :                           &ds->dcd.denom_balance,
    1921              :                           &amount_without_fee);
    1922            0 :     TALER_ARL_amount_add (&ds->dcd.denom_risk,
    1923              :                           &ds->dcd.denom_risk,
    1924              :                           &amount_without_fee);
    1925            0 :     TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_escrowed),
    1926              :                           &TALER_ARL_USE_AB (total_escrowed),
    1927              :                           &amount_without_fee);
    1928            0 :     TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_balance_risk),
    1929              :                           &TALER_ARL_USE_AB (coin_balance_risk),
    1930              :                           &amount_without_fee);
    1931            0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1932              :                 "New balance of denomination `%s' after refund is %s\n",
    1933              :                 GNUNET_h2s (&issue->denom_hash.hash),
    1934              :                 TALER_amount2s (&ds->dcd.denom_balance));
    1935              :   }
    1936              :   /* update total refund fee balance */
    1937            0 :   TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_refund_fee_revenue),
    1938              :                         &TALER_ARL_USE_AB (coin_refund_fee_revenue),
    1939              :                         &issue->fees.refund);
    1940            0 :   if (full_refund)
    1941              :   {
    1942            0 :     TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_deposit_fee_loss),
    1943              :                           &TALER_ARL_USE_AB (coin_deposit_fee_loss),
    1944              :                           &issue->fees.deposit);
    1945              :   }
    1946            0 :   return GNUNET_OK;
    1947              : }
    1948              : 
    1949              : 
    1950              : /**
    1951              :  * Function called with details about purse refunds that have been made, with
    1952              :  * the goal of auditing the purse refund's execution.
    1953              :  *
    1954              :  * @param cc closure
    1955              :  * @param rowid row of the purse-refund
    1956              :  * @param amount_with_fee amount of the deposit into the purse
    1957              :  * @param coin_pub coin that is to be refunded the @a given amount_with_fee
    1958              :  * @param denom_pub denomination of @a coin_pub
    1959              :  * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
    1960              :  */
    1961              : static enum GNUNET_GenericReturnValue
    1962            0 : purse_refund_coin_cb (
    1963              :   struct CoinContext *cc,
    1964              :   uint64_t rowid,
    1965              :   const struct TALER_Amount *amount_with_fee,
    1966              :   const struct TALER_CoinSpendPublicKeyP *coin_pub,
    1967              :   const struct TALER_DenominationPublicKey *denom_pub)
    1968              : {
    1969              :   const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue;
    1970              :   struct DenominationSummary *ds;
    1971              :   enum GNUNET_DB_QueryStatus qs;
    1972              : 
    1973            0 :   qs = TALER_ARL_get_denomination_info (denom_pub,
    1974              :                                         &issue,
    1975              :                                         NULL);
    1976            0 :   if (0 > qs)
    1977              :   {
    1978            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1979            0 :     cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
    1980            0 :     return GNUNET_SYSERR;
    1981              :   }
    1982            0 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    1983              :   {
    1984            0 :     qs = report_row_inconsistency ("purse-refunds",
    1985              :                                    rowid,
    1986              :                                    "denomination key not found");
    1987            0 :     if (0 > qs)
    1988              :     {
    1989            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1990            0 :       cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
    1991            0 :       return GNUNET_SYSERR;
    1992              :     }
    1993            0 :     return GNUNET_OK;
    1994              :   }
    1995            0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1996              :               "Aborted purse-deposit of coin %s in denomination `%s' value %s\n",
    1997              :               TALER_B2S (coin_pub),
    1998              :               GNUNET_h2s (&issue->denom_hash.hash),
    1999              :               TALER_amount2s (amount_with_fee));
    2000              : 
    2001              :   /* update coin's denomination balance */
    2002            0 :   ds = get_denomination_summary (cc,
    2003              :                                  issue);
    2004            0 :   if (NULL == ds)
    2005              :   {
    2006            0 :     qs = report_row_inconsistency ("purse-refund",
    2007              :                                    rowid,
    2008              :                                    "denomination key for purse-refunded coin unknown to auditor");
    2009            0 :     if (0 > qs)
    2010              :     {
    2011            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    2012            0 :       cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
    2013            0 :       return GNUNET_SYSERR;
    2014              :     }
    2015              :   }
    2016              :   else
    2017              :   {
    2018            0 :     TALER_ARL_amount_add (&ds->dcd.denom_balance,
    2019              :                           &ds->dcd.denom_balance,
    2020              :                           amount_with_fee);
    2021            0 :     TALER_ARL_amount_add (&ds->dcd.denom_risk,
    2022              :                           &ds->dcd.denom_risk,
    2023              :                           amount_with_fee);
    2024            0 :     TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_escrowed),
    2025              :                           &TALER_ARL_USE_AB (total_escrowed),
    2026              :                           amount_with_fee);
    2027            0 :     TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_balance_risk),
    2028              :                           &TALER_ARL_USE_AB (coin_balance_risk),
    2029              :                           amount_with_fee);
    2030            0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    2031              :                 "New balance of denomination `%s' after purse-refund is %s\n",
    2032              :                 GNUNET_h2s (&issue->denom_hash.hash),
    2033              :                 TALER_amount2s (&ds->dcd.denom_balance));
    2034              :   }
    2035              :   /* update total deposit fee balance */
    2036            0 :   TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_deposit_fee_loss),
    2037              :                         &TALER_ARL_USE_AB (coin_deposit_fee_loss),
    2038              :                         &issue->fees.deposit);
    2039              : 
    2040            0 :   return GNUNET_OK;
    2041              : }
    2042              : 
    2043              : 
    2044              : /**
    2045              :  * Function called with details about a purse that was refunded.  Adds the
    2046              :  * refunded amounts back to the outstanding balance of the respective
    2047              :  * denominations.
    2048              :  *
    2049              :  * @param cc closure
    2050              :  * @param rowid unique serial ID for the refund in our DB
    2051              :  * @param purse_pub public key of the purse
    2052              :  * @param reserve_pub public key of the targeted reserve (ignored)
    2053              :  * @param val targeted amount to be in the reserve (ignored)
    2054              :  * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
    2055              :  */
    2056              : static enum GNUNET_GenericReturnValue
    2057            0 : purse_refund_cb (struct CoinContext *cc,
    2058              :                  uint64_t rowid,
    2059              :                  const struct TALER_PurseContractPublicKeyP *purse_pub,
    2060              :                  const struct TALER_ReservePublicKeyP *reserve_pub,
    2061              :                  const struct TALER_Amount *val)
    2062              : {
    2063              :   enum GNUNET_DB_QueryStatus qs;
    2064              : 
    2065              :   (void) val; /* irrelevant on refund */
    2066              :   (void) reserve_pub; /* irrelevant, may even be NULL */
    2067            0 :   GNUNET_assert (rowid >=
    2068              :                  TALER_ARL_USE_PP (coins_purse_refunds_serial_id)); /* should be monotonically increasing */
    2069            0 :   TALER_ARL_USE_PP (coins_purse_refunds_serial_id) = rowid + 1;
    2070            0 :   qs = TALER_EXCHANGEDB_iterate_purse_deposits_by_purse (TALER_ARL_edb,
    2071              :                                                          purse_pub,
    2072              :                                                          &
    2073              :                                                          purse_refund_coin_cb,
    2074              :                                                          cc);
    2075            0 :   if (qs < 0)
    2076              :   {
    2077            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    2078            0 :     return GNUNET_SYSERR;
    2079              :   }
    2080            0 :   return GNUNET_OK;
    2081              : }
    2082              : 
    2083              : 
    2084              : /**
    2085              :  * Check that the recoup operation was properly initiated by a coin
    2086              :  * and update the denomination's losses accordingly.
    2087              :  *
    2088              :  * @param cc the context with details about the coin
    2089              :  * @param operation name of the operation matching @a rowid
    2090              :  * @param rowid row identifier used to uniquely identify the recoup operation
    2091              :  * @param amount how much should be added back to the reserve
    2092              :  * @param coin public information about the coin
    2093              :  * @param denom_pub public key of the denomionation of @a coin
    2094              :  * @param coin_sig signature with @e coin_pub of type #TALER_SIGNATURE_WALLET_COIN_RECOUP
    2095              :  * @param coin_blind blinding factor used to blind the coin
    2096              :  * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
    2097              :  */
    2098              : static enum GNUNET_GenericReturnValue
    2099            0 : check_recoup (struct CoinContext *cc,
    2100              :               const char *operation,
    2101              :               uint64_t rowid,
    2102              :               const struct TALER_Amount *amount,
    2103              :               const struct TALER_CoinPublicInfo *coin,
    2104              :               const struct TALER_DenominationPublicKey *denom_pub,
    2105              :               const struct TALER_CoinSpendSignatureP *coin_sig,
    2106              :               const union GNUNET_CRYPTO_BlindingSecretP *coin_blind)
    2107              : {
    2108              :   struct DenominationSummary *ds;
    2109              :   enum GNUNET_DB_QueryStatus qs;
    2110              :   const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue;
    2111              : 
    2112            0 :   if (GNUNET_OK !=
    2113            0 :       TALER_wallet_recoup_verify (&coin->denom_pub_hash,
    2114              :                                   coin_blind,
    2115              :                                   &coin->coin_pub,
    2116              :                                   coin_sig))
    2117              :   {
    2118            0 :     qs = report_row_inconsistency (operation,
    2119              :                                    rowid,
    2120              :                                    "recoup signature invalid");
    2121            0 :     if (0 > qs)
    2122              :     {
    2123            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    2124            0 :       cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
    2125            0 :       return GNUNET_SYSERR;
    2126              :     }
    2127              :   }
    2128            0 :   if (GNUNET_OK !=
    2129            0 :       TALER_test_coin_valid (coin,
    2130              :                              denom_pub))
    2131              :   {
    2132            0 :     struct TALER_AUDITORDB_BadSigLosses bsl = {
    2133              :       .problem_row_id = rowid,
    2134              :       .operation = (char *) operation,
    2135              :       .loss = *amount,
    2136              :       .operation_specific_pub = coin->coin_pub.eddsa_pub
    2137              :     };
    2138              : 
    2139            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    2140              :                 "Failed to verify coin signature in row %llu\n",
    2141              :                 (unsigned long long) rowid);
    2142            0 :     qs = TALER_AUDITORDB_insert_bad_sig_losses (
    2143              :       TALER_ARL_adb,
    2144              :       &bsl);
    2145              : 
    2146            0 :     if (0 > qs)
    2147              :     {
    2148            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    2149            0 :       cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
    2150            0 :       return GNUNET_SYSERR;
    2151              :     }
    2152            0 :     TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_irregular_loss),
    2153              :                           &TALER_ARL_USE_AB (coin_irregular_loss),
    2154              :                           amount);
    2155              :   }
    2156            0 :   qs = TALER_ARL_get_denomination_info_by_hash (&coin->denom_pub_hash,
    2157              :                                                 &issue);
    2158            0 :   if (0 > qs)
    2159              :   {
    2160            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    2161            0 :     cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
    2162            0 :     return GNUNET_SYSERR;
    2163              :   }
    2164            0 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    2165              :   {
    2166            0 :     qs = report_row_inconsistency (operation,
    2167              :                                    rowid,
    2168              :                                    "denomination key not found");
    2169            0 :     if (0 > qs)
    2170              :     {
    2171            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    2172            0 :       cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
    2173            0 :       return GNUNET_SYSERR;
    2174              :     }
    2175            0 :     return GNUNET_OK;
    2176              :   }
    2177            0 :   qs = check_known_coin (operation,
    2178              :                          issue,
    2179              :                          rowid,
    2180              :                          &coin->coin_pub,
    2181              :                          denom_pub,
    2182              :                          amount);
    2183            0 :   if (0 > qs)
    2184              :   {
    2185            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    2186            0 :     cc->qs = qs;
    2187            0 :     return GNUNET_SYSERR;
    2188              :   }
    2189            0 :   ds = get_denomination_summary (cc,
    2190              :                                  issue);
    2191            0 :   if (NULL == ds)
    2192              :   {
    2193            0 :     qs = report_row_inconsistency ("recoup",
    2194              :                                    rowid,
    2195              :                                    "denomination key for recouped coin unknown to auditor");
    2196            0 :     if (0 > qs)
    2197              :     {
    2198            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    2199            0 :       cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
    2200            0 :       return GNUNET_SYSERR;
    2201              :     }
    2202              :   }
    2203              :   else
    2204              :   {
    2205            0 :     if (! ds->was_revoked)
    2206              :     {
    2207            0 :       struct TALER_AUDITORDB_BadSigLosses bsldnr = {
    2208              :         .problem_row_id = rowid,
    2209              :         .operation = (char *) operation,
    2210              :         .loss = *amount,
    2211              :         .operation_specific_pub = coin->coin_pub.eddsa_pub
    2212              :       };
    2213              : 
    2214            0 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    2215              :                   "Recoup allowed on non-revoked denomination in row %llu\n",
    2216              :                   (unsigned long long) rowid);
    2217            0 :       qs = TALER_AUDITORDB_insert_bad_sig_losses (
    2218              :         TALER_ARL_adb,
    2219              :         &bsldnr);
    2220              : 
    2221            0 :       if (qs < 0)
    2222              :       {
    2223            0 :         GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    2224            0 :         cc->qs = qs;
    2225            0 :         return GNUNET_SYSERR;
    2226              :       }
    2227            0 :       TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_irregular_loss),
    2228              :                             &TALER_ARL_USE_AB (coin_irregular_loss),
    2229              :                             amount);
    2230              :     }
    2231            0 :     TALER_ARL_amount_add (&ds->dcd.recoup_loss,
    2232              :                           &ds->dcd.recoup_loss,
    2233              :                           amount);
    2234            0 :     TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_recoup_loss),
    2235              :                           &TALER_ARL_USE_AB (total_recoup_loss),
    2236              :                           amount);
    2237              :   }
    2238            0 :   return GNUNET_OK;
    2239              : }
    2240              : 
    2241              : 
    2242              : /**
    2243              :  * Function called about recoups the exchange has to perform.
    2244              :  *
    2245              :  * @param cc a `struct CoinContext *`
    2246              :  * @param rowid row identifier used to uniquely identify the recoup operation
    2247              :  * @param timestamp when did we receive the recoup request
    2248              :  * @param amount how much should be added back to the reserve
    2249              :  * @param reserve_pub public key of the reserve
    2250              :  * @param coin public information about the coin
    2251              :  * @param denom_pub denomination public key of @a coin
    2252              :  * @param coin_sig signature with @e coin_pub of type #TALER_SIGNATURE_WALLET_COIN_RECOUP
    2253              :  * @param coin_blind blinding factor used to blind the coin
    2254              :  * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
    2255              :  */
    2256              : static enum GNUNET_GenericReturnValue
    2257            0 : recoup_cb (struct CoinContext *cc,
    2258              :            uint64_t rowid,
    2259              :            struct GNUNET_TIME_Timestamp timestamp,
    2260              :            const struct TALER_Amount *amount,
    2261              :            const struct TALER_ReservePublicKeyP *reserve_pub,
    2262              :            const struct TALER_CoinPublicInfo *coin,
    2263              :            const struct TALER_DenominationPublicKey *denom_pub,
    2264              :            const struct TALER_CoinSpendSignatureP *coin_sig,
    2265              :            const union GNUNET_CRYPTO_BlindingSecretP *coin_blind)
    2266              : {
    2267              :   enum GNUNET_DB_QueryStatus qs;
    2268              : 
    2269            0 :   GNUNET_assert (rowid >= TALER_ARL_USE_PP (coins_recoup_serial_id)); /* should be monotonically increasing */
    2270            0 :   TALER_ARL_USE_PP (coins_recoup_serial_id) = rowid + 1;
    2271              :   (void) timestamp;
    2272              :   (void) reserve_pub;
    2273            0 :   if (GNUNET_OK !=
    2274            0 :       TALER_wallet_recoup_verify (&coin->denom_pub_hash,
    2275              :                                   coin_blind,
    2276              :                                   &coin->coin_pub,
    2277              :                                   coin_sig))
    2278              :   {
    2279            0 :     struct TALER_AUDITORDB_BadSigLosses bsl = {
    2280              :       .problem_row_id = rowid,
    2281              :       .operation = (char *) "recoup",
    2282              :       .loss = *amount,
    2283              :       .operation_specific_pub = coin->coin_pub.eddsa_pub
    2284              :     };
    2285              : 
    2286            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    2287              :                 "Failed to verify recoup signature in row %llu\n",
    2288              :                 (unsigned long long) rowid);
    2289            0 :     qs = TALER_AUDITORDB_insert_bad_sig_losses (
    2290              :       TALER_ARL_adb,
    2291              :       &bsl);
    2292            0 :     if (qs < 0)
    2293              :     {
    2294            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    2295            0 :       cc->qs = qs;
    2296            0 :       return GNUNET_SYSERR;
    2297              :     }
    2298            0 :     TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_irregular_loss),
    2299              :                           &TALER_ARL_USE_AB (coin_irregular_loss),
    2300              :                           amount);
    2301            0 :     return GNUNET_OK;
    2302              :   }
    2303            0 :   return check_recoup (cc,
    2304              :                        "recoup",
    2305              :                        rowid,
    2306              :                        amount,
    2307              :                        coin,
    2308              :                        denom_pub,
    2309              :                        coin_sig,
    2310              :                        coin_blind);
    2311              : }
    2312              : 
    2313              : 
    2314              : #if FIXME_9828
    2315              : /**
    2316              :  * Function called about recoups on refreshed coins the exchange had to
    2317              :  * perform. Updates the denomination balance(s). Does not change the
    2318              :  * coin balances, as those are already updated when we check the coin
    2319              :  * history.
    2320              :  *
    2321              :  * @param cc a `struct CoinContext *`
    2322              :  * @param rowid row identifier used to uniquely identify the recoup operation
    2323              :  * @param timestamp when did we receive the recoup request
    2324              :  * @param amount how much should be added back to the old coin
    2325              :  * @param old_coin_pub original coin that was refreshed to create @a coin
    2326              :  * @param old_denom_pub_hash hash of the public key of @a old_coin_pub
    2327              :  * @param coin public information about the fresh coin
    2328              :  * @param denom_pub denomination public key of @a coin
    2329              :  * @param coin_sig signature with @e coin_pub of type #TALER_SIGNATURE_WALLET_COIN_RECOUP
    2330              :  * @param coin_blind blinding factor used to blind the coin
    2331              :  * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
    2332              :  */
    2333              : static enum GNUNET_GenericReturnValue
    2334              : recoup_refresh_cb (struct CoinContext *cc,
    2335              :                    uint64_t rowid,
    2336              :                    struct GNUNET_TIME_Timestamp timestamp,
    2337              :                    const struct TALER_Amount *amount,
    2338              :                    const struct TALER_CoinSpendPublicKeyP *old_coin_pub,
    2339              :                    const struct TALER_DenominationHashP *old_denom_pub_hash,
    2340              :                    const struct TALER_CoinPublicInfo *coin,
    2341              :                    const struct TALER_DenominationPublicKey *denom_pub,
    2342              :                    const struct TALER_CoinSpendSignatureP *coin_sig,
    2343              :                    const union GNUNET_CRYPTO_BlindingSecretP *coin_blind)
    2344              : {
    2345              :   const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue;
    2346              :   enum GNUNET_DB_QueryStatus qs;
    2347              : 
    2348              :   (void) timestamp;
    2349              :   (void) old_coin_pub;
    2350              :   GNUNET_assert (rowid >= TALER_ARL_USE_PP (coins_recoup_refresh_serial_id)); /* should be monotonically increasing */
    2351              :   TALER_ARL_USE_PP (coins_recoup_refresh_serial_id) = rowid + 1;
    2352              :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    2353              :               "Recoup-refresh amount is %s\n",
    2354              :               TALER_amount2s (amount));
    2355              : 
    2356              :   /* Update old coin's denomination balance summary */
    2357              :   qs = TALER_ARL_get_denomination_info_by_hash (old_denom_pub_hash,
    2358              :                                                 &issue);
    2359              :   if (qs < 0)
    2360              :   {
    2361              :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    2362              :     cc->qs = qs;
    2363              :     return GNUNET_SYSERR;
    2364              :   }
    2365              :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    2366              :   {
    2367              :     qs = report_row_inconsistency ("refresh-recoup",
    2368              :                                    rowid,
    2369              :                                    "denomination key of old coin not found");
    2370              :     if (qs < 0)
    2371              :     {
    2372              :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    2373              :       cc->qs = qs;
    2374              :       return GNUNET_SYSERR;
    2375              :     }
    2376              :   }
    2377              : 
    2378              :   {
    2379              :     struct DenominationSummary *dso;
    2380              : 
    2381              :     dso = get_denomination_summary (cc,
    2382              :                                     issue);
    2383              :     if (NULL == dso)
    2384              :     {
    2385              :       qs = report_row_inconsistency ("refresh_reveal",
    2386              :                                      rowid,
    2387              :                                      "denomination key for old coin unknown to auditor");
    2388              :       if (qs < 0)
    2389              :       {
    2390              :         GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    2391              :         cc->qs = qs;
    2392              :         return GNUNET_SYSERR;
    2393              :       }
    2394              :     }
    2395              :     else
    2396              :     {
    2397              :       TALER_ARL_amount_add (&dso->dcd.denom_balance,
    2398              :                             &dso->dcd.denom_balance,
    2399              :                             amount);
    2400              :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    2401              :                   "New balance of denomination `%s' after refresh-recoup is %s\n",
    2402              :                   GNUNET_h2s (&issue->denom_hash.hash),
    2403              :                   TALER_amount2s (&dso->dcd.denom_balance));
    2404              :     }
    2405              :   }
    2406              : 
    2407              :   if (GNUNET_OK !=
    2408              :       TALER_wallet_recoup_refresh_verify (&coin->denom_pub_hash,
    2409              :                                           coin_blind,
    2410              :                                           &coin->coin_pub,
    2411              :                                           coin_sig))
    2412              :   {
    2413              :     struct TALER_AUDITORDB_BadSigLosses bsl = {
    2414              :       .problem_row_id = rowid,
    2415              :       .operation = (char *) "recoup-refresh",
    2416              :       .loss = *amount,
    2417              :       .operation_specific_pub = coin->coin_pub.eddsa_pub
    2418              :     };
    2419              : 
    2420              :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    2421              :                 "Failed to verify recoup-refresh signature in row %llu\n",
    2422              :                 (unsigned long long) rowid);
    2423              :     qs = TALER_AUDITORDB_insert_bad_sig_losses (
    2424              :       TALER_ARL_adb,
    2425              :       &bsl);
    2426              :     if (qs < 0)
    2427              :     {
    2428              :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    2429              :       cc->qs = qs;
    2430              :       return GNUNET_SYSERR;
    2431              :     }
    2432              :     TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_irregular_loss),
    2433              :                           &TALER_ARL_USE_AB (coin_irregular_loss),
    2434              :                           amount);
    2435              :     return GNUNET_OK;
    2436              :   }
    2437              :   return check_recoup (cc,
    2438              :                        "recoup-refresh",
    2439              :                        rowid,
    2440              :                        amount,
    2441              :                        coin,
    2442              :                        denom_pub,
    2443              :                        coin_sig,
    2444              :                        coin_blind);
    2445              : }
    2446              : 
    2447              : 
    2448              : #endif
    2449              : 
    2450              : 
    2451              : /**
    2452              :  * Function called with the results of iterate_denomination_info(),
    2453              :  * or directly (!).  Used to check that we correctly signed the
    2454              :  * denomination and to warn if there are denominations not approved
    2455              :  * by this auditor.
    2456              :  *
    2457              :  * @param iqs closure, pointer to `enum GNUNET_DB_QueryStatus`
    2458              :  * @param denom_serial row ID of the denominations table of the exchange DB
    2459              :  * @param denom_pub public key, sometimes NULL (!)
    2460              :  * @param issue issuing information with value, fees and other info about the denomination.
    2461              :  */
    2462              : static void
    2463          160 : check_denomination (
    2464              :   enum GNUNET_DB_QueryStatus *iqs,
    2465              :   uint64_t denom_serial,
    2466              :   const struct TALER_DenominationPublicKey *denom_pub,
    2467              :   const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue)
    2468              : {
    2469              :   enum GNUNET_DB_QueryStatus qs;
    2470              :   struct TALER_AuditorSignatureP auditor_sig;
    2471              : 
    2472              :   (void) denom_pub;
    2473          160 :   qs = TALER_EXCHANGEDB_get_auditor_denom_sig (TALER_ARL_edb,
    2474              :                                                &issue->denom_hash,
    2475              :                                                &TALER_ARL_auditor_pub,
    2476              :                                                &auditor_sig);
    2477          160 :   if (0 > qs)
    2478              :   {
    2479            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    2480            0 :     *iqs = qs;
    2481            0 :     return;
    2482              :   }
    2483          160 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    2484              :   {
    2485            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    2486              :                 "Encountered denomination `%s' (%s) valid from %s (%llu-%llu) that this auditor is not auditing!\n",
    2487              :                 GNUNET_h2s (&issue->denom_hash.hash),
    2488              :                 TALER_amount2s (&issue->value),
    2489              :                 GNUNET_TIME_timestamp2s (issue->start),
    2490              :                 (unsigned long long) issue->start.abs_time.abs_value_us,
    2491              :                 (unsigned long long) issue->expire_legal.abs_time.abs_value_us);
    2492            0 :     return; /* skip! */
    2493              :   }
    2494          160 :   if (GNUNET_OK !=
    2495          160 :       TALER_auditor_denom_validity_verify (
    2496              :         TALER_ARL_auditor_url,
    2497              :         &issue->denom_hash,
    2498              :         &TALER_ARL_master_pub,
    2499              :         issue->start,
    2500              :         issue->expire_withdraw,
    2501              :         issue->expire_deposit,
    2502              :         issue->expire_legal,
    2503              :         &issue->value,
    2504              :         &issue->fees,
    2505              :         &TALER_ARL_auditor_pub,
    2506              :         &auditor_sig))
    2507              :   {
    2508            0 :     struct TALER_AUDITORDB_DenominationsWithoutSigs dws = {
    2509              :       .denompub_h = issue->denom_hash,
    2510              :       .start_time = issue->start.abs_time,
    2511              :       .end_time = issue->expire_legal.abs_time,
    2512              :       .value = issue->value
    2513              :     };
    2514              : 
    2515            0 :     qs = TALER_AUDITORDB_insert_denominations_without_sigs (
    2516              :       TALER_ARL_adb,
    2517              :       &dws);
    2518              : 
    2519            0 :     if (qs < 0)
    2520              :     {
    2521            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    2522            0 :       *iqs = qs;
    2523            0 :       return;
    2524              :     }
    2525              :   }
    2526          160 :   *iqs = qs;
    2527              : }
    2528              : 
    2529              : 
    2530              : /**
    2531              :  * Function called with details about purse deposits that have been made, with
    2532              :  * the goal of auditing the deposit's execution.
    2533              :  *
    2534              :  * @param cc closure
    2535              :  * @param rowid unique serial ID for the deposit in our DB
    2536              :  * @param deposit deposit details
    2537              :  * @param reserve_pub which reserve is the purse merged into, NULL if unknown
    2538              :  * @param flags purse flags
    2539              :  * @param auditor_balance purse balance (according to the
    2540              :  *          auditor during auditing)
    2541              :  * @param purse_total target amount the purse should reach
    2542              :  * @param denom_pub denomination public key of @a coin_pub
    2543              :  * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
    2544              :  */
    2545              : static enum GNUNET_GenericReturnValue
    2546            0 : purse_deposit_cb (
    2547              :   struct CoinContext *cc,
    2548              :   uint64_t rowid,
    2549              :   const struct TALER_EXCHANGEDB_PurseDeposit *deposit,
    2550              :   const struct TALER_ReservePublicKeyP *reserve_pub,
    2551              :   enum TALER_WalletAccountMergeFlags flags,
    2552              :   const struct TALER_Amount *auditor_balance,
    2553              :   const struct TALER_Amount *purse_total,
    2554              :   const struct TALER_DenominationPublicKey *denom_pub)
    2555              : {
    2556              :   enum GNUNET_DB_QueryStatus qs;
    2557              :   struct TALER_DenominationHashP dh;
    2558              :   const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue;
    2559              :   struct DenominationSummary *ds;
    2560              : 
    2561              :   (void) flags;
    2562              :   (void) auditor_balance;
    2563              :   (void) purse_total;
    2564              :   (void) reserve_pub;
    2565            0 :   GNUNET_assert (rowid >=
    2566              :                  TALER_ARL_USE_PP (coins_purse_deposits_serial_id));
    2567            0 :   TALER_ARL_USE_PP (coins_purse_deposits_serial_id) = rowid + 1;
    2568            0 :   qs = TALER_ARL_get_denomination_info (denom_pub,
    2569              :                                         &issue,
    2570              :                                         &dh);
    2571            0 :   if (0 > qs)
    2572              :   {
    2573            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    2574            0 :     cc->qs = qs;
    2575            0 :     return GNUNET_SYSERR;
    2576              :   }
    2577            0 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    2578              :   {
    2579            0 :     qs = report_row_inconsistency ("purse-deposits",
    2580              :                                    rowid,
    2581              :                                    "denomination key not found");
    2582            0 :     if (0 > qs)
    2583              :     {
    2584            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    2585            0 :       cc->qs = qs;
    2586            0 :       return GNUNET_SYSERR;
    2587              :     }
    2588            0 :     return GNUNET_OK;
    2589              :   }
    2590            0 :   qs = check_known_coin ("purse-deposit",
    2591              :                          issue,
    2592              :                          rowid,
    2593              :                          &deposit->coin_pub,
    2594              :                          denom_pub,
    2595              :                          &deposit->amount);
    2596            0 :   if (0 > qs)
    2597              :   {
    2598            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    2599            0 :     cc->qs = qs;
    2600            0 :     return GNUNET_SYSERR;
    2601              :   }
    2602              : 
    2603            0 :   if (GNUNET_OK !=
    2604            0 :       TALER_wallet_purse_deposit_verify (
    2605            0 :         NULL != deposit->exchange_base_url
    2606              :         ? deposit->exchange_base_url
    2607              :         : TALER_ARL_exchange_url,
    2608              :         &deposit->purse_pub,
    2609              :         &deposit->amount,
    2610              :         &dh,
    2611              :         &deposit->h_age_commitment,
    2612              :         &deposit->coin_pub,
    2613              :         &deposit->coin_sig))
    2614              :   {
    2615            0 :     struct TALER_AUDITORDB_BadSigLosses bsl = {
    2616              :       .problem_row_id = rowid,
    2617              :       .operation = (char *) "purse-deposit",
    2618              :       .loss = deposit->amount,
    2619              :       .operation_specific_pub = deposit->coin_pub.eddsa_pub
    2620              :     };
    2621              : 
    2622            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    2623              :                 "Failed to verify purse deposit signature in row %llu\n",
    2624              :                 (unsigned long long) rowid);
    2625            0 :     qs = TALER_AUDITORDB_insert_bad_sig_losses (
    2626              :       TALER_ARL_adb,
    2627              :       &bsl);
    2628            0 :     if (0 > qs)
    2629              :     {
    2630            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    2631            0 :       cc->qs = qs;
    2632            0 :       return GNUNET_SYSERR;
    2633              :     }
    2634            0 :     TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_irregular_loss),
    2635              :                           &TALER_ARL_USE_AB (coin_irregular_loss),
    2636              :                           &deposit->amount);
    2637            0 :     return GNUNET_OK;
    2638              :   }
    2639              : 
    2640              :   /* update coin's denomination balance */
    2641            0 :   ds = get_denomination_summary (cc,
    2642              :                                  issue);
    2643            0 :   if (NULL == ds)
    2644              :   {
    2645            0 :     qs = report_row_inconsistency ("purse-deposit",
    2646              :                                    rowid,
    2647              :                                    "denomination key for purse-deposited coin unknown to auditor");
    2648            0 :     if (0 > qs)
    2649              :     {
    2650            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    2651            0 :       cc->qs = qs;
    2652            0 :       return GNUNET_SYSERR;
    2653              :     }
    2654              :   }
    2655              :   else
    2656              :   {
    2657            0 :     qs = reduce_denom_balance (ds,
    2658              :                                rowid,
    2659              :                                &deposit->amount);
    2660            0 :     if (0 > qs)
    2661              :     {
    2662            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    2663            0 :       cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
    2664            0 :       return GNUNET_SYSERR;
    2665              :     }
    2666              :   }
    2667              : 
    2668              :   /* update global deposit fees */
    2669            0 :   TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_deposit_fee_revenue),
    2670              :                         &TALER_ARL_USE_AB (coin_deposit_fee_revenue),
    2671              :                         &issue->fees.deposit);
    2672            0 :   return GNUNET_OK;
    2673              : }
    2674              : 
    2675              : 
    2676              : /**
    2677              :  * Analyze the exchange's processing of coins.
    2678              :  *
    2679              :  * @param cls closure
    2680              :  * @return transaction status code
    2681              :  */
    2682              : static enum GNUNET_DB_QueryStatus
    2683            4 : analyze_coins (void *cls)
    2684              : {
    2685              :   struct CoinContext cc;
    2686              :   enum GNUNET_DB_QueryStatus qs;
    2687              :   enum GNUNET_DB_QueryStatus iqs;
    2688              : 
    2689              :   (void) cls;
    2690            4 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    2691              :               "Checking denominations...\n");
    2692            4 :   iqs = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
    2693            4 :   qs = TALER_EXCHANGEDB_iterate_denomination_info (TALER_ARL_edb,
    2694              :                                                    &check_denomination,
    2695              :                                                    &iqs);
    2696            4 :   if (0 > qs)
    2697              :   {
    2698            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    2699            0 :     return qs;
    2700              :   }
    2701            4 :   if (0 > iqs)
    2702              :   {
    2703            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == iqs);
    2704            0 :     return iqs;
    2705              :   }
    2706            4 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    2707              :               "Analyzing coins\n");
    2708            4 :   qs = TALER_AUDITORDB_get_auditor_progress (
    2709              :     TALER_ARL_adb,
    2710              :     TALER_ARL_GET_PP (coins_withdraw_serial_id),
    2711              :     TALER_ARL_GET_PP (coins_deposit_serial_id),
    2712              :     TALER_ARL_GET_PP (coins_melt_serial_id),
    2713              :     TALER_ARL_GET_PP (coins_refund_serial_id),
    2714              :     TALER_ARL_GET_PP (coins_recoup_serial_id),
    2715              :     TALER_ARL_GET_PP (coins_recoup_refresh_serial_id),
    2716              :     TALER_ARL_GET_PP (coins_purse_deposits_serial_id),
    2717              :     TALER_ARL_GET_PP (coins_purse_refunds_serial_id),
    2718              :     NULL);
    2719            4 :   if (0 > qs)
    2720              :   {
    2721            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    2722            0 :     return qs;
    2723              :   }
    2724            4 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    2725              :   {
    2726            0 :     GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
    2727              :                 "First analysis using this auditor, starting from scratch\n");
    2728              :   }
    2729              :   else
    2730              :   {
    2731            4 :     GNUNET_log (
    2732              :       GNUNET_ERROR_TYPE_INFO,
    2733              :       "Resuming coin audit at %llu/%llu/%llu/%llu/%llu/%llu/%llu/%llu\n",
    2734              :       (unsigned long long) TALER_ARL_USE_PP (
    2735              :         coins_withdraw_serial_id),
    2736              :       (unsigned long long) TALER_ARL_USE_PP (
    2737              :         coins_deposit_serial_id),
    2738              :       (unsigned long long) TALER_ARL_USE_PP (
    2739              :         coins_melt_serial_id),
    2740              :       (unsigned long long) TALER_ARL_USE_PP (
    2741              :         coins_refund_serial_id),
    2742              :       (unsigned long long) TALER_ARL_USE_PP (
    2743              :         coins_recoup_serial_id),
    2744              :       (unsigned long long) TALER_ARL_USE_PP (
    2745              :         coins_recoup_refresh_serial_id),
    2746              :       (unsigned long long) TALER_ARL_USE_PP (
    2747              :         coins_purse_deposits_serial_id),
    2748              :       (unsigned long long) TALER_ARL_USE_PP (
    2749              :         coins_purse_refunds_serial_id));
    2750              :   }
    2751              : 
    2752              :   /* setup 'cc' */
    2753            4 :   cc.qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
    2754            4 :   cc.denom_summaries = GNUNET_CONTAINER_multihashmap_create (256,
    2755              :                                                              GNUNET_NO);
    2756            4 :   qs = TALER_AUDITORDB_get_balance (
    2757              :     TALER_ARL_adb,
    2758              :     TALER_ARL_GET_AB (coin_balance_risk),
    2759              :     TALER_ARL_GET_AB (total_escrowed),
    2760              :     TALER_ARL_GET_AB (coin_irregular_loss),
    2761              :     TALER_ARL_GET_AB (coin_melt_fee_revenue),
    2762              :     TALER_ARL_GET_AB (coin_deposit_fee_revenue),
    2763              :     TALER_ARL_GET_AB (coin_deposit_fee_loss),
    2764              :     TALER_ARL_GET_AB (coin_refund_fee_revenue),
    2765              :     TALER_ARL_GET_AB (total_recoup_loss),
    2766              :     TALER_ARL_GET_AB (coins_total_arithmetic_delta_plus),
    2767              :     TALER_ARL_GET_AB (coins_total_arithmetic_delta_minus),
    2768              :     TALER_ARL_GET_AB (coins_reported_emergency_risk_by_count),
    2769              :     TALER_ARL_GET_AB (coins_reported_emergency_risk_by_amount),
    2770              :     TALER_ARL_GET_AB (coins_emergencies_loss),
    2771              :     TALER_ARL_GET_AB (coins_emergencies_loss_by_count),
    2772              :     NULL);
    2773            4 :   if (0 > qs)
    2774              :   {
    2775            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    2776            0 :     goto cleanup;
    2777              :   }
    2778              :   /* process withdrawals */
    2779            4 :   if (0 >
    2780            4 :       (qs = TALER_EXCHANGEDB_iterate_withdrawals_above_serial_id (
    2781              :          TALER_ARL_edb,
    2782              :          TALER_ARL_USE_PP (coins_withdraw_serial_id),
    2783              :          &withdraw_cb,
    2784              :          &cc)))
    2785              :   {
    2786            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    2787            0 :     goto cleanup;
    2788              :   }
    2789            4 :   if (0 > cc.qs)
    2790              :   {
    2791            0 :     qs = cc.qs;
    2792            0 :     goto cleanup;
    2793              :   }
    2794              :   /* process refreshes */
    2795            4 :   if (0 >
    2796            4 :       (qs = TALER_EXCHANGEDB_iterate_refreshes_above_serial_id (
    2797              :          TALER_ARL_edb,
    2798              :          TALER_ARL_USE_PP (coins_melt_serial_id),
    2799              :          &refresh_session_cb,
    2800              :          &cc)))
    2801              :   {
    2802            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    2803            0 :     goto cleanup;
    2804              :   }
    2805            4 :   if (0 > cc.qs)
    2806              :   {
    2807            0 :     qs = cc.qs;
    2808            0 :     goto cleanup;
    2809              :   }
    2810              :   /* process refunds */
    2811            4 :   if (0 >
    2812            4 :       (qs = TALER_EXCHANGEDB_iterate_refunds_above_serial_id (
    2813              :          TALER_ARL_edb,
    2814              :          TALER_ARL_USE_PP (coins_refund_serial_id),
    2815              :          &refund_cb,
    2816              :          &cc)))
    2817              :   {
    2818            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    2819            0 :     goto cleanup;
    2820              :   }
    2821            4 :   if (0 > cc.qs)
    2822              :   {
    2823            0 :     qs = cc.qs;
    2824            0 :     goto cleanup;
    2825              :   }
    2826              : #if FIXME_9828
    2827              :   /* process recoups */
    2828              :   if (0 >
    2829              :       (qs = TALER_EXCHANGEDB_iterate_recoup_refreshes_above_serial_id (
    2830              :          TALER_ARL_edb,
    2831              :          TALER_ARL_USE_PP (coins_recoup_refresh_serial_id),
    2832              :          &recoup_refresh_cb,
    2833              :          &cc)))
    2834              :   {
    2835              :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    2836              :     goto cleanup;
    2837              :   }
    2838              :   if (0 > cc.qs)
    2839              :   {
    2840              :     qs = cc.qs;
    2841              :     goto cleanup;
    2842              :   }
    2843              : #endif
    2844              :   /* process deposits */
    2845            4 :   if (0 >
    2846            4 :       (qs = TALER_EXCHANGEDB_iterate_coin_deposits_above_serial_id (
    2847              :          TALER_ARL_edb,
    2848              :          TALER_ARL_USE_PP (coins_deposit_serial_id),
    2849              :          &deposit_cb,
    2850              :          &cc)))
    2851              :   {
    2852            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    2853            0 :     goto cleanup;
    2854              :   }
    2855            4 :   if (0 > cc.qs)
    2856              :   {
    2857            0 :     qs = cc.qs;
    2858            0 :     goto cleanup;
    2859              :   }
    2860              :   /* process purse_deposits */
    2861            4 :   if (0 >
    2862            4 :       (qs = TALER_EXCHANGEDB_iterate_purse_deposits_above_serial_id (
    2863              :          TALER_ARL_edb,
    2864              :          TALER_ARL_USE_PP (coins_purse_deposits_serial_id),
    2865              :          &purse_deposit_cb,
    2866              :          &cc)))
    2867              :   {
    2868            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    2869            0 :     goto cleanup;
    2870              :   }
    2871            4 :   if (0 > cc.qs)
    2872              :   {
    2873            0 :     qs = cc.qs;
    2874            0 :     goto cleanup;
    2875              :   }
    2876              :   /* process purse_refunds */
    2877            4 :   if (0 >
    2878            4 :       (qs = TALER_EXCHANGEDB_iterate_purse_decisions_above_serial_id (
    2879              :          TALER_ARL_edb,
    2880              :          TALER_ARL_USE_PP (coins_purse_refunds_serial_id),
    2881              :          true, /* only go for refunds! */
    2882              :          &purse_refund_cb,
    2883              :          &cc)))
    2884              :   {
    2885            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    2886            0 :     goto cleanup;
    2887              :   }
    2888            4 :   if (0 > cc.qs)
    2889              :   {
    2890            0 :     qs = cc.qs;
    2891            0 :     goto cleanup;
    2892              :   }
    2893            4 :   if (0 >
    2894            4 :       (qs = TALER_EXCHANGEDB_iterate_recoups_above_serial_id (
    2895              :          TALER_ARL_edb,
    2896              :          TALER_ARL_USE_PP (coins_recoup_serial_id),
    2897              :          &recoup_cb,
    2898              :          &cc)))
    2899              :   {
    2900            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    2901            0 :     goto cleanup;
    2902              :   }
    2903            4 :   if (0 > cc.qs)
    2904              :   {
    2905            0 :     qs = cc.qs;
    2906            0 :     goto cleanup;
    2907              :   }
    2908              :   /* sync 'cc' back to disk */
    2909            4 :   cc.qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
    2910            4 :   GNUNET_CONTAINER_multihashmap_iterate (cc.denom_summaries,
    2911              :                                          &sync_denomination,
    2912              :                                          &cc);
    2913              : 
    2914            4 :   if (0 > cc.qs)
    2915              :   {
    2916            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == cc.qs);
    2917            0 :     qs = cc.qs;
    2918            0 :     goto cleanup;
    2919              :   }
    2920              : 
    2921            4 :   qs = TALER_AUDITORDB_insert_balance (
    2922              :     TALER_ARL_adb,
    2923              :     TALER_ARL_SET_AB (coin_balance_risk),
    2924              :     TALER_ARL_SET_AB (total_escrowed),
    2925              :     TALER_ARL_SET_AB (coin_irregular_loss),
    2926              :     TALER_ARL_SET_AB (coin_melt_fee_revenue),
    2927              :     TALER_ARL_SET_AB (coin_deposit_fee_revenue),
    2928              :     TALER_ARL_SET_AB (coin_deposit_fee_loss),
    2929              :     TALER_ARL_SET_AB (coin_refund_fee_revenue),
    2930              :     TALER_ARL_SET_AB (total_recoup_loss),
    2931              :     TALER_ARL_SET_AB (coins_total_arithmetic_delta_plus),
    2932              :     TALER_ARL_SET_AB (coins_total_arithmetic_delta_minus),
    2933              :     TALER_ARL_SET_AB (coins_reported_emergency_risk_by_count),
    2934              :     TALER_ARL_SET_AB (coins_reported_emergency_risk_by_amount),
    2935              :     TALER_ARL_SET_AB (coins_emergencies_loss),
    2936              :     TALER_ARL_SET_AB (coins_emergencies_loss_by_count),
    2937              :     NULL);
    2938            4 :   if (0 > qs)
    2939              :   {
    2940            0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    2941              :                 "Failed to update auditor DB, not recording progress\n");
    2942            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    2943            0 :     goto cleanup;
    2944              :   }
    2945              : 
    2946            4 :   qs = TALER_AUDITORDB_insert_auditor_progress (
    2947              :     TALER_ARL_adb,
    2948              :     TALER_ARL_SET_PP (coins_withdraw_serial_id),
    2949              :     TALER_ARL_SET_PP (coins_deposit_serial_id),
    2950              :     TALER_ARL_SET_PP (coins_melt_serial_id),
    2951              :     TALER_ARL_SET_PP (coins_refund_serial_id),
    2952              :     TALER_ARL_SET_PP (coins_recoup_serial_id),
    2953              :     TALER_ARL_SET_PP (coins_recoup_refresh_serial_id),
    2954              :     TALER_ARL_SET_PP (coins_purse_deposits_serial_id),
    2955              :     TALER_ARL_SET_PP (coins_purse_refunds_serial_id),
    2956              :     NULL);
    2957            4 :   if (0 > qs)
    2958              :   {
    2959            0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    2960              :                 "Failed to update auditor DB, not recording progress\n");
    2961            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    2962            0 :     goto cleanup;
    2963              :   }
    2964              : 
    2965            4 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    2966              :               "Concluded coin audit step at %llu/%llu/%llu/%llu/%llu/%llu/%llu/%llu\n",
    2967              :               (unsigned long long) TALER_ARL_USE_PP (coins_withdraw_serial_id),
    2968              :               (unsigned long long) TALER_ARL_USE_PP (coins_deposit_serial_id),
    2969              :               (unsigned long long) TALER_ARL_USE_PP (coins_melt_serial_id),
    2970              :               (unsigned long long) TALER_ARL_USE_PP (coins_refund_serial_id),
    2971              :               (unsigned long long) TALER_ARL_USE_PP (coins_recoup_serial_id),
    2972              :               (unsigned long long) TALER_ARL_USE_PP (
    2973              :                 coins_recoup_refresh_serial_id),
    2974              :               (unsigned long long) TALER_ARL_USE_PP (
    2975              :                 coins_purse_deposits_serial_id),
    2976              :               (unsigned long long) TALER_ARL_USE_PP (
    2977              :                 coins_purse_refunds_serial_id));
    2978            4 :   qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
    2979            4 : cleanup:
    2980            4 :   GNUNET_CONTAINER_multihashmap_iterate (cc.denom_summaries,
    2981              :                                          &cleanup_denomination,
    2982              :                                          &cc);
    2983            4 :   GNUNET_CONTAINER_multihashmap_destroy (cc.denom_summaries);
    2984            4 :   return qs;
    2985              : }
    2986              : 
    2987              : 
    2988              : /**
    2989              :  * Function called on events received from Postgres.
    2990              :  *
    2991              :  * @param cls closure, NULL
    2992              :  * @param extra additional event data provided
    2993              :  * @param extra_size number of bytes in @a extra
    2994              :  */
    2995              : static void
    2996            0 : db_notify (void *cls,
    2997              :            const void *extra,
    2998              :            size_t extra_size)
    2999              : {
    3000              :   (void) cls;
    3001              :   (void) extra;
    3002              :   (void) extra_size;
    3003            0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    3004              :               "Received notification to wake coins helper\n");
    3005            0 :   if (GNUNET_OK !=
    3006            0 :       TALER_ARL_setup_sessions_and_run (&analyze_coins,
    3007              :                                         NULL))
    3008              :   {
    3009            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    3010              :                 "Audit failed\n");
    3011            0 :     GNUNET_SCHEDULER_shutdown ();
    3012            0 :     global_ret = EXIT_FAILURE;
    3013            0 :     return;
    3014              :   }
    3015              : }
    3016              : 
    3017              : 
    3018              : /**
    3019              :  * Function called on shutdown.
    3020              :  */
    3021              : static void
    3022            4 : do_shutdown (void *cls)
    3023              : {
    3024              :   (void) cls;
    3025            4 :   if (NULL != eh)
    3026              :   {
    3027            4 :     TALER_AUDITORDB_event_listen_cancel (eh);
    3028            4 :     eh = NULL;
    3029              :   }
    3030            4 :   TALER_ARL_done ();
    3031            4 : }
    3032              : 
    3033              : 
    3034              : /**
    3035              :  * Main function that will be run.
    3036              :  *
    3037              :  * @param cls closure
    3038              :  * @param args remaining command-line arguments
    3039              :  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
    3040              :  * @param c configuration
    3041              :  */
    3042              : static void
    3043            4 : run (void *cls,
    3044              :      char *const *args,
    3045              :      const char *cfgfile,
    3046              :      const struct GNUNET_CONFIGURATION_Handle *c)
    3047              : {
    3048              :   (void) cls;
    3049              :   (void) args;
    3050              :   (void) cfgfile;
    3051            4 :   cfg = c;
    3052            4 :   GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
    3053              :                                  NULL);
    3054            4 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    3055              :               "Launching coins auditor\n");
    3056            4 :   if (EXIT_SUCCESS !=
    3057            4 :       (global_ret = TALER_ARL_init (c)))
    3058              :   {
    3059            0 :     return;
    3060              :   }
    3061            4 :   if (test_mode != 1)
    3062              :   {
    3063            4 :     struct GNUNET_DB_EventHeaderP es = {
    3064            4 :       .size = htons (sizeof (es)),
    3065            4 :       .type = htons (TALER_DBEVENT_EXCHANGE_AUDITOR_WAKE_HELPER_COINS)
    3066              :     };
    3067              : 
    3068            4 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    3069              :                 "Running helper indefinitely\n");
    3070            4 :     eh = TALER_AUDITORDB_event_listen (TALER_ARL_adb,
    3071              :                                        &es,
    3072            4 :                                        GNUNET_TIME_UNIT_FOREVER_REL,
    3073              :                                        &db_notify,
    3074              :                                        NULL);
    3075              :   }
    3076            4 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    3077              :               "Starting audit\n");
    3078            4 :   if (GNUNET_OK !=
    3079            4 :       TALER_ARL_setup_sessions_and_run (&analyze_coins,
    3080              :                                         NULL))
    3081              :   {
    3082            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    3083              :                 "Audit failed\n");
    3084            0 :     GNUNET_SCHEDULER_shutdown ();
    3085            0 :     global_ret = EXIT_FAILURE;
    3086            0 :     return;
    3087              :   }
    3088              : }
    3089              : 
    3090              : 
    3091              : /**
    3092              :  * The main function to audit operations on coins.
    3093              :  *
    3094              :  * @param argc number of arguments from the command line
    3095              :  * @param argv command line arguments
    3096              :  * @return 0 ok, 1 on error
    3097              :  */
    3098              : int
    3099            4 : main (int argc,
    3100              :       char *const *argv)
    3101              : {
    3102            4 :   const struct GNUNET_GETOPT_CommandLineOption options[] = {
    3103            4 :     GNUNET_GETOPT_option_flag ('i',
    3104              :                                "internal",
    3105              :                                "perform checks only applicable for exchange-internal audits",
    3106              :                                &internal_checks),
    3107            4 :     GNUNET_GETOPT_option_flag ('t',
    3108              :                                "test",
    3109              :                                "run in test mode and exit when idle",
    3110              :                                &test_mode),
    3111            4 :     GNUNET_GETOPT_option_timetravel ('T',
    3112              :                                      "timetravel"),
    3113              :     GNUNET_GETOPT_OPTION_END
    3114              :   };
    3115              :   enum GNUNET_GenericReturnValue ret;
    3116              : 
    3117            4 :   ret = GNUNET_PROGRAM_run (
    3118              :     TALER_AUDITOR_project_data (),
    3119              :     argc,
    3120              :     argv,
    3121              :     "taler-helper-auditor-coins",
    3122              :     gettext_noop ("Audit Taler coin processing"),
    3123              :     options,
    3124              :     &run,
    3125              :     NULL);
    3126            4 :   if (GNUNET_SYSERR == ret)
    3127            0 :     return EXIT_NOTCONFIGURED;
    3128            4 :   if (GNUNET_NO == ret)
    3129            0 :     return EXIT_SUCCESS;
    3130            4 :   return global_ret;
    3131              : }
    3132              : 
    3133              : 
    3134              : /* end of taler-helper-auditor-coins.c */
        

Generated by: LCOV version 2.0-1