LCOV - code coverage report
Current view: top level - auditor - taler-helper-auditor-coins.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 399 839 47.6 %
Date: 2025-06-05 21:03:14 Functions: 22 29 75.9 %

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

Generated by: LCOV version 1.16