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


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