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

Generated by: LCOV version 2.0-1