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


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