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


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