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 (108595B)


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