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


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