exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

taler-helper-auditor-coins.c (121012B)


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