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


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