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


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