LCOV - code coverage report
Current view: top level - auditor - taler-helper-auditor-coins.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 12.4 % 840 104
Test Date: 2025-12-28 14:06:02 Functions: 17.2 % 29 5

            Line data    Source code
       1              : /*
       2              :   This file is part of TALER
       3              :   Copyright (C) 2016-2025 Taler Systems SA
       4              : 
       5              :   TALER is free software; you can redistribute it and/or modify it under the
       6              :   terms of the GNU Affero Public License as published by the Free Software
       7              :   Foundation; either version 3, or (at your option) any later version.
       8              : 
       9              :   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
      10              :   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
      11              :   A PARTICULAR PURPOSE.  See the GNU Affero Public License for more details.
      12              : 
      13              :   You should have received a copy of the GNU Affero Public License along with
      14              :   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
      15              : */
      16              : /**
      17              :  * @file auditor/taler-helper-auditor-coins.c
      18              :  * @brief audits coins in an exchange database.
      19              :  * @author Christian Grothoff
      20              :  */
      21              : #include "taler/platform.h"
      22              : #include "taler/taler_auditordb_plugin.h"
      23              : #include "report-lib.h"
      24              : #include "taler/taler_dbevents.h"
      25              : #include "taler/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            0 : coin_history_index (const struct TALER_CoinSpendPublicKeyP *coin_pub)
     150              : {
     151              :   uint32_t i;
     152              : 
     153            0 :   GNUNET_memcpy (&i,
     154              :                  coin_pub,
     155              :                  sizeof (i));
     156            0 :   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            0 : cache_history (const struct TALER_CoinSpendPublicKeyP *coin_pub,
     168              :                struct TALER_EXCHANGEDB_TransactionList *tl)
     169              : {
     170            0 :   unsigned int i = coin_history_index (coin_pub);
     171              : 
     172            0 :   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            0 :   coin_histories[i].coin_pub = *coin_pub;
     176            0 :   coin_histories[i].tl = tl;
     177            0 : }
     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            0 : get_cached_history (const struct TALER_CoinSpendPublicKeyP *coin_pub)
     188              : {
     189            0 :   unsigned int i = coin_history_index (coin_pub);
     190              : 
     191            0 :   if (0 ==
     192            0 :       GNUNET_memcmp (coin_pub,
     193              :                      &coin_histories[i].coin_pub))
     194              :   {
     195            0 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     196              :                 "Found verification of %s in cache\n",
     197              :                 TALER_B2S (coin_pub));
     198            0 :     return coin_histories[i].tl;
     199              :   }
     200            0 :   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            0 : 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            0 :   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            0 :   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            0 :   qs = TALER_ARL_adb->insert_emergency (
     241            0 :     TALER_ARL_adb->cls,
     242              :     &emergency);
     243            0 :   if (qs < 0)
     244              :   {
     245            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     246            0 :     return qs;
     247              :   }
     248            0 :   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            0 :   TALER_ARL_amount_add (&TALER_ARL_USE_AB (coins_emergencies_loss),
     254              :                         &TALER_ARL_USE_AB (coins_emergencies_loss),
     255              :                         loss);
     256            0 :   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            0 : 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            0 :   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            0 :   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            0 :   qs = TALER_ARL_adb->insert_emergency_by_count (
     300            0 :     TALER_ARL_adb->cls,
     301              :     &emergenciesByCount);
     302              : 
     303            0 :   if (qs < 0)
     304              :   {
     305            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     306            0 :     return qs;
     307              :   }
     308            0 :   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            0 :   for (uint64_t i = num_issued; i < num_known; i++)
     314            0 :     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            0 :   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            0 : 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            0 :   if (0 < TALER_amount_cmp (exchange,
     349              :                             auditor))
     350              :   {
     351              :     /* exchange > auditor */
     352            0 :     TALER_ARL_amount_subtract (&delta,
     353              :                                exchange,
     354              :                                auditor);
     355              :   }
     356              :   else
     357              :   {
     358              :     /* auditor < exchange */
     359            0 :     profitable = -profitable;
     360            0 :     TALER_ARL_amount_subtract (&delta,
     361              :                                auditor,
     362              :                                exchange);
     363              :   }
     364              : 
     365              :   {
     366            0 :     struct TALER_AUDITORDB_AmountArithmeticInconsistency aai = {
     367            0 :       .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            0 :     qs = TALER_ARL_adb->insert_amount_arithmetic_inconsistency (
     376            0 :       TALER_ARL_adb->cls,
     377              :       &aai);
     378            0 :     if (qs < 0)
     379              :     {
     380            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     381            0 :       return qs;
     382              :     }
     383              :   }
     384            0 :   if (0 != profitable)
     385              :   {
     386            0 :     target = (1 == profitable)
     387              :       ? &TALER_ARL_USE_AB (coins_total_arithmetic_delta_plus)
     388            0 :       : &TALER_ARL_USE_AB (coins_total_arithmetic_delta_minus);
     389            0 :     TALER_ARL_amount_add (target,
     390              :                           target,
     391              :                           &delta);
     392              :   }
     393            0 :   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            0 : 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            0 :   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            0 :     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            0 :   if (0 > qs)
     476            0 :     return qs;
     477            0 :   GNUNET_assert (GNUNET_OK ==
     478              :                  TALER_amount_set_zero (value->currency,
     479              :                                         &refunded));
     480            0 :   GNUNET_assert (GNUNET_OK ==
     481              :                  TALER_amount_set_zero (value->currency,
     482              :                                         &spent));
     483            0 :   GNUNET_assert (GNUNET_OK ==
     484              :                  TALER_amount_set_zero (value->currency,
     485              :                                         &deposit_fee));
     486            0 :   have_refund = false;
     487            0 :   for (struct TALER_EXCHANGEDB_TransactionList *pos = tl;
     488            0 :        NULL != pos;
     489            0 :        pos = pos->next)
     490              :   {
     491            0 :     switch (pos->type)
     492              :     {
     493            0 :     case TALER_EXCHANGEDB_TT_DEPOSIT:
     494              :       /* spent += pos->amount_with_fee */
     495            0 :       TALER_ARL_amount_add (&spent,
     496              :                             &spent,
     497              :                             &pos->details.deposit->amount_with_fee);
     498            0 :       deposit_fee = pos->details.deposit->deposit_fee;
     499            0 :       break;
     500            0 :     case TALER_EXCHANGEDB_TT_MELT:
     501              :       /* spent += pos->amount_with_fee */
     502            0 :       TALER_ARL_amount_add (&spent,
     503              :                             &spent,
     504              :                             &pos->details.melt->amount_with_fee);
     505            0 :       break;
     506            0 :     case TALER_EXCHANGEDB_TT_REFUND:
     507              :       /* refunded += pos->refund_amount - pos->refund_fee */
     508            0 :       TALER_ARL_amount_add (&refunded,
     509              :                             &refunded,
     510              :                             &pos->details.refund->refund_amount);
     511            0 :       TALER_ARL_amount_add (&spent,
     512              :                             &spent,
     513              :                             &pos->details.refund->refund_fee);
     514            0 :       have_refund = true;
     515            0 :       break;
     516            0 :     case TALER_EXCHANGEDB_TT_RECOUP_REFRESH_RECEIVER:
     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_WITHDRAW:
     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            0 :   if (have_refund)
     557              :   {
     558              :     /* If we gave any refund, also discount ONE deposit fee */
     559            0 :     TALER_ARL_amount_add (&refunded,
     560              :                           &refunded,
     561              :                           &deposit_fee);
     562              :   }
     563              :   /* total coin value = original value plus refunds */
     564            0 :   TALER_ARL_amount_add (&total,
     565              :                         &refunded,
     566              :                         value);
     567            0 :   if (1 ==
     568            0 :       TALER_amount_cmp (&spent,
     569              :                         &total))
     570              :   {
     571              :     /* spent > total: bad */
     572              :     struct TALER_Amount loss;
     573              : 
     574            0 :     TALER_ARL_amount_subtract (&loss,
     575              :                                &spent,
     576              :                                &total);
     577            0 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     578              :                 "Loss detected for coin %s - %s\n",
     579              :                 TALER_B2S (coin_pub),
     580              :                 TALER_amount2s (&loss));
     581            0 :     qs = report_amount_arithmetic_inconsistency (operation,
     582              :                                                  rowid,
     583              :                                                  &spent,
     584              :                                                  &total,
     585              :                                                  -1);
     586            0 :     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            0 :   cache_history (coin_pub,
     595              :                  tl);
     596            0 :   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            0 : 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            0 :   qs = TALER_ARL_adb->get_denomination_balance (TALER_ARL_adb->cls,
     676              :                                                 denom_hash,
     677              :                                                 &ds->dcd);
     678            0 :   if (0 > qs)
     679              :   {
     680            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     681            0 :     return qs;
     682              :   }
     683            0 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
     684              :   {
     685            0 :     ds->in_db = true;
     686              :   }
     687              :   else
     688              :   {
     689            0 :     GNUNET_assert (GNUNET_OK ==
     690              :                    TALER_amount_set_zero (TALER_ARL_currency,
     691              :                                           &ds->dcd.denom_balance));
     692            0 :     GNUNET_assert (GNUNET_OK ==
     693              :                    TALER_amount_set_zero (TALER_ARL_currency,
     694              :                                           &ds->dcd.denom_loss));
     695            0 :     GNUNET_assert (GNUNET_OK ==
     696              :                    TALER_amount_set_zero (TALER_ARL_currency,
     697              :                                           &ds->dcd.denom_risk));
     698            0 :     GNUNET_assert (GNUNET_OK ==
     699              :                    TALER_amount_set_zero (TALER_ARL_currency,
     700              :                                           &ds->dcd.recoup_loss));
     701              :   }
     702            0 :   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            0 :   qs = TALER_ARL_edb->get_denomination_revocation (TALER_ARL_edb->cls,
     708              :                                                    denom_hash,
     709              :                                                    &msig,
     710              :                                                    &rowid);
     711            0 :   if (0 > qs)
     712              :   {
     713            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     714            0 :     return qs;
     715              :   }
     716            0 :   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            0 :   return ds->in_db
     740              :     ? GNUNET_DB_STATUS_SUCCESS_ONE_RESULT
     741            0 :     : 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            0 : get_denomination_summary (
     754              :   struct CoinContext *cc,
     755              :   const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue)
     756              : {
     757              :   struct DenominationSummary *ds;
     758            0 :   const struct TALER_DenominationHashP *dh = &issue->denom_hash;
     759              : 
     760            0 :   ds = GNUNET_CONTAINER_multihashmap_get (cc->denom_summaries,
     761              :                                           &dh->hash);
     762            0 :   if (NULL != ds)
     763            0 :     return ds;
     764            0 :   ds = GNUNET_new (struct DenominationSummary);
     765            0 :   ds->issue = issue;
     766            0 :   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            0 :   GNUNET_assert (GNUNET_OK ==
     774              :                  GNUNET_CONTAINER_multihashmap_put (cc->denom_summaries,
     775              :                                                     &dh->hash,
     776              :                                                     ds,
     777              :                                                     GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
     778            0 :   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            0 : sync_denomination (void *cls,
     795              :                    const struct GNUNET_HashCode *denom_hash,
     796              :                    void *value)
     797              : {
     798            0 :   struct CoinContext *cc = cls;
     799            0 :   struct TALER_DenominationHashP denom_h = {
     800              :     .hash = *denom_hash
     801              :   };
     802            0 :   struct DenominationSummary *ds = value;
     803            0 :   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            0 :   now = GNUNET_TIME_absolute_get ();
     810            0 :   expire_deposit = issue->expire_deposit;
     811              :   /* add day grace period to deal with clocks not being perfectly synchronized */
     812            0 :   expire_deposit_grace = GNUNET_TIME_absolute_add (expire_deposit.abs_time,
     813              :                                                    DEPOSIT_GRACE_PERIOD);
     814            0 :   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            0 :     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            0 :     cnt = TALER_ARL_edb->count_known_coins (TALER_ARL_edb->cls,
     881              :                                             &denom_h);
     882            0 :     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            0 :     if (ds->dcd.num_issued < (uint64_t) cnt)
     891              :     {
     892              :       /* more coins deposited than issued! very bad */
     893            0 :       qs = report_emergency_by_count (issue,
     894              :                                       ds->dcd.num_issued,
     895              :                                       cnt,
     896            0 :                                       &ds->dcd.denom_risk);
     897            0 :       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            0 :     if (ds->report_emergency)
     905              :     {
     906              :       /* Value of coins deposited exceed value of coins
     907              :          issued! Also very bad! */
     908            0 :       qs = report_emergency_by_amount (issue,
     909            0 :                                        &ds->dcd.denom_risk,
     910            0 :                                        &ds->dcd.denom_loss);
     911            0 :       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            0 :     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            0 :       qs = TALER_ARL_adb->insert_denomination_balance (TALER_ARL_adb->cls,
     924              :                                                        &denom_h,
     925            0 :                                                        &ds->dcd);
     926              : 
     927            0 :     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            0 :   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            0 : cleanup_denomination (void *cls,
     949              :                       const struct GNUNET_HashCode *denom_hash,
     950              :                       void *value)
     951              : {
     952            0 :   struct CoinContext *cc = cls;
     953            0 :   struct DenominationSummary *ds = value;
     954              : 
     955            0 :   GNUNET_assert (GNUNET_YES ==
     956              :                  GNUNET_CONTAINER_multihashmap_remove (cc->denom_summaries,
     957              :                                                        denom_hash,
     958              :                                                        ds));
     959            0 :   GNUNET_free (ds);
     960            0 :   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            0 : 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            0 :   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            0 :   GNUNET_assert (rowid >=
    1018              :                  TALER_ARL_USE_PP (coins_withdraw_serial_id)); /* should be monotonically increasing */
    1019            0 :   TALER_ARL_USE_PP (coins_withdraw_serial_id) = rowid + 1;
    1020              : 
    1021            0 :   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            0 :     qs = TALER_ARL_get_denomination_info_by_serial (denom_serials[i],
    1028              :                                                     &issue);
    1029            0 :     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            0 :     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            0 :     ds = get_denomination_summary (cc,
    1049              :                                    issue);
    1050            0 :     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            0 :     ds->dcd.num_issued++;
    1057            0 :     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            0 :     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            0 :     TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_escrowed),
    1066              :                           &TALER_ARL_USE_AB (total_escrowed),
    1067              :                           &issue->value);
    1068            0 :     TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_balance_risk),
    1069              :                           &TALER_ARL_USE_AB (coin_balance_risk),
    1070              :                           &issue->value);
    1071            0 :     TALER_ARL_amount_add (&ds->dcd.denom_balance,
    1072              :                           &ds->dcd.denom_balance,
    1073              :                           &issue->value);
    1074            0 :     TALER_ARL_amount_add (&ds->dcd.denom_risk,
    1075              :                           &ds->dcd.denom_risk,
    1076              :                           &issue->value);
    1077              :   }
    1078            0 :   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            0 : 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            0 :   if (NULL == get_cached_history (coin_pub))
    1110              :   {
    1111            0 :     qs = check_coin_history (coin_pub,
    1112              :                              rowid,
    1113              :                              operation,
    1114              :                              &issue->value);
    1115            0 :     if (0 > qs)
    1116              :     {
    1117            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1118            0 :       return qs;
    1119              :     }
    1120            0 :     GNUNET_break (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != qs);
    1121              :   }
    1122              : 
    1123            0 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1124              :               "Checking denomination signature on %s\n",
    1125              :               TALER_B2S (coin_pub));
    1126            0 :   qs = TALER_ARL_edb->get_known_coin (TALER_ARL_edb->cls,
    1127              :                                       coin_pub,
    1128              :                                       &ci);
    1129            0 :   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            0 :   if (GNUNET_YES !=
    1135            0 :       TALER_test_coin_valid (&ci,
    1136              :                              denom_pub))
    1137              :   {
    1138            0 :     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            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    1146              :                 "Failed to verify coin denomination signature in row %llu\n",
    1147              :                 (unsigned long long) rowid);
    1148            0 :     qs = TALER_ARL_adb->insert_bad_sig_losses (
    1149            0 :       TALER_ARL_adb->cls,
    1150              :       &bsl);
    1151            0 :     if (qs < 0)
    1152              :     {
    1153            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1154            0 :       return qs;
    1155              :     }
    1156            0 :     TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_irregular_loss),
    1157              :                           &TALER_ARL_USE_AB (coin_irregular_loss),
    1158              :                           loss_potential);
    1159              :   }
    1160            0 :   TALER_denom_sig_free (&ci.denom_sig);
    1161            0 :   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            0 : 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            0 :   if (TALER_ARL_SR_INVALID_NEGATIVE ==
    1184            0 :       TALER_ARL_amount_subtract_neg (&tmp,
    1185              :                                      &dso->dcd.denom_balance,
    1186              :                                      amount_with_fee))
    1187              :   {
    1188            0 :     TALER_ARL_amount_add (&dso->dcd.denom_loss,
    1189              :                           &dso->dcd.denom_loss,
    1190              :                           amount_with_fee);
    1191            0 :     dso->report_emergency = true;
    1192              :   }
    1193              :   else
    1194              :   {
    1195            0 :     dso->dcd.denom_balance = tmp;
    1196              :   }
    1197            0 :   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            0 :     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            0 :     if (0 > qs)
    1213              :     {
    1214            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1215            0 :       return qs;
    1216              :     }
    1217              :   }
    1218              :   else
    1219              :   {
    1220            0 :     TALER_ARL_amount_subtract (&TALER_ARL_USE_AB (total_escrowed),
    1221              :                                &TALER_ARL_USE_AB (total_escrowed),
    1222              :                                amount_with_fee);
    1223              :   }
    1224            0 :   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            0 :   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            0 : 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_AgeCommitmentHashP *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            0 : {
    1263            0 :   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            0 :   GNUNET_assert (rowid >=
    1270              :                  TALER_ARL_USE_PP (coins_melt_serial_id)); /* should be monotonically increasing */
    1271            0 :   TALER_ARL_USE_PP (coins_melt_serial_id) = rowid + 1;
    1272            0 :   qs = TALER_ARL_get_denomination_info (old_denom_pub,
    1273              :                                         &issue,
    1274              :                                         &h_denom_pub);
    1275            0 :   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            0 :   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            0 :   qs = check_known_coin ("melt",
    1295              :                          issue,
    1296              :                          rowid,
    1297              :                          coin_pub,
    1298              :                          old_denom_pub,
    1299              :                          amount_with_fee);
    1300            0 :   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            0 :   if (GNUNET_OK !=
    1309            0 :       TALER_wallet_melt_verify (amount_with_fee,
    1310            0 :                                 &issue->fees.refresh,
    1311              :                                 rc,
    1312              :                                 &h_denom_pub,
    1313              :                                 h_age_commitment,
    1314              :                                 coin_pub,
    1315              :                                 coin_sig))
    1316              :   {
    1317            0 :     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            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    1325              :                 "Failed to verify coin melt signature in row %llu\n",
    1326              :                 (unsigned long long) rowid);
    1327            0 :     qs = TALER_ARL_adb->insert_bad_sig_losses (
    1328            0 :       TALER_ARL_adb->cls,
    1329              :       &bsl);
    1330            0 :     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            0 :     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            0 :   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            0 :   {
    1347              :     struct TALER_Amount refresh_cost;
    1348              :     struct TALER_Amount amount_without_fee;
    1349            0 :     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            0 :     GNUNET_assert (GNUNET_OK ==
    1354              :                    TALER_amount_set_zero (amount_with_fee->currency,
    1355              :                                           &refresh_cost));
    1356            0 :     for (size_t i = 0; i < num_nds; i++)
    1357              :     {
    1358            0 :       qs = TALER_ARL_get_denomination_info_by_serial (new_denom_serials[i],
    1359              :                                                       &nis[i]);
    1360            0 :       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            0 :       TALER_ARL_amount_add (&refresh_cost,
    1368              :                             &refresh_cost,
    1369              :                             &nis[i]->fees.withdraw);
    1370            0 :       TALER_ARL_amount_add (&refresh_cost,
    1371              :                             &refresh_cost,
    1372              :                             &nis[i]->value);
    1373              :     }
    1374              : 
    1375              :     /* compute contribution of old coin */
    1376            0 :     if (TALER_ARL_SR_POSITIVE !=
    1377            0 :         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            0 :     if (1 == TALER_amount_cmp (&refresh_cost,
    1403              :                                &amount_without_fee))
    1404              :     {
    1405              :       /* refresh_cost > amount_without_fee, which is bad (exchange lost) */
    1406            0 :       GNUNET_break_op (0);
    1407            0 :       qs = report_amount_arithmetic_inconsistency ("melt (cost)",
    1408              :                                                    rowid,
    1409              :                                                    &amount_without_fee, /* 'exchange' */
    1410              :                                                    &refresh_cost, /* 'auditor' */
    1411              :                                                    1);
    1412            0 :       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            0 :     for (size_t i = 0; i < num_nds; i++)
    1422              :     {
    1423            0 :       const struct TALER_EXCHANGEDB_DenominationKeyInformation *ni
    1424              :         = nis[i];
    1425              :       struct DenominationSummary *dsi;
    1426              : 
    1427            0 :       dsi = get_denomination_summary (cc,
    1428              :                                       ni);
    1429            0 :       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            0 :         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            0 :         dsi->dcd.num_issued++;
    1448            0 :         TALER_ARL_amount_add (&dsi->dcd.denom_balance,
    1449              :                               &dsi->dcd.denom_balance,
    1450              :                               &ni->value);
    1451            0 :         TALER_ARL_amount_add (&dsi->dcd.denom_risk,
    1452              :                               &dsi->dcd.denom_risk,
    1453              :                               &ni->value);
    1454            0 :         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            0 :         TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_escrowed),
    1459              :                               &TALER_ARL_USE_AB (total_escrowed),
    1460              :                               &ni->value);
    1461            0 :         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            0 :   dso = get_denomination_summary (cc,
    1470              :                                   issue);
    1471            0 :   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            0 :     qs = reduce_denom_balance (dso,
    1486              :                                rowid,
    1487              :                                amount_with_fee);
    1488            0 :     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            0 :   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            0 :   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            0 : 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            0 :   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            0 :   GNUNET_assert (rowid >=
    1532              :                  TALER_ARL_USE_PP (coins_deposit_serial_id)); /* should be monotonically increasing */
    1533            0 :   TALER_ARL_USE_PP (coins_deposit_serial_id) = rowid + 1;
    1534              : 
    1535            0 :   qs = TALER_ARL_get_denomination_info (denom_pub,
    1536              :                                         &issue,
    1537              :                                         NULL);
    1538            0 :   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            0 :   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            0 :   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            0 :   qs = check_known_coin ("deposit",
    1573              :                          issue,
    1574              :                          rowid,
    1575              :                          &deposit->coin.coin_pub,
    1576              :                          denom_pub,
    1577              :                          &deposit->amount_with_fee);
    1578            0 :   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            0 :     TALER_denom_pub_hash (denom_pub,
    1591              :                           &h_denom_pub);
    1592            0 :     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            0 :     if (GNUNET_OK !=
    1599            0 :         TALER_wallet_deposit_verify (&deposit->amount_with_fee,
    1600            0 :                                      &issue->fees.deposit,
    1601              :                                      &h_wire,
    1602              :                                      &deposit->h_contract_terms,
    1603            0 :                                      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            0 :       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            0 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    1623              :                   "Failed to verify coin deposit signature in row %llu\n",
    1624              :                   (unsigned long long) rowid);
    1625            0 :       qs = TALER_ARL_adb->insert_bad_sig_losses (
    1626            0 :         TALER_ARL_adb->cls,
    1627              :         &bsl);
    1628            0 :       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            0 :       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            0 :       return GNUNET_OK;
    1638              :     }
    1639              :   }
    1640            0 :   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            0 :   ds = get_denomination_summary (cc,
    1648              :                                  issue);
    1649            0 :   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            0 :     qs = reduce_denom_balance (ds,
    1664              :                                rowid,
    1665              :                                &deposit->amount_with_fee);
    1666            0 :     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            0 :   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            0 :   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            0 : 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            0 :   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            0 :   GNUNET_assert (rowid >= TALER_ARL_USE_PP (coins_refund_serial_id)); /* should be monotonically increasing */
    1719            0 :   TALER_ARL_USE_PP (coins_refund_serial_id) = rowid + 1;
    1720              : 
    1721            0 :   qs = TALER_ARL_get_denomination_info (denom_pub,
    1722              :                                         &issue,
    1723              :                                         NULL);
    1724            0 :   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            0 :   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            0 :   if (GNUNET_OK !=
    1746            0 :       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            0 :   if (TALER_ARL_SR_INVALID_NEGATIVE ==
    1779            0 :       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            0 :   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            0 :   ds = get_denomination_summary (cc,
    1805              :                                  issue);
    1806            0 :   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            0 :     TALER_ARL_amount_add (&ds->dcd.denom_balance,
    1821              :                           &ds->dcd.denom_balance,
    1822              :                           &amount_without_fee);
    1823            0 :     TALER_ARL_amount_add (&ds->dcd.denom_risk,
    1824              :                           &ds->dcd.denom_risk,
    1825              :                           &amount_without_fee);
    1826            0 :     TALER_ARL_amount_add (&TALER_ARL_USE_AB (total_escrowed),
    1827              :                           &TALER_ARL_USE_AB (total_escrowed),
    1828              :                           &amount_without_fee);
    1829            0 :     TALER_ARL_amount_add (&TALER_ARL_USE_AB (coin_balance_risk),
    1830              :                           &TALER_ARL_USE_AB (coin_balance_risk),
    1831              :                           &amount_without_fee);
    1832            0 :     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            0 :   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            0 :   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            0 :   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         2120 : 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         2120 :   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         2120 :   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         2120 :   if (0 > qs)
    2383              :   {
    2384            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    2385            0 :     *iqs = qs;
    2386            0 :     return;
    2387              :   }
    2388         2120 :   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         2120 :   if (GNUNET_OK !=
    2400         2120 :       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            0 :     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            0 :     qs = TALER_ARL_adb->insert_denominations_without_sigs (
    2421            0 :       TALER_ARL_adb->cls,
    2422              :       &dws);
    2423              : 
    2424            0 :     if (qs < 0)
    2425              :     {
    2426            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    2427            0 :       *iqs = qs;
    2428            0 :       return;
    2429              :     }
    2430              :   }
    2431         2120 :   *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            4 : 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            4 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    2591              :               "Checking denominations...\n");
    2592            4 :   iqs = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
    2593            4 :   qs = TALER_ARL_edb->iterate_denomination_info (TALER_ARL_edb->cls,
    2594              :                                                  &check_denomination,
    2595              :                                                  &iqs);
    2596            4 :   if (0 > qs)
    2597              :   {
    2598            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    2599            0 :     return qs;
    2600              :   }
    2601            4 :   if (0 > iqs)
    2602              :   {
    2603            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    2604            0 :     return qs;
    2605              :   }
    2606            4 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    2607              :               "Analyzing coins\n");
    2608            4 :   qs = TALER_ARL_adb->get_auditor_progress (
    2609            4 :     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            4 :   if (0 > qs)
    2620              :   {
    2621            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    2622            0 :     return qs;
    2623              :   }
    2624            4 :   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            4 :     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            4 :   cc.qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
    2652            4 :   cc.denom_summaries = GNUNET_CONTAINER_multihashmap_create (256,
    2653              :                                                              GNUNET_NO);
    2654            4 :   qs = TALER_ARL_adb->get_balance (
    2655            4 :     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            4 :   if (0 > qs)
    2672              :   {
    2673            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    2674            0 :     goto cleanup;
    2675              :   }
    2676              :   /* process withdrawals */
    2677            4 :   if (0 >
    2678            4 :       (qs = TALER_ARL_edb->select_withdrawals_above_serial_id (
    2679            4 :          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            4 :   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            4 :   if (0 >
    2712            4 :       (qs = TALER_ARL_edb->select_coin_deposits_above_serial_id (
    2713            4 :          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            4 :   if (0 > cc.qs)
    2722              :   {
    2723            0 :     qs = cc.qs;
    2724            0 :     goto cleanup;
    2725              :   }
    2726              :   /* process purse_deposits */
    2727            4 :   if (0 >
    2728            4 :       (qs = TALER_ARL_edb->select_purse_deposits_above_serial_id (
    2729            4 :          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            4 :   if (0 > cc.qs)
    2738              :   {
    2739            0 :     qs = cc.qs;
    2740            0 :     goto cleanup;
    2741              :   }
    2742              :   /* process refunds */
    2743            4 :   if (0 >
    2744            4 :       (qs = TALER_ARL_edb->select_refunds_above_serial_id (
    2745            4 :          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            4 :   if (0 > cc.qs)
    2754              :   {
    2755            0 :     qs = cc.qs;
    2756            0 :     goto cleanup;
    2757              :   }
    2758              :   /* process purse_refunds */
    2759            4 :   if (0 >
    2760            4 :       (qs = TALER_ARL_edb->select_purse_decisions_above_serial_id (
    2761            4 :          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            4 :   if (0 > cc.qs)
    2771              :   {
    2772            0 :     qs = cc.qs;
    2773            0 :     goto cleanup;
    2774              :   }
    2775              :   /* process refreshes */
    2776            4 :   if (0 >
    2777            4 :       (qs = TALER_ARL_edb->select_refreshes_above_serial_id (
    2778            4 :          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            4 :   if (0 > cc.qs)
    2787              :   {
    2788            0 :     qs = cc.qs;
    2789            0 :     goto cleanup;
    2790              :   }
    2791            4 :   if (0 >
    2792            4 :       (qs = TALER_ARL_edb->select_recoup_above_serial_id (
    2793            4 :          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            4 :   if (0 > cc.qs)
    2802              :   {
    2803            0 :     qs = cc.qs;
    2804            0 :     goto cleanup;
    2805              :   }
    2806              :   /* sync 'cc' back to disk */
    2807            4 :   cc.qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
    2808            4 :   GNUNET_CONTAINER_multihashmap_iterate (cc.denom_summaries,
    2809              :                                          &sync_denomination,
    2810              :                                          &cc);
    2811              : 
    2812            4 :   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            4 :   qs = TALER_ARL_adb->insert_balance (
    2820            4 :     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            4 :   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            4 :   qs = TALER_ARL_adb->update_balance (
    2845            4 :     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            4 :   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            4 :   qs = TALER_ARL_adb->insert_auditor_progress (
    2870            4 :     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            4 :   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            4 :   qs = TALER_ARL_adb->update_auditor_progress (
    2889            4 :     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            4 :   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            4 :   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            4 :   qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
    2920            4 : cleanup:
    2921            4 :   GNUNET_CONTAINER_multihashmap_iterate (cc.denom_summaries,
    2922              :                                          &cleanup_denomination,
    2923              :                                          &cc);
    2924            4 :   GNUNET_CONTAINER_multihashmap_destroy (cc.denom_summaries);
    2925            4 :   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            4 : do_shutdown (void *cls)
    2964              : {
    2965              :   (void) cls;
    2966            4 :   if (NULL != eh)
    2967              :   {
    2968            4 :     TALER_ARL_adb->event_listen_cancel (eh);
    2969            4 :     eh = NULL;
    2970              :   }
    2971            4 :   TALER_ARL_done ();
    2972            4 : }
    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            4 : 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            4 :   cfg = c;
    2993            4 :   GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
    2994              :                                  NULL);
    2995            4 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    2996              :               "Launching coins auditor\n");
    2997            4 :   if (GNUNET_OK != TALER_ARL_init (c))
    2998              :   {
    2999            0 :     global_ret = EXIT_FAILURE;
    3000            0 :     return;
    3001              :   }
    3002            4 :   if (test_mode != 1)
    3003              :   {
    3004            4 :     struct GNUNET_DB_EventHeaderP es = {
    3005            4 :       .size = htons (sizeof (es)),
    3006            4 :       .type = htons (TALER_DBEVENT_EXCHANGE_AUDITOR_WAKE_HELPER_COINS)
    3007              :     };
    3008              : 
    3009            4 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    3010              :                 "Running helper indefinitely\n");
    3011            4 :     eh = TALER_ARL_adb->event_listen (TALER_ARL_adb->cls,
    3012              :                                       &es,
    3013            4 :                                       GNUNET_TIME_UNIT_FOREVER_REL,
    3014              :                                       &db_notify,
    3015              :                                       NULL);
    3016              :   }
    3017            4 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    3018              :               "Starting audit\n");
    3019            4 :   if (GNUNET_OK !=
    3020            4 :       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            4 : main (int argc,
    3041              :       char *const *argv)
    3042              : {
    3043            4 :   const struct GNUNET_GETOPT_CommandLineOption options[] = {
    3044            4 :     GNUNET_GETOPT_option_flag ('i',
    3045              :                                "internal",
    3046              :                                "perform checks only applicable for exchange-internal audits",
    3047              :                                &internal_checks),
    3048            4 :     GNUNET_GETOPT_option_flag ('t',
    3049              :                                "test",
    3050              :                                "run in test mode and exit when idle",
    3051              :                                &test_mode),
    3052            4 :     GNUNET_GETOPT_option_timetravel ('T',
    3053              :                                      "timetravel"),
    3054              :     GNUNET_GETOPT_OPTION_END
    3055              :   };
    3056              :   enum GNUNET_GenericReturnValue ret;
    3057              : 
    3058            4 :   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            4 :   if (GNUNET_SYSERR == ret)
    3068            0 :     return EXIT_INVALIDARGUMENT;
    3069            4 :   if (GNUNET_NO == ret)
    3070            0 :     return EXIT_SUCCESS;
    3071            4 :   return global_ret;
    3072              : }
    3073              : 
    3074              : 
    3075              : /* end of taler-helper-auditor-coins.c */
        

Generated by: LCOV version 2.0-1