LCOV - code coverage report
Current view: top level - auditor - taler-helper-auditor-reserves.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 302 585 51.6 %
Date: 2025-06-22 12:09:43 Functions: 13 18 72.2 %

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   Copyright (C) 2016-2024 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-reserves.c
      18             :  * @brief audits the reserves of 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             :  * Use a 1 day grace period to deal with clocks not being perfectly synchronized.
      30             :  */
      31             : #define CLOSING_GRACE_PERIOD GNUNET_TIME_UNIT_DAYS
      32             : 
      33             : /**
      34             :  * Return value from main().
      35             :  */
      36             : static int global_ret;
      37             : 
      38             : /**
      39             :  * State of the last database transaction.
      40             :  */
      41             : static enum GNUNET_DB_QueryStatus global_qs;
      42             : 
      43             : /**
      44             :  * Run in test mode. Exit when idle instead of
      45             :  * going to sleep and waiting for more work.
      46             :  */
      47             : static int test_mode;
      48             : 
      49             : /**
      50             :  * After how long should idle reserves be closed?
      51             :  */
      52             : static struct GNUNET_TIME_Relative idle_reserve_expiration_time;
      53             : 
      54             : /**
      55             :  * Checkpointing our progress for reserves.
      56             :  */
      57             : static TALER_ARL_DEF_PP (reserves_reserve_in_serial_id);
      58             : static TALER_ARL_DEF_PP (reserves_withdraw_serial_id);
      59             : static TALER_ARL_DEF_PP (reserves_reserve_recoup_serial_id);
      60             : static TALER_ARL_DEF_PP (reserves_reserve_open_serial_id);
      61             : static TALER_ARL_DEF_PP (reserves_reserve_close_serial_id);
      62             : static TALER_ARL_DEF_PP (reserves_purse_decisions_serial_id);
      63             : static TALER_ARL_DEF_PP (reserves_account_merges_serial_id);
      64             : static TALER_ARL_DEF_PP (reserves_history_requests_serial_id);
      65             : 
      66             : /**
      67             :  * Tracked global reserve balances.
      68             :  */
      69             : static TALER_ARL_DEF_AB (reserves_reserve_total_balance);
      70             : static TALER_ARL_DEF_AB (reserves_reserve_loss);
      71             : static TALER_ARL_DEF_AB (reserves_withdraw_fee_revenue);
      72             : static TALER_ARL_DEF_AB (reserves_close_fee_revenue);
      73             : static TALER_ARL_DEF_AB (reserves_purse_fee_revenue);
      74             : static TALER_ARL_DEF_AB (reserves_open_fee_revenue);
      75             : static TALER_ARL_DEF_AB (reserves_history_fee_revenue);
      76             : 
      77             : /**
      78             :  * Total amount lost by operations for which signatures were invalid.
      79             :  */
      80             : static TALER_ARL_DEF_AB (reserves_total_bad_sig_loss);
      81             : 
      82             : /**
      83             :  * Total amount affected by reserves not having been closed on time.
      84             :  */
      85             : static TALER_ARL_DEF_AB (total_balance_reserve_not_closed);
      86             : 
      87             : /**
      88             :  * Total delta between expected and stored reserve balance summaries,
      89             :  * for positive deltas.  Used only when internal checks are
      90             :  * enabled.
      91             :  */
      92             : static TALER_ARL_DEF_AB (total_balance_summary_delta_plus);
      93             : 
      94             : /**
      95             :  * Total delta between expected and stored reserve balance summaries,
      96             :  * for negative deltas.  Used only when internal checks are
      97             :  * enabled.
      98             :  */
      99             : static TALER_ARL_DEF_AB (total_balance_summary_delta_minus);
     100             : 
     101             : /**
     102             :  * Profits the exchange made by bad amount calculations.
     103             :  */
     104             : static TALER_ARL_DEF_AB (reserves_total_arithmetic_delta_plus);
     105             : 
     106             : /**
     107             :  * Losses the exchange made by bad amount calculations.
     108             :  */
     109             : static TALER_ARL_DEF_AB (reserves_total_arithmetic_delta_minus);
     110             : 
     111             : /**
     112             :  * Should we run checks that only work for exchange-internal audits?
     113             :  */
     114             : static int internal_checks;
     115             : 
     116             : static struct GNUNET_DB_EventHandler *eh;
     117             : 
     118             : /**
     119             :  * The auditors's configuration.
     120             :  */
     121             : static const struct GNUNET_CONFIGURATION_Handle *cfg;
     122             : 
     123             : /* ***************************** Report logic **************************** */
     124             : 
     125             : 
     126             : /**
     127             :  * Report a (serious) inconsistency in the exchange's database with
     128             :  * respect to calculations involving amounts.
     129             :  *
     130             :  * @param operation what operation had the inconsistency
     131             :  * @param rowid affected row, 0 if row is missing
     132             :  * @param exchange amount calculated by exchange
     133             :  * @param auditor amount calculated by auditor
     134             :  * @param profitable 1 if @a exchange being larger than @a auditor is
     135             :  *           profitable for the exchange for this operation,
     136             :  *           -1 if @a exchange being smaller than @a auditor is
     137             :  *           profitable for the exchange, and 0 if it is unclear
     138             :  */
     139             : static void
     140           1 : report_amount_arithmetic_inconsistency (
     141             :   const char *operation,
     142             :   uint64_t rowid,
     143             :   const struct TALER_Amount *exchange,
     144             :   const struct TALER_Amount *auditor,
     145             :   int profitable)
     146             : {
     147             :   struct TALER_Amount delta;
     148             :   struct TALER_Amount *target;
     149             :   enum GNUNET_DB_QueryStatus qs;
     150             : 
     151           1 :   if (0 < TALER_amount_cmp (exchange,
     152             :                             auditor))
     153             :   {
     154             :     /* exchange > auditor */
     155           0 :     TALER_ARL_amount_subtract (&delta,
     156             :                                exchange,
     157             :                                auditor);
     158             :   }
     159             :   else
     160             :   {
     161             :     /* auditor < exchange */
     162           1 :     profitable = -profitable;
     163           1 :     TALER_ARL_amount_subtract (&delta,
     164             :                                auditor,
     165             :                                exchange);
     166             :   }
     167             : 
     168             :   {
     169           1 :     struct TALER_AUDITORDB_AmountArithmeticInconsistency aai = {
     170             :       .problem_row_id = rowid,
     171             :       .profitable = profitable,
     172             :       .operation = (char *) operation,
     173             :       .exchange_amount = *exchange,
     174             :       .auditor_amount = *auditor,
     175             :     };
     176             : 
     177           1 :     qs = TALER_ARL_adb->insert_amount_arithmetic_inconsistency (
     178           1 :       TALER_ARL_adb->cls,
     179             :       &aai);
     180             : 
     181           1 :     if (qs < 0)
     182             :     {
     183           0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     184           0 :       global_qs = qs;
     185           0 :       return;
     186             :     }
     187             :   }
     188             : 
     189           1 :   if (0 != profitable)
     190             :   {
     191           0 :     target = (1 == profitable)
     192             :       ? &TALER_ARL_USE_AB (reserves_total_arithmetic_delta_plus)
     193           0 :       : &TALER_ARL_USE_AB (reserves_total_arithmetic_delta_minus);
     194           0 :     TALER_ARL_amount_add (target,
     195             :                           target,
     196             :                           &delta);
     197             :   }
     198             : }
     199             : 
     200             : 
     201             : /**
     202             :  * Report a (serious) inconsistency in the exchange's database.
     203             :  *
     204             :  * @param table affected table
     205             :  * @param rowid affected row, 0 if row is missing
     206             :  * @param diagnostic message explaining the problem
     207             :  */
     208             : static void
     209           1 : report_row_inconsistency (const char *table,
     210             :                           uint64_t rowid,
     211             :                           const char *diagnostic)
     212             : {
     213             :   enum GNUNET_DB_QueryStatus qs;
     214           1 :   struct TALER_AUDITORDB_RowInconsistency ri = {
     215             :     .diagnostic = (char *) diagnostic,
     216             :     .row_table = (char *) table,
     217             :     .row_id = rowid
     218             :   };
     219             : 
     220           1 :   qs = TALER_ARL_adb->insert_row_inconsistency (
     221           1 :     TALER_ARL_adb->cls,
     222             :     &ri);
     223             : 
     224           1 :   if (qs < 0)
     225             :   {
     226           0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     227           0 :     global_qs = qs;
     228           0 :     return;
     229             :   }
     230             : }
     231             : 
     232             : 
     233             : /* ***************************** Analyze reserves ************************ */
     234             : /* This logic checks the reserves_in, withdraw and reserves-tables */
     235             : 
     236             : /**
     237             :  * Summary data we keep per reserve.
     238             :  */
     239             : struct ReserveSummary
     240             : {
     241             :   /**
     242             :    * Public key of the reserve.
     243             :    * Always set when the struct is first initialized.
     244             :    */
     245             :   struct TALER_ReservePublicKeyP reserve_pub;
     246             : 
     247             :   /**
     248             :    * Sum of all incoming transfers during this transaction.
     249             :    * Updated only in #handle_reserve_in().
     250             :    */
     251             :   struct TALER_Amount total_in;
     252             : 
     253             :   /**
     254             :    * Sum of all outgoing transfers during this transaction (includes fees).
     255             :    * Updated only in #handle_withdrawals().
     256             :    */
     257             :   struct TALER_Amount total_out;
     258             : 
     259             :   /**
     260             :    * Sum of balance and fees encountered during this transaction.
     261             :    */
     262             :   struct TALER_AUDITORDB_ReserveFeeBalance curr_balance;
     263             : 
     264             :   /**
     265             :    * Previous balances of the reserve as remembered by the auditor.
     266             :    * (updated based on @e total_in and @e total_out at the end).
     267             :    */
     268             :   struct TALER_AUDITORDB_ReserveFeeBalance prev_balance;
     269             : 
     270             :   /**
     271             :    * Previous reserve expiration data, as remembered by the auditor.
     272             :    * (updated on-the-fly in #handle_reserve_in()).
     273             :    */
     274             :   struct GNUNET_TIME_Timestamp a_expiration_date;
     275             : 
     276             :   /**
     277             :    * Which account did originally put money into the reserve?
     278             :    */
     279             :   struct TALER_FullPayto sender_account;
     280             : 
     281             :   /**
     282             :    * Did we have a previous reserve info?  Used to decide between
     283             :    * UPDATE and INSERT later.  Initialized in
     284             :    * #load_auditor_reserve_summary() together with the a-* values
     285             :    * (if available).
     286             :    */
     287             :   bool had_ri;
     288             : 
     289             : };
     290             : 
     291             : 
     292             : /**
     293             :  * Load the auditor's remembered state about the reserve into @a rs.
     294             :  * The "total_in" and "total_out" amounts of @a rs must already be
     295             :  * initialized (so we can determine the currency).
     296             :  *
     297             :  * @param[in,out] rs reserve summary to (fully) initialize
     298             :  * @return transaction status code
     299             :  */
     300             : static enum GNUNET_DB_QueryStatus
     301          68 : load_auditor_reserve_summary (struct ReserveSummary *rs)
     302             : {
     303             :   enum GNUNET_DB_QueryStatus qs;
     304             :   uint64_t rowid;
     305             : 
     306          68 :   qs = TALER_ARL_adb->get_reserve_info (TALER_ARL_adb->cls,
     307          68 :                                         &rs->reserve_pub,
     308             :                                         &rowid,
     309             :                                         &rs->prev_balance,
     310             :                                         &rs->a_expiration_date,
     311             :                                         &rs->sender_account);
     312          68 :   if (0 > qs)
     313             :   {
     314           0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     315           0 :     return qs;
     316             :   }
     317          68 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
     318             :   {
     319          68 :     rs->had_ri = false;
     320          68 :     GNUNET_assert (GNUNET_OK ==
     321             :                    TALER_amount_set_zero (rs->total_in.currency,
     322             :                                           &rs->prev_balance.reserve_balance));
     323          68 :     GNUNET_assert (GNUNET_OK ==
     324             :                    TALER_amount_set_zero (rs->total_in.currency,
     325             :                                           &rs->prev_balance.reserve_loss));
     326          68 :     GNUNET_assert (GNUNET_OK ==
     327             :                    TALER_amount_set_zero (rs->total_in.currency,
     328             :                                           &rs->prev_balance.withdraw_fee_balance
     329             :                                           ));
     330          68 :     GNUNET_assert (GNUNET_OK ==
     331             :                    TALER_amount_set_zero (rs->total_in.currency,
     332             :                                           &rs->prev_balance.close_fee_balance));
     333          68 :     GNUNET_assert (GNUNET_OK ==
     334             :                    TALER_amount_set_zero (rs->total_in.currency,
     335             :                                           &rs->prev_balance.purse_fee_balance));
     336          68 :     GNUNET_assert (GNUNET_OK ==
     337             :                    TALER_amount_set_zero (rs->total_in.currency,
     338             :                                           &rs->prev_balance.open_fee_balance));
     339          68 :     GNUNET_assert (GNUNET_OK ==
     340             :                    TALER_amount_set_zero (rs->total_in.currency,
     341             :                                           &rs->prev_balance.history_fee_balance)
     342             :                    );
     343          68 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     344             :                 "Creating fresh reserve `%s'\n",
     345             :                 TALER_B2S (&rs->reserve_pub));
     346          68 :     return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
     347             :   }
     348           0 :   rs->had_ri = true;
     349           0 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     350             :               "Auditor remembers reserve `%s' has balance %s\n",
     351             :               TALER_B2S (&rs->reserve_pub),
     352             :               TALER_amount2s (&rs->prev_balance.reserve_balance));
     353           0 :   return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
     354             : }
     355             : 
     356             : 
     357             : /**
     358             :  * Closure to the various callbacks we make while checking a reserve.
     359             :  */
     360             : struct ReserveContext
     361             : {
     362             :   /**
     363             :    * Map from hash of reserve's public key to a `struct ReserveSummary`.
     364             :    */
     365             :   struct GNUNET_CONTAINER_MultiHashMap *reserves;
     366             : 
     367             :   /**
     368             :    * Map from hash of denomination's public key to a
     369             :    * static string "revoked" for keys that have been revoked,
     370             :    * or "master signature invalid" in case the revocation is
     371             :    * there but bogus.
     372             :    */
     373             :   struct GNUNET_CONTAINER_MultiHashMap *revoked;
     374             : 
     375             :   /**
     376             :    * Transaction status code, set to error codes if applicable.
     377             :    */
     378             :   enum GNUNET_DB_QueryStatus qs;
     379             : 
     380             : };
     381             : 
     382             : 
     383             : /**
     384             :  * Create a new reserve for @a reserve_pub in @a rc.
     385             :  *
     386             :  * @param[in,out] rc context to update
     387             :  * @param reserve_pub key for which to create a reserve
     388             :  * @return NULL on error
     389             :  */
     390             : static struct ReserveSummary *
     391         135 : setup_reserve (struct ReserveContext *rc,
     392             :                const struct TALER_ReservePublicKeyP *reserve_pub)
     393             : {
     394             :   struct ReserveSummary *rs;
     395             :   struct GNUNET_HashCode key;
     396             :   enum GNUNET_DB_QueryStatus qs;
     397             : 
     398         135 :   GNUNET_CRYPTO_hash (reserve_pub,
     399             :                       sizeof (*reserve_pub),
     400             :                       &key);
     401         135 :   rs = GNUNET_CONTAINER_multihashmap_get (rc->reserves,
     402             :                                           &key);
     403         135 :   if (NULL != rs)
     404          67 :     return rs;
     405          68 :   rs = GNUNET_new (struct ReserveSummary);
     406          68 :   rs->reserve_pub = *reserve_pub;
     407          68 :   GNUNET_assert (GNUNET_OK ==
     408             :                  TALER_amount_set_zero (TALER_ARL_currency,
     409             :                                         &rs->total_in));
     410          68 :   GNUNET_assert (GNUNET_OK ==
     411             :                  TALER_amount_set_zero (TALER_ARL_currency,
     412             :                                         &rs->total_out));
     413          68 :   GNUNET_assert (GNUNET_OK ==
     414             :                  TALER_amount_set_zero (TALER_ARL_currency,
     415             :                                         &rs->curr_balance.reserve_balance));
     416          68 :   GNUNET_assert (GNUNET_OK ==
     417             :                  TALER_amount_set_zero (TALER_ARL_currency,
     418             :                                         &rs->curr_balance.reserve_loss));
     419          68 :   GNUNET_assert (GNUNET_OK ==
     420             :                  TALER_amount_set_zero (TALER_ARL_currency,
     421             :                                         &rs->curr_balance.withdraw_fee_balance))
     422             :   ;
     423          68 :   GNUNET_assert (GNUNET_OK ==
     424             :                  TALER_amount_set_zero (TALER_ARL_currency,
     425             :                                         &rs->curr_balance.close_fee_balance));
     426          68 :   GNUNET_assert (GNUNET_OK ==
     427             :                  TALER_amount_set_zero (TALER_ARL_currency,
     428             :                                         &rs->curr_balance.purse_fee_balance));
     429          68 :   GNUNET_assert (GNUNET_OK ==
     430             :                  TALER_amount_set_zero (TALER_ARL_currency,
     431             :                                         &rs->curr_balance.open_fee_balance));
     432          68 :   GNUNET_assert (GNUNET_OK ==
     433             :                  TALER_amount_set_zero (TALER_ARL_currency,
     434             :                                         &rs->curr_balance.history_fee_balance));
     435          68 :   if (0 > (qs = load_auditor_reserve_summary (rs)))
     436             :   {
     437           0 :     GNUNET_free (rs);
     438           0 :     rc->qs = qs;
     439           0 :     return NULL;
     440             :   }
     441          68 :   GNUNET_assert (GNUNET_OK ==
     442             :                  GNUNET_CONTAINER_multihashmap_put (rc->reserves,
     443             :                                                     &key,
     444             :                                                     rs,
     445             :                                                     GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
     446          68 :   return rs;
     447             : }
     448             : 
     449             : 
     450             : /**
     451             :  * Function called with details about incoming wire transfers.
     452             :  *
     453             :  * @param cls our `struct ReserveContext`
     454             :  * @param rowid unique serial ID for the refresh session in our DB
     455             :  * @param reserve_pub public key of the reserve (also the WTID)
     456             :  * @param credit amount that was received
     457             :  * @param sender_account_details information about the sender's bank account
     458             :  * @param wire_reference unique reference identifying the wire transfer
     459             :  * @param execution_date when did we receive the funds
     460             :  * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
     461             :  */
     462             : static enum GNUNET_GenericReturnValue
     463          68 : handle_reserve_in (
     464             :   void *cls,
     465             :   uint64_t rowid,
     466             :   const struct TALER_ReservePublicKeyP *reserve_pub,
     467             :   const struct TALER_Amount *credit,
     468             :   const struct TALER_FullPayto sender_account_details,
     469             :   uint64_t wire_reference,
     470             :   struct GNUNET_TIME_Timestamp execution_date)
     471             : {
     472          68 :   struct ReserveContext *rc = cls;
     473             :   struct ReserveSummary *rs;
     474             :   struct GNUNET_TIME_Timestamp expiry;
     475             : 
     476             :   (void) wire_reference;
     477             :   /* should be monotonically increasing */
     478          68 :   GNUNET_assert (rowid >= TALER_ARL_USE_PP (reserves_reserve_in_serial_id));
     479          68 :   TALER_ARL_USE_PP (reserves_reserve_in_serial_id) = rowid + 1;
     480          68 :   rs = setup_reserve (rc,
     481             :                       reserve_pub);
     482          68 :   if (NULL == rs)
     483             :   {
     484           0 :     GNUNET_break (0);
     485           0 :     return GNUNET_SYSERR;
     486             :   }
     487          68 :   if (NULL == rs->sender_account.full_payto)
     488             :     rs->sender_account.full_payto
     489          68 :       = GNUNET_strdup (sender_account_details.full_payto);
     490          68 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     491             :               "Additional incoming wire transfer for reserve `%s' of %s\n",
     492             :               TALER_B2S (reserve_pub),
     493             :               TALER_amount2s (credit));
     494          68 :   expiry = GNUNET_TIME_absolute_to_timestamp (
     495             :     GNUNET_TIME_absolute_add (execution_date.abs_time,
     496             :                               idle_reserve_expiration_time));
     497          68 :   rs->a_expiration_date = GNUNET_TIME_timestamp_max (rs->a_expiration_date,
     498             :                                                      expiry);
     499          68 :   TALER_ARL_amount_add (&rs->total_in,
     500             :                         &rs->total_in,
     501             :                         credit);
     502          68 :   return GNUNET_OK;
     503             : }
     504             : 
     505             : 
     506             : /**
     507             :  * Function called with details about withdraw operations.  Verifies
     508             :  * the signature and updates the reserve's balance.
     509             :  *
     510             :  * @param cls our `struct ReserveContext`
     511             :  * @param rowid unique serial ID for the refresh session in our DB
     512             :  * @param num_denom_serials number of elements in @e denom_serials array
     513             :  * @param denom_serials array with length @e num_denom_serials of serial ID's of denominations in our DB
     514             :  * @param selected_h hash over the gamma-selected planchets
     515             :  * @param h_planchets running hash over all hashes of blinded planchets in the original withdraw request
     516             :  * @param blinding_seed the blinding seed for CS denominations that was provided during withdraw; might be NULL
     517             :  * @param age_proof_required true if the withdraw request required an age proof.
     518             :  * @param max_age if @e age_proof_required is true, the maximum age that was set on the coins.
     519             :  * @param noreveal_index if @e age_proof_required is true, the index that was returned by the exchange for the reveal phase.
     520             :  * @param reserve_pub public key of the reserve
     521             :  * @param reserve_sig signature over the withdraw operation
     522             :  * @param execution_date when did the wallet withdraw the coin
     523             :  * @param amount_with_fee amount that was withdrawn
     524             :  * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
     525             :  */
     526             : static enum GNUNET_GenericReturnValue
     527          66 : handle_withdrawals (
     528             :   void *cls,
     529             :   uint64_t rowid,
     530             :   size_t num_denom_serials,
     531             :   const uint64_t *denom_serials,
     532             :   const struct TALER_HashBlindedPlanchetsP *selected_h,
     533             :   const struct TALER_HashBlindedPlanchetsP *h_planchets,
     534             :   const struct TALER_BlindingMasterSeedP *blinding_seed,
     535             :   bool age_proof_required,
     536             :   uint8_t max_age,
     537             :   uint8_t noreveal_index,
     538             :   const struct TALER_ReservePublicKeyP *reserve_pub,
     539             :   const struct TALER_ReserveSignatureP *reserve_sig,
     540             :   struct GNUNET_TIME_Timestamp execution_date,
     541             :   const struct TALER_Amount *amount_with_fee)
     542             : {
     543          66 :   struct ReserveContext *rc = cls;
     544             :   struct ReserveSummary *rs;
     545             :   const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue;
     546             :   struct TALER_Amount auditor_amount;
     547             :   struct TALER_Amount auditor_fee;
     548             :   struct TALER_Amount auditor_amount_with_fee;
     549             :   enum GNUNET_DB_QueryStatus qs;
     550             : 
     551             :   /* should be monotonically increasing */
     552          66 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     553             :               "Analyzing withdrawal row %llu\n",
     554             :               (unsigned long long) rowid);
     555          66 :   GNUNET_assert (rowid >= TALER_ARL_USE_PP (reserves_withdraw_serial_id));
     556          66 :   TALER_ARL_USE_PP (reserves_withdraw_serial_id) = rowid + 1;
     557             : 
     558          66 :   GNUNET_assert (GNUNET_OK == TALER_amount_set_zero (TALER_ARL_currency,
     559             :                                                      &auditor_amount));
     560          66 :   GNUNET_assert (GNUNET_OK == TALER_amount_set_zero (TALER_ARL_currency,
     561             :                                                      &auditor_fee));
     562          66 :   GNUNET_assert (GNUNET_OK == TALER_amount_set_zero (TALER_ARL_currency,
     563             :                                                      &auditor_amount_with_fee));
     564             : 
     565         924 :   for (size_t i = 0; i < num_denom_serials; i++)
     566             :   {
     567             :     /* lookup denomination pub data (make sure denom_pub is valid, establish fees);
     568             :        initializes wsrd.h_denomination_pub! */
     569         858 :     qs = TALER_ARL_get_denomination_info_by_serial (denom_serials[i],
     570             :                                                     &issue);
     571         858 :     if (0 > qs)
     572             :     {
     573           0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     574           0 :       if (GNUNET_DB_STATUS_HARD_ERROR == qs)
     575           0 :         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     576             :                     "Hard database error trying to get denomination by serial %llu (%s) from database!\n",
     577             :                     (unsigned long long) denom_serials[i],
     578             :                     GNUNET_h2s (&h_planchets->hash));
     579           0 :       rc->qs = qs;
     580           0 :       return GNUNET_SYSERR;
     581             :     }
     582         858 :     if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
     583             :     {
     584           0 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     585             :                   "Denomination #%llu not found\n",
     586             :                   (unsigned long long) denom_serials[i]);
     587           0 :       report_row_inconsistency ("withdraw",
     588             :                                 rowid,
     589             :                                 "denomination key not found");
     590           0 :       if (global_qs < 0)
     591           0 :         return GNUNET_SYSERR;
     592           0 :       return GNUNET_OK;
     593             :     }
     594         858 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     595             :                 "Analyzing withdrawn denomination #%llu (%s)\n",
     596             :                 (unsigned long long) denom_serials[i],
     597             :                 TALER_amount2s (&issue->value));
     598             : 
     599             :     /* check that execution date is within withdraw range for denom_pub  */
     600         858 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     601             :                 "Checking withdraw timing: %llu, expire: %llu, timing: %llu\n",
     602             :                 (unsigned long long) issue->start.abs_time.abs_value_us,
     603             :                 (unsigned long
     604             :                  long) issue->expire_withdraw.abs_time.abs_value_us,
     605             :                 (unsigned long long) execution_date.abs_time.abs_value_us);
     606         858 :     if (GNUNET_TIME_timestamp_cmp (issue->start,
     607             :                                    >,
     608         858 :                                    execution_date) ||
     609         858 :         GNUNET_TIME_timestamp_cmp (issue->expire_withdraw,
     610             :                                    <,
     611             :                                    execution_date))
     612             :     {
     613             :       struct TALER_AUDITORDB_DenominationKeyValidityWithdrawInconsistency
     614           1 :         dkvwi ={
     615             :         .problem_row_id = rowid,
     616             :         .execution_date = execution_date.abs_time,
     617           1 :         .denompub_h = issue->denom_hash,
     618             :         .reserve_pub = *reserve_pub
     619             :       };
     620             : 
     621           1 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     622             :                   "Withdraw outside of denomination #%llu validity period detected\n",
     623             :                   (unsigned long long) denom_serials[i]);
     624             :       qs =
     625           1 :         TALER_ARL_adb->insert_denomination_key_validity_withdraw_inconsistency (
     626           1 :           TALER_ARL_adb->cls,
     627             :           &dkvwi);
     628             : 
     629           1 :       if (qs < 0)
     630             :       {
     631           0 :         GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     632           0 :         rc->qs = qs;
     633           0 :         return GNUNET_SYSERR;
     634             :       }
     635             :     }
     636         858 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     637             :                 "Adding withdraw fee of denomination (%s)\n",
     638             :                 TALER_amount2s (&issue->fees.withdraw));
     639         858 :     TALER_ARL_amount_add (&auditor_amount,
     640             :                           &auditor_amount,
     641             :                           &issue->value);
     642         858 :     TALER_ARL_amount_add (&auditor_fee,
     643             :                           &auditor_fee,
     644             :                           &issue->fees.withdraw);
     645             :     {
     646             :       struct TALER_Amount issue_amount_with_fee;
     647             : 
     648         858 :       TALER_ARL_amount_add (&issue_amount_with_fee,
     649             :                             &issue->value,
     650             :                             &issue->fees.withdraw);
     651         858 :       TALER_ARL_amount_add (&auditor_amount_with_fee,
     652             :                             &auditor_amount_with_fee,
     653             :                             &issue_amount_with_fee);
     654             :     }
     655             :   }
     656             : 
     657             :   /* check reserve_sig (first: setup remaining members of wsrd) */
     658          66 :   if (GNUNET_OK !=
     659          66 :       TALER_wallet_withdraw_verify (
     660             :         &auditor_amount,
     661             :         &auditor_fee,
     662             :         h_planchets,
     663             :         blinding_seed,
     664             :         age_proof_required
     665           0 :         ? &issue->age_mask
     666             :         : NULL,
     667             :         age_proof_required
     668             :         ? max_age
     669             :         : 0,
     670             :         reserve_pub,
     671             :         reserve_sig))
     672             :   {
     673           1 :     struct TALER_AUDITORDB_BadSigLosses bsl = {
     674             :       .problem_row_id = rowid,
     675             :       .operation = (char *) "withdraw",
     676             :       .loss = *amount_with_fee,
     677             :       .operation_specific_pub = reserve_pub->eddsa_pub
     678             :     };
     679             : 
     680           1 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     681             :                 "Withdraw signature invalid (row #%llu)\n",
     682             :                 (unsigned long long) rowid);
     683           1 :     qs = TALER_ARL_adb->insert_bad_sig_losses (
     684           1 :       TALER_ARL_adb->cls,
     685             :       &bsl);
     686           1 :     if (qs < 0)
     687             :     {
     688           0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     689           0 :       rc->qs = qs;
     690           0 :       return GNUNET_SYSERR;
     691             :     }
     692           1 :     TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_total_bad_sig_loss),
     693             :                           &TALER_ARL_USE_AB (reserves_total_bad_sig_loss),
     694             :                           amount_with_fee);
     695           1 :     return GNUNET_OK;     /* exit function here, we cannot add this to the legitimate withdrawals */
     696             :   }
     697             : 
     698          65 :   if (0 !=
     699          65 :       TALER_amount_cmp (&auditor_amount_with_fee,
     700             :                         amount_with_fee))
     701             :   {
     702           1 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     703             :                 "Withdraw fee inconsistent (row #%llu)\n",
     704             :                 (unsigned long long) rowid);
     705           1 :     report_row_inconsistency ("withdraw",
     706             :                               rowid,
     707             :                               "amount with fee from exchange does not match denomination value plus fee");
     708           1 :     if (global_qs < 0)
     709           0 :       return GNUNET_SYSERR;
     710             :   }
     711          65 :   rs = setup_reserve (rc,
     712             :                       reserve_pub);
     713          65 :   if (NULL == rs)
     714             :   {
     715           0 :     GNUNET_break (0);
     716           0 :     return GNUNET_SYSERR;
     717             :   }
     718          65 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     719             :               "Reserve `%s' reduced by %s from withdraw\n",
     720             :               TALER_B2S (reserve_pub),
     721             :               TALER_amount2s (&auditor_amount_with_fee));
     722          65 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     723             :               "Increasing withdraw profits by fee %s\n",
     724             :               TALER_amount2s (&issue->fees.withdraw));
     725          65 :   TALER_ARL_amount_add (&rs->curr_balance.withdraw_fee_balance,
     726             :                         &rs->curr_balance.withdraw_fee_balance,
     727             :                         &issue->fees.withdraw);
     728          65 :   TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_withdraw_fee_revenue),
     729             :                         &TALER_ARL_USE_AB (reserves_withdraw_fee_revenue),
     730             :                         &issue->fees.withdraw);
     731          65 :   TALER_ARL_amount_add (&rs->total_out,
     732             :                         &rs->total_out,
     733             :                         &auditor_amount_with_fee);
     734          65 :   return GNUNET_OK;
     735             : }
     736             : 
     737             : 
     738             : /**
     739             :  * Function called with details about withdraw operations.  Verifies
     740             :  * the signature and updates the reserve's balance.
     741             :  *
     742             :  * @param cls our `struct ReserveContext`
     743             :  * @param rowid unique serial ID for the refresh session in our DB
     744             :  * @param timestamp when did we receive the recoup request
     745             :  * @param amount how much should be added back to the reserve
     746             :  * @param reserve_pub public key of the reserve
     747             :  * @param coin public information about the coin, denomination signature is
     748             :  *        already verified in #check_recoup()
     749             :  * @param denom_pub public key of the denomionation of @a coin
     750             :  * @param coin_sig signature with @e coin_pub of type #TALER_SIGNATURE_WALLET_COIN_RECOUP
     751             :  * @param coin_blind blinding factor used to blind the coin
     752             :  * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
     753             :  */
     754             : static enum GNUNET_GenericReturnValue
     755           0 : handle_recoup_by_reserve (
     756             :   void *cls,
     757             :   uint64_t rowid,
     758             :   struct GNUNET_TIME_Timestamp timestamp,
     759             :   const struct TALER_Amount *amount,
     760             :   const struct TALER_ReservePublicKeyP *reserve_pub,
     761             :   const struct TALER_CoinPublicInfo *coin,
     762             :   const struct TALER_DenominationPublicKey *denom_pub,
     763             :   const struct TALER_CoinSpendSignatureP *coin_sig,
     764             :   const union GNUNET_CRYPTO_BlindingSecretP *coin_blind)
     765             : {
     766           0 :   struct ReserveContext *rc = cls;
     767             :   struct ReserveSummary *rs;
     768             :   struct GNUNET_TIME_Timestamp expiry;
     769             :   struct TALER_MasterSignatureP msig;
     770             :   uint64_t rev_rowid;
     771             :   enum GNUNET_DB_QueryStatus qs;
     772             :   const char *rev;
     773             : 
     774             :   (void) denom_pub;
     775             :   /* should be monotonically increasing */
     776           0 :   GNUNET_assert (rowid >= TALER_ARL_USE_PP (reserves_reserve_recoup_serial_id));
     777           0 :   TALER_ARL_USE_PP (reserves_reserve_recoup_serial_id) = rowid + 1;
     778             :   /* We know that denom_pub matches denom_pub_hash because this
     779             :      is how the SQL statement joined the tables. */
     780           0 :   if (GNUNET_OK !=
     781           0 :       TALER_wallet_recoup_verify (&coin->denom_pub_hash,
     782             :                                   coin_blind,
     783             :                                   &coin->coin_pub,
     784             :                                   coin_sig))
     785             :   {
     786           0 :     struct TALER_AUDITORDB_BadSigLosses bslr = {
     787             :       .problem_row_id = rowid,
     788             :       .operation = (char *) "recoup",
     789             :       .loss = *amount,
     790             :       .operation_specific_pub = coin->coin_pub.eddsa_pub
     791             :     };
     792             : 
     793           0 :     qs = TALER_ARL_adb->insert_bad_sig_losses (
     794           0 :       TALER_ARL_adb->cls,
     795             :       &bslr);
     796             : 
     797           0 :     if (qs < 0)
     798             :     {
     799           0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     800           0 :       rc->qs = qs;
     801           0 :       return GNUNET_SYSERR;
     802             :     }
     803           0 :     TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_total_bad_sig_loss),
     804             :                           &TALER_ARL_USE_AB (reserves_total_bad_sig_loss),
     805             :                           amount);
     806             :   }
     807             : 
     808             :   /* check that the coin was eligible for recoup!*/
     809           0 :   rev = GNUNET_CONTAINER_multihashmap_get (rc->revoked,
     810             :                                            &coin->denom_pub_hash.hash);
     811           0 :   if (NULL == rev)
     812             :   {
     813           0 :     qs = TALER_ARL_edb->get_denomination_revocation (TALER_ARL_edb->cls,
     814             :                                                      &coin->denom_pub_hash,
     815             :                                                      &msig,
     816             :                                                      &rev_rowid);
     817           0 :     if (0 > qs)
     818             :     {
     819           0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     820           0 :       rc->qs = qs;
     821           0 :       return GNUNET_SYSERR;
     822             :     }
     823           0 :     if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
     824             :     {
     825           0 :       report_row_inconsistency ("recoup",
     826             :                                 rowid,
     827             :                                 "denomination key not in revocation set");
     828           0 :       if (global_qs < 0)
     829           0 :         return GNUNET_SYSERR;
     830           0 :       TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_reserve_loss),
     831             :                             &TALER_ARL_USE_AB (reserves_reserve_loss),
     832             :                             amount);
     833             :     }
     834             :     else
     835             :     {
     836           0 :       if (GNUNET_OK !=
     837           0 :           TALER_exchange_offline_denomination_revoke_verify (
     838             :             &coin->denom_pub_hash,
     839             :             &TALER_ARL_master_pub,
     840             :             &msig))
     841             :       {
     842           0 :         rev = "master signature invalid";
     843             :       }
     844             :       else
     845             :       {
     846           0 :         rev = "revoked";
     847             :       }
     848           0 :       GNUNET_assert (
     849             :         GNUNET_OK ==
     850             :         GNUNET_CONTAINER_multihashmap_put (
     851             :           rc->revoked,
     852             :           &coin->denom_pub_hash.hash,
     853             :           (void *) rev,
     854             :           GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
     855             :     }
     856             :   }
     857             :   else
     858             :   {
     859           0 :     rev_rowid = 0;   /* reported elsewhere */
     860             :   }
     861           0 :   if ((NULL != rev) &&
     862           0 :       (0 == strcmp (rev,
     863             :                     "master signature invalid")))
     864             :   {
     865           0 :     struct TALER_AUDITORDB_BadSigLosses bslrm = {
     866             :       .problem_row_id = rev_rowid,
     867             :       .operation = (char *) "recoup-master",
     868             :       .loss = *amount,
     869             :       .operation_specific_pub = TALER_ARL_master_pub.eddsa_pub
     870             :     };
     871             : 
     872           0 :     qs = TALER_ARL_adb->insert_bad_sig_losses (
     873           0 :       TALER_ARL_adb->cls,
     874             :       &bslrm);
     875             : 
     876           0 :     if (qs < 0)
     877             :     {
     878           0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     879           0 :       rc->qs = qs;
     880           0 :       return GNUNET_SYSERR;
     881             :     }
     882           0 :     TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_total_bad_sig_loss),
     883             :                           &TALER_ARL_USE_AB (reserves_total_bad_sig_loss),
     884             :                           amount);
     885             :   }
     886             : 
     887           0 :   rs = setup_reserve (rc,
     888             :                       reserve_pub);
     889           0 :   if (NULL == rs)
     890             :   {
     891           0 :     GNUNET_break (0);
     892           0 :     return GNUNET_SYSERR;
     893             :   }
     894           0 :   TALER_ARL_amount_add (&rs->total_in,
     895             :                         &rs->total_in,
     896             :                         amount);
     897           0 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     898             :               "Additional /recoup value to for reserve `%s' of %s\n",
     899             :               TALER_B2S (reserve_pub),
     900             :               TALER_amount2s (amount));
     901           0 :   expiry = GNUNET_TIME_absolute_to_timestamp (
     902             :     GNUNET_TIME_absolute_add (timestamp.abs_time,
     903             :                               idle_reserve_expiration_time));
     904           0 :   rs->a_expiration_date = GNUNET_TIME_timestamp_max (rs->a_expiration_date,
     905             :                                                      expiry);
     906           0 :   return GNUNET_OK;
     907             : }
     908             : 
     909             : 
     910             : /**
     911             :  * Obtain the closing fee for a transfer at @a time for target
     912             :  * @a receiver_account.
     913             :  *
     914             :  * @param receiver_account payto:// URI of the target account
     915             :  * @param atime when was the transfer made
     916             :  * @param[out] fee set to the closing fee
     917             :  * @return #GNUNET_OK on success
     918             :  */
     919             : static enum GNUNET_GenericReturnValue
     920           5 : get_closing_fee (const struct TALER_FullPayto receiver_account,
     921             :                  struct GNUNET_TIME_Timestamp atime,
     922             :                  struct TALER_Amount *fee)
     923             : {
     924             :   struct TALER_MasterSignatureP master_sig;
     925             :   struct GNUNET_TIME_Timestamp start_date;
     926             :   struct GNUNET_TIME_Timestamp end_date;
     927             :   struct TALER_WireFeeSet fees;
     928             :   char *method;
     929             :   uint64_t rowid;
     930             : 
     931           5 :   method = TALER_payto_get_method (receiver_account.full_payto);
     932           5 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     933             :               "Method is `%s'\n",
     934             :               method);
     935           5 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     936           5 :       TALER_ARL_edb->get_wire_fee (TALER_ARL_edb->cls,
     937             :                                    method,
     938             :                                    atime,
     939             :                                    &rowid,
     940             :                                    &start_date,
     941             :                                    &end_date,
     942             :                                    &fees,
     943             :                                    &master_sig))
     944             :   {
     945             :     char *diag;
     946             : 
     947           0 :     GNUNET_asprintf (&diag,
     948             :                      "closing fee for `%s' unavailable at %s\n",
     949             :                      method,
     950             :                      GNUNET_TIME_timestamp2s (atime));
     951           0 :     report_row_inconsistency ("closing-fee",
     952             :                               rowid,
     953             :                               diag);
     954           0 :     GNUNET_free (diag);
     955           0 :     GNUNET_free (method);
     956           0 :     return GNUNET_SYSERR;
     957             :   }
     958           5 :   *fee = fees.closing;
     959           5 :   GNUNET_free (method);
     960           5 :   return GNUNET_OK;
     961             : }
     962             : 
     963             : 
     964             : /**
     965             :  * Function called about reserve opening operations.
     966             :  *
     967             :  * @param cls closure
     968             :  * @param rowid row identifier used to uniquely identify the reserve closing operation
     969             :  * @param reserve_payment how much to pay from the
     970             :  *        reserve's own balance for opening the reserve
     971             :  * @param request_timestamp when was the request created
     972             :  * @param reserve_expiration desired expiration time for the reserve
     973             :  * @param purse_limit minimum number of purses the client
     974             :  *       wants to have concurrently open for this reserve
     975             :  * @param reserve_pub public key of the reserve
     976             :  * @param reserve_sig signature affirming the operation
     977             :  * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
     978             :  */
     979             : static enum GNUNET_GenericReturnValue
     980           0 : handle_reserve_open (
     981             :   void *cls,
     982             :   uint64_t rowid,
     983             :   const struct TALER_Amount *reserve_payment,
     984             :   struct GNUNET_TIME_Timestamp request_timestamp,
     985             :   struct GNUNET_TIME_Timestamp reserve_expiration,
     986             :   uint32_t purse_limit,
     987             :   const struct TALER_ReservePublicKeyP *reserve_pub,
     988             :   const struct TALER_ReserveSignatureP *reserve_sig)
     989             : {
     990           0 :   struct ReserveContext *rc = cls;
     991             :   struct ReserveSummary *rs;
     992             :   enum GNUNET_DB_QueryStatus qs;
     993             : 
     994             :   /* should be monotonically increasing */
     995           0 :   GNUNET_assert (rowid >= TALER_ARL_USE_PP (reserves_reserve_open_serial_id));
     996           0 :   TALER_ARL_USE_PP (reserves_reserve_open_serial_id) = rowid + 1;
     997             : 
     998           0 :   rs = setup_reserve (rc,
     999             :                       reserve_pub);
    1000           0 :   if (NULL == rs)
    1001             :   {
    1002           0 :     GNUNET_break (0);
    1003           0 :     return GNUNET_SYSERR;
    1004             :   }
    1005           0 :   if (GNUNET_OK !=
    1006           0 :       TALER_wallet_reserve_open_verify (reserve_payment,
    1007             :                                         request_timestamp,
    1008             :                                         reserve_expiration,
    1009             :                                         purse_limit,
    1010             :                                         reserve_pub,
    1011             :                                         reserve_sig))
    1012             :   {
    1013           0 :     struct TALER_AUDITORDB_BadSigLosses bsl = {
    1014             :       .problem_row_id = rowid,
    1015             :       .operation = (char *) "reserve-open",
    1016             :       .loss = *reserve_payment,
    1017             :       .operation_specific_pub = reserve_pub->eddsa_pub
    1018             :     };
    1019             : 
    1020           0 :     qs = TALER_ARL_adb->insert_bad_sig_losses (
    1021           0 :       TALER_ARL_adb->cls,
    1022             :       &bsl);
    1023             : 
    1024           0 :     if (qs < 0)
    1025             :     {
    1026           0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1027           0 :       rc->qs = qs;
    1028           0 :       return GNUNET_SYSERR;
    1029             :     }
    1030           0 :     TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_total_bad_sig_loss),
    1031             :                           &TALER_ARL_USE_AB (reserves_total_bad_sig_loss),
    1032             :                           reserve_payment);
    1033           0 :     return GNUNET_OK;
    1034             :   }
    1035           0 :   TALER_ARL_amount_add (&rs->curr_balance.open_fee_balance,
    1036             :                         &rs->curr_balance.open_fee_balance,
    1037             :                         reserve_payment);
    1038           0 :   TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_open_fee_revenue),
    1039             :                         &TALER_ARL_USE_AB (reserves_open_fee_revenue),
    1040             :                         reserve_payment);
    1041           0 :   TALER_ARL_amount_add (&rs->total_out,
    1042             :                         &rs->total_out,
    1043             :                         reserve_payment);
    1044           0 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1045             :               "Additional open operation for reserve `%s' of %s\n",
    1046             :               TALER_B2S (reserve_pub),
    1047             :               TALER_amount2s (reserve_payment));
    1048           0 :   return GNUNET_OK;
    1049             : }
    1050             : 
    1051             : 
    1052             : /**
    1053             :  * Function called about reserve closing operations
    1054             :  * the aggregator triggered.
    1055             :  *
    1056             :  * @param cls closure
    1057             :  * @param rowid row identifier used to uniquely identify the reserve closing operation
    1058             :  * @param execution_date when did we execute the close operation
    1059             :  * @param amount_with_fee how much did we debit the reserve
    1060             :  * @param closing_fee how much did we charge for closing the reserve
    1061             :  * @param reserve_pub public key of the reserve
    1062             :  * @param receiver_account where did we send the funds
    1063             :  * @param transfer_details details about the wire transfer
    1064             :  * @param close_request_row which close request triggered the operation?
    1065             :  *         0 if it was a timeout
    1066             :  * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
    1067             :  */
    1068             : static enum GNUNET_GenericReturnValue
    1069           2 : handle_reserve_closed (
    1070             :   void *cls,
    1071             :   uint64_t rowid,
    1072             :   struct GNUNET_TIME_Timestamp execution_date,
    1073             :   const struct TALER_Amount *amount_with_fee,
    1074             :   const struct TALER_Amount *closing_fee,
    1075             :   const struct TALER_ReservePublicKeyP *reserve_pub,
    1076             :   const struct TALER_FullPayto receiver_account,
    1077             :   const struct TALER_WireTransferIdentifierRawP *transfer_details,
    1078             :   uint64_t close_request_row)
    1079             : {
    1080           2 :   struct ReserveContext *rc = cls;
    1081             :   struct ReserveSummary *rs;
    1082             : 
    1083             :   (void) transfer_details;
    1084             :   /* should be monotonically increasing */
    1085           2 :   GNUNET_assert (rowid >= TALER_ARL_USE_PP (reserves_reserve_close_serial_id));
    1086           2 :   TALER_ARL_USE_PP (reserves_reserve_close_serial_id) = rowid + 1;
    1087             : 
    1088           2 :   rs = setup_reserve (rc,
    1089             :                       reserve_pub);
    1090           2 :   if (NULL == rs)
    1091             :   {
    1092           0 :     GNUNET_break (0);
    1093           0 :     return GNUNET_SYSERR;
    1094             :   }
    1095             :   {
    1096             :     struct TALER_Amount expected_fee;
    1097             : 
    1098             :     /* verify closing_fee is correct! */
    1099           2 :     if (GNUNET_OK !=
    1100           2 :         get_closing_fee (receiver_account,
    1101             :                          execution_date,
    1102             :                          &expected_fee))
    1103             :     {
    1104           0 :       GNUNET_break (0);
    1105             :     }
    1106           2 :     else if (0 != TALER_amount_cmp (&expected_fee,
    1107             :                                     closing_fee))
    1108             :     {
    1109           0 :       report_amount_arithmetic_inconsistency (
    1110             :         "closing aggregation fee",
    1111             :         rowid,
    1112             :         closing_fee,
    1113             :         &expected_fee,
    1114             :         1);
    1115           0 :       if (global_qs < 0)
    1116           0 :         return GNUNET_SYSERR;
    1117             :     }
    1118             :   }
    1119             : 
    1120           2 :   TALER_ARL_amount_add (&rs->curr_balance.close_fee_balance,
    1121             :                         &rs->curr_balance.close_fee_balance,
    1122             :                         closing_fee);
    1123           2 :   TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_close_fee_revenue),
    1124             :                         &TALER_ARL_USE_AB (reserves_close_fee_revenue),
    1125             :                         closing_fee);
    1126           2 :   TALER_ARL_amount_add (&rs->total_out,
    1127             :                         &rs->total_out,
    1128             :                         amount_with_fee);
    1129           2 :   if (0 != close_request_row)
    1130             :   {
    1131             :     struct TALER_ReserveSignatureP reserve_sig;
    1132             :     struct GNUNET_TIME_Timestamp request_timestamp;
    1133             :     struct TALER_Amount close_balance;
    1134             :     struct TALER_Amount close_fee;
    1135             :     struct TALER_FullPayto payto_uri;
    1136             :     enum GNUNET_DB_QueryStatus qs;
    1137             : 
    1138           0 :     qs = TALER_ARL_edb->select_reserve_close_request_info (
    1139           0 :       TALER_ARL_edb->cls,
    1140             :       reserve_pub,
    1141             :       close_request_row,
    1142             :       &reserve_sig,
    1143             :       &request_timestamp,
    1144             :       &close_balance,
    1145             :       &close_fee,
    1146             :       &payto_uri);
    1147           0 :     if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
    1148             :     {
    1149           0 :       report_row_inconsistency ("reserves_close",
    1150             :                                 rowid,
    1151             :                                 "reserve close request unknown");
    1152           0 :       if (global_qs < 0)
    1153           0 :         return GNUNET_SYSERR;
    1154             :     }
    1155             :     else
    1156             :     {
    1157             :       struct TALER_FullPaytoHashP h_payto;
    1158             : 
    1159           0 :       TALER_full_payto_hash (payto_uri,
    1160             :                              &h_payto);
    1161           0 :       if (GNUNET_OK !=
    1162           0 :           TALER_wallet_reserve_close_verify (
    1163             :             request_timestamp,
    1164             :             &h_payto,
    1165             :             reserve_pub,
    1166             :             &reserve_sig))
    1167             :       {
    1168           0 :         struct TALER_AUDITORDB_BadSigLosses bsl = {
    1169             :           .problem_row_id = close_request_row,
    1170             :           .operation = (char *) "close-request",
    1171             :           .loss = *amount_with_fee,
    1172             :           .operation_specific_pub = reserve_pub->eddsa_pub
    1173             :         };
    1174             : 
    1175           0 :         qs = TALER_ARL_adb->insert_bad_sig_losses (
    1176           0 :           TALER_ARL_adb->cls,
    1177             :           &bsl);
    1178             : 
    1179           0 :         if (qs < 0)
    1180             :         {
    1181           0 :           GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1182           0 :           rc->qs = qs;
    1183           0 :           GNUNET_free (payto_uri.full_payto);
    1184           0 :           return GNUNET_SYSERR;
    1185             :         }
    1186           0 :         TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_total_bad_sig_loss),
    1187             :                               &TALER_ARL_USE_AB (reserves_total_bad_sig_loss),
    1188             :                               amount_with_fee);
    1189             :       }
    1190             :     }
    1191           0 :     if ( (NULL == payto_uri.full_payto) &&
    1192           0 :          (NULL == rs->sender_account.full_payto) )
    1193             :     {
    1194           0 :       GNUNET_break (! rs->had_ri);
    1195           0 :       report_row_inconsistency ("reserves_close",
    1196             :                                 rowid,
    1197             :                                 "target account not verified, auditor does not know reserve");
    1198           0 :       if (global_qs < 0)
    1199           0 :         return GNUNET_SYSERR;
    1200             :     }
    1201           0 :     if (NULL == payto_uri.full_payto)
    1202             :     {
    1203           0 :       if ((NULL == rs->sender_account.full_payto) ||
    1204           0 :           (0 != TALER_full_payto_cmp (rs->sender_account,
    1205             :                                       receiver_account)))
    1206             :       {
    1207           0 :         report_row_inconsistency ("reserves_close",
    1208             :                                   rowid,
    1209             :                                   "target account does not match origin account");
    1210           0 :         if (global_qs < 0)
    1211           0 :           return GNUNET_SYSERR;
    1212             :       }
    1213             :     }
    1214             :     else
    1215             :     {
    1216           0 :       if (0 != TALER_full_payto_cmp (payto_uri,
    1217             :                                      receiver_account))
    1218             :       {
    1219           0 :         report_row_inconsistency ("reserves_close",
    1220             :                                   rowid,
    1221             :                                   "target account does not match origin account");
    1222           0 :         if (global_qs < 0)
    1223             :         {
    1224           0 :           GNUNET_free (payto_uri.full_payto);
    1225           0 :           return GNUNET_SYSERR;
    1226             :         }
    1227             :       }
    1228             :     }
    1229           0 :     GNUNET_free (payto_uri.full_payto);
    1230             :   }
    1231             :   else
    1232             :   {
    1233           2 :     if (NULL == rs->sender_account.full_payto)
    1234             :     {
    1235           0 :       GNUNET_break (! rs->had_ri);
    1236           0 :       report_row_inconsistency ("reserves_close",
    1237             :                                 rowid,
    1238             :                                 "target account not verified, auditor does not know reserve");
    1239           0 :       if (global_qs < 0)
    1240           0 :         return GNUNET_SYSERR;
    1241             :     }
    1242           2 :     else if (0 != TALER_full_payto_cmp (rs->sender_account,
    1243             :                                         receiver_account))
    1244             :     {
    1245           0 :       report_row_inconsistency ("reserves_close",
    1246             :                                 rowid,
    1247             :                                 "target account does not match origin account");
    1248           0 :       if (global_qs < 0)
    1249           0 :         return GNUNET_SYSERR;
    1250             :     }
    1251             :   }
    1252             : 
    1253           2 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1254             :               "Additional closing operation for reserve `%s' of %s\n",
    1255             :               TALER_B2S (reserve_pub),
    1256             :               TALER_amount2s (amount_with_fee));
    1257           2 :   return GNUNET_OK;
    1258             : }
    1259             : 
    1260             : 
    1261             : /**
    1262             :  * Function called with details about account merge requests that have been
    1263             :  * made, with the goal of accounting for the merge fee paid by the reserve (if
    1264             :  * applicable).
    1265             :  *
    1266             :  * @param cls closure
    1267             :  * @param rowid unique serial ID for the deposit in our DB
    1268             :  * @param reserve_pub reserve affected by the merge
    1269             :  * @param purse_pub purse being merged
    1270             :  * @param h_contract_terms hash over contract of the purse
    1271             :  * @param purse_expiration when would the purse expire
    1272             :  * @param amount total amount in the purse
    1273             :  * @param min_age minimum age of all coins deposited into the purse
    1274             :  * @param flags how was the purse created
    1275             :  * @param purse_fee if a purse fee was paid, how high is it
    1276             :  * @param merge_timestamp when was the merge approved
    1277             :  * @param reserve_sig signature by reserve approving the merge
    1278             :  * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
    1279             :  */
    1280             : static enum GNUNET_GenericReturnValue
    1281           0 : handle_account_merged (
    1282             :   void *cls,
    1283             :   uint64_t rowid,
    1284             :   const struct TALER_ReservePublicKeyP *reserve_pub,
    1285             :   const struct TALER_PurseContractPublicKeyP *purse_pub,
    1286             :   const struct TALER_PrivateContractHashP *h_contract_terms,
    1287             :   struct GNUNET_TIME_Timestamp purse_expiration,
    1288             :   const struct TALER_Amount *amount,
    1289             :   uint32_t min_age,
    1290             :   enum TALER_WalletAccountMergeFlags flags,
    1291             :   const struct TALER_Amount *purse_fee,
    1292             :   struct GNUNET_TIME_Timestamp merge_timestamp,
    1293             :   const struct TALER_ReserveSignatureP *reserve_sig)
    1294             : {
    1295           0 :   struct ReserveContext *rc = cls;
    1296             :   struct ReserveSummary *rs;
    1297             :   enum GNUNET_DB_QueryStatus qs;
    1298             : 
    1299             :   /* should be monotonically increasing */
    1300           0 :   GNUNET_assert (rowid >= TALER_ARL_USE_PP (reserves_account_merges_serial_id));
    1301           0 :   TALER_ARL_USE_PP (reserves_account_merges_serial_id) = rowid + 1;
    1302           0 :   if (GNUNET_OK !=
    1303           0 :       TALER_wallet_account_merge_verify (merge_timestamp,
    1304             :                                          purse_pub,
    1305             :                                          purse_expiration,
    1306             :                                          h_contract_terms,
    1307             :                                          amount,
    1308             :                                          purse_fee,
    1309             :                                          min_age,
    1310             :                                          flags,
    1311             :                                          reserve_pub,
    1312             :                                          reserve_sig))
    1313             :   {
    1314           0 :     struct TALER_AUDITORDB_BadSigLosses bsl = {
    1315             :       .problem_row_id = rowid,
    1316             :       .operation = (char *) "account-merge",
    1317             :       .loss = *purse_fee,
    1318             :       .operation_specific_pub = reserve_pub->eddsa_pub
    1319             :     };
    1320             : 
    1321           0 :     qs = TALER_ARL_adb->insert_bad_sig_losses (
    1322           0 :       TALER_ARL_adb->cls,
    1323             :       &bsl);
    1324           0 :     if (qs < 0)
    1325             :     {
    1326           0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1327           0 :       rc->qs = qs;
    1328           0 :       return GNUNET_SYSERR;
    1329             :     }
    1330           0 :     TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_total_bad_sig_loss),
    1331             :                           &TALER_ARL_USE_AB (reserves_total_bad_sig_loss),
    1332             :                           purse_fee);
    1333           0 :     return GNUNET_OK;
    1334             :   }
    1335           0 :   if ((flags & TALER_WAMF_MERGE_MODE_MASK) !=
    1336             :       TALER_WAMF_MODE_CREATE_WITH_PURSE_FEE)
    1337           0 :     return GNUNET_OK; /* no impact on reserve balance */
    1338           0 :   rs = setup_reserve (rc,
    1339             :                       reserve_pub);
    1340           0 :   if (NULL == rs)
    1341             :   {
    1342           0 :     GNUNET_break (0);
    1343           0 :     return GNUNET_SYSERR;
    1344             :   }
    1345           0 :   TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_purse_fee_revenue),
    1346             :                         &TALER_ARL_USE_AB (reserves_purse_fee_revenue),
    1347             :                         purse_fee);
    1348           0 :   TALER_ARL_amount_add (&rs->curr_balance.purse_fee_balance,
    1349             :                         &rs->curr_balance.purse_fee_balance,
    1350             :                         purse_fee);
    1351           0 :   TALER_ARL_amount_add (&rs->total_out,
    1352             :                         &rs->total_out,
    1353             :                         purse_fee);
    1354           0 :   return GNUNET_OK;
    1355             : }
    1356             : 
    1357             : 
    1358             : /**
    1359             :  * Function called with details about a purse that was merged into an account.
    1360             :  * Only updates the reserve balance, the actual verifications are done in the
    1361             :  * purse helper.
    1362             :  *
    1363             :  * @param cls closure
    1364             :  * @param rowid unique serial ID for the refund in our DB
    1365             :  * @param purse_pub public key of the purse
    1366             :  * @param reserve_pub which reserve is the purse credited to
    1367             :  * @param purse_value what is the target value of the purse
    1368             :  * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
    1369             :  */
    1370             : static enum GNUNET_GenericReturnValue
    1371           0 : purse_decision_cb (void *cls,
    1372             :                    uint64_t rowid,
    1373             :                    const struct TALER_PurseContractPublicKeyP *purse_pub,
    1374             :                    const struct TALER_ReservePublicKeyP *reserve_pub,
    1375             :                    const struct TALER_Amount *purse_value)
    1376             : {
    1377           0 :   struct ReserveContext *rc = cls;
    1378             :   struct ReserveSummary *rs;
    1379             : 
    1380           0 :   GNUNET_assert (rowid >= TALER_ARL_USE_PP (
    1381             :                    reserves_purse_decisions_serial_id)); /* should be monotonically increasing */
    1382           0 :   TALER_ARL_USE_PP (reserves_purse_decisions_serial_id) = rowid + 1;
    1383           0 :   rs = setup_reserve (rc,
    1384             :                       reserve_pub);
    1385           0 :   if (NULL == rs)
    1386             :   {
    1387           0 :     GNUNET_break (0);
    1388           0 :     return GNUNET_SYSERR;
    1389             :   }
    1390           0 :   TALER_ARL_amount_add (&rs->total_in,
    1391             :                         &rs->total_in,
    1392             :                         purse_value);
    1393           0 :   return GNUNET_OK;
    1394             : }
    1395             : 
    1396             : 
    1397             : /**
    1398             :  * Check that the reserve summary matches what the exchange database
    1399             :  * thinks about the reserve, and update our own state of the reserve.
    1400             :  *
    1401             :  * Remove all reserves that we are happy with from the DB.
    1402             :  *
    1403             :  * @param cls our `struct ReserveContext`
    1404             :  * @param key hash of the reserve public key
    1405             :  * @param value a `struct ReserveSummary`
    1406             :  * @return #GNUNET_OK to process more entries
    1407             :  */
    1408             : static enum GNUNET_GenericReturnValue
    1409          68 : verify_reserve_balance (void *cls,
    1410             :                         const struct GNUNET_HashCode *key,
    1411             :                         void *value)
    1412             : {
    1413          68 :   struct ReserveContext *rc = cls;
    1414          68 :   struct ReserveSummary *rs = value;
    1415             :   struct TALER_Amount mbalance;
    1416             :   struct TALER_Amount nbalance;
    1417             :   enum GNUNET_DB_QueryStatus qs;
    1418             :   enum GNUNET_GenericReturnValue ret;
    1419             : 
    1420          68 :   ret = GNUNET_OK;
    1421             :   /* Check our reserve summary balance calculation shows that
    1422             :      the reserve balance is acceptable (i.e. non-negative) */
    1423          68 :   TALER_ARL_amount_add (&mbalance,
    1424             :                         &rs->total_in,
    1425             :                         &rs->prev_balance.reserve_balance);
    1426          68 :   if (TALER_ARL_SR_INVALID_NEGATIVE ==
    1427          68 :       TALER_ARL_amount_subtract_neg (&nbalance,
    1428             :                                      &mbalance,
    1429             :                                      &rs->total_out))
    1430             :   {
    1431           1 :     struct TALER_AUDITORDB_ReserveBalanceInsufficientInconsistency rbiil = {
    1432             :       .reserve_pub = rs->reserve_pub.eddsa_pub,
    1433             :       .inconsistency_gain = false
    1434             :     };
    1435             : 
    1436           1 :     TALER_ARL_amount_subtract (&rbiil.inconsistency_amount,
    1437             :                                &rs->total_out,
    1438             :                                &mbalance);
    1439           1 :     TALER_ARL_amount_add (&rs->curr_balance.reserve_loss,
    1440             :                           &rs->prev_balance.reserve_loss,
    1441             :                           &rbiil.inconsistency_amount);
    1442           1 :     TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_reserve_loss),
    1443             :                           &TALER_ARL_USE_AB (reserves_reserve_loss),
    1444             :                           &rbiil.inconsistency_amount);
    1445           1 :     qs = TALER_ARL_adb->insert_reserve_balance_insufficient_inconsistency (
    1446           1 :       TALER_ARL_adb->cls,
    1447             :       &rbiil);
    1448             : 
    1449           1 :     if (qs < 0)
    1450             :     {
    1451           0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1452           0 :       rc->qs = qs;
    1453           0 :       return GNUNET_SYSERR;
    1454             :     }
    1455             :     /* Continue with a reserve balance of zero */
    1456           1 :     GNUNET_assert (GNUNET_OK ==
    1457             :                    TALER_amount_set_zero (TALER_ARL_currency,
    1458             :                                           &rs->curr_balance.reserve_balance));
    1459           1 :     nbalance = rs->curr_balance.reserve_balance;
    1460             :   }
    1461             :   else
    1462             :   {
    1463             :     /* Update remaining reserve balance! */
    1464          67 :     rs->curr_balance.reserve_balance = nbalance;
    1465             :   }
    1466             : 
    1467          68 :   if (internal_checks)
    1468             :   {
    1469             :     /* Now check OUR balance calculation vs. the one the exchange has
    1470             :        in its database. This can only be done when we are doing an
    1471             :        internal audit, as otherwise the balance of the 'reserves' table
    1472             :        is not replicated at the auditor. */
    1473          68 :     struct TALER_EXCHANGEDB_Reserve reserve = {
    1474             :       .pub = rs->reserve_pub
    1475             :     };
    1476             : 
    1477          68 :     qs = TALER_ARL_edb->reserves_get (TALER_ARL_edb->cls,
    1478             :                                       &reserve);
    1479          68 :     if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
    1480             :     {
    1481             :       /* If the exchange doesn't have this reserve in the summary, it
    1482             :          is like the exchange 'lost' that amount from its records,
    1483             :          making an illegitimate gain over the amount it dropped.
    1484             :          We don't add the amount to some total simply because it is
    1485             :          not an actualized gain and could be trivially corrected by
    1486             :          restoring the summary. */
    1487           0 :       struct TALER_AUDITORDB_ReserveBalanceInsufficientInconsistency rbiig = {
    1488             :         .reserve_pub = rs->reserve_pub.eddsa_pub,
    1489             :         .inconsistency_amount = nbalance,
    1490             :         .inconsistency_gain = true
    1491             :       };
    1492             : 
    1493           0 :       qs = TALER_ARL_adb->insert_reserve_balance_insufficient_inconsistency (
    1494           0 :         TALER_ARL_adb->cls,
    1495             :         &rbiig);
    1496             : 
    1497           0 :       if (qs < 0)
    1498             :       {
    1499           0 :         GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1500           0 :         rc->qs = qs;
    1501           0 :         return GNUNET_SYSERR;
    1502             :       }
    1503           0 :       if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    1504             :       {
    1505           0 :         GNUNET_break (0);
    1506           0 :         qs = GNUNET_DB_STATUS_HARD_ERROR;
    1507             :       }
    1508           0 :       rc->qs = qs;
    1509             :     }
    1510             :     else
    1511             :     {
    1512             :       /* Check that exchange's balance matches our expected balance for the reserve */
    1513          68 :       if (0 != TALER_amount_cmp (&rs->curr_balance.reserve_balance,
    1514             :                                  &reserve.balance))
    1515             :       {
    1516             :         struct TALER_Amount delta;
    1517             : 
    1518           5 :         if (0 < TALER_amount_cmp (&rs->curr_balance.reserve_balance,
    1519             :                                   &reserve.balance))
    1520             :         {
    1521             :           /* balance > reserve.balance */
    1522           4 :           TALER_ARL_amount_subtract (&delta,
    1523             :                                      &rs->curr_balance.reserve_balance,
    1524             :                                      &reserve.balance);
    1525           4 :           TALER_ARL_amount_add (&TALER_ARL_USE_AB (
    1526             :                                   total_balance_summary_delta_plus),
    1527             :                                 &TALER_ARL_USE_AB (
    1528             :                                   total_balance_summary_delta_plus),
    1529             :                                 &delta);
    1530             :         }
    1531             :         else
    1532             :         {
    1533             :           /* balance < reserve.balance */
    1534           1 :           TALER_ARL_amount_subtract (&delta,
    1535             :                                      &reserve.balance,
    1536             :                                      &rs->curr_balance.reserve_balance);
    1537           1 :           TALER_ARL_amount_add (&TALER_ARL_USE_AB (
    1538             :                                   total_balance_summary_delta_minus),
    1539             :                                 &TALER_ARL_USE_AB (
    1540             :                                   total_balance_summary_delta_minus),
    1541             :                                 &delta);
    1542             :         }
    1543             : 
    1544             :         {
    1545           5 :           struct TALER_AUDITORDB_ReserveBalanceInsufficientInconsistency rbiig =
    1546             :           {
    1547             :             .reserve_pub = rs->reserve_pub.eddsa_pub,
    1548             :             .inconsistency_amount = nbalance,
    1549             :             .inconsistency_gain = true
    1550             :           };
    1551             : 
    1552           5 :           qs = TALER_ARL_adb->insert_reserve_balance_insufficient_inconsistency
    1553             :                (
    1554           5 :             TALER_ARL_adb->cls,
    1555             :             &rbiig);
    1556             :         }
    1557           5 :         if (qs < 0)
    1558             :         {
    1559           0 :           GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1560           0 :           rc->qs = qs;
    1561           0 :           return GNUNET_SYSERR;
    1562             :         }
    1563             : 
    1564             :         {
    1565           5 :           struct TALER_AUDITORDB_ReserveBalanceSummaryWrongInconsistency rbswi =
    1566             :           {
    1567             :             .exchange_amount = reserve.balance,
    1568             :             .auditor_amount = rs->curr_balance.reserve_balance,
    1569             :             .reserve_pub = rs->reserve_pub
    1570             :           };
    1571             : 
    1572           5 :           qs = TALER_ARL_adb->insert_reserve_balance_summary_wrong_inconsistency
    1573             :                (
    1574           5 :             TALER_ARL_adb->cls,
    1575             :             &rbswi);
    1576             :         }
    1577           5 :         if (qs < 0)
    1578             :         {
    1579           0 :           GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1580           0 :           rc->qs = qs;
    1581           0 :           return GNUNET_SYSERR;
    1582             :         }
    1583             :       }
    1584             :     }
    1585             :   }   /* end of 'if (internal_checks)' */
    1586             : 
    1587             :   /* Check that reserve is being closed if it is past its expiration date
    1588             :      (and the closing fee would not exceed the remaining balance) */
    1589          68 :   if (GNUNET_TIME_relative_cmp (CLOSING_GRACE_PERIOD,
    1590             :                                 <,
    1591             :                                 GNUNET_TIME_absolute_get_duration (
    1592             :                                   rs->a_expiration_date.abs_time)))
    1593             :   {
    1594             :     /* Reserve is expired */
    1595             :     struct TALER_Amount cfee;
    1596             : 
    1597           6 :     if ( (NULL != rs->sender_account.full_payto) &&
    1598             :          (GNUNET_OK ==
    1599           3 :           get_closing_fee (rs->sender_account,
    1600             :                            rs->a_expiration_date,
    1601             :                            &cfee)) )
    1602             :     {
    1603             :       /* We got the closing fee */
    1604           3 :       if (1 == TALER_amount_cmp (&nbalance,
    1605             :                                  &cfee))
    1606             :       {
    1607           1 :         struct TALER_AUDITORDB_ReserveNotClosedInconsistency rnci = {
    1608             :           .reserve_pub = rs->reserve_pub,
    1609             :           .expiration_time = rs->a_expiration_date.abs_time,
    1610             :           .balance = nbalance,
    1611           1 :           .diagnostic = rs->sender_account.full_payto
    1612             :         };
    1613             : 
    1614             :         /* remaining balance (according to us) exceeds closing fee */
    1615           1 :         TALER_ARL_amount_add (&TALER_ARL_USE_AB (
    1616             :                                 total_balance_reserve_not_closed),
    1617             :                               &TALER_ARL_USE_AB (
    1618             :                                 total_balance_reserve_not_closed),
    1619             :                               &rnci.balance);
    1620           1 :         qs = TALER_ARL_adb->insert_reserve_not_closed_inconsistency (
    1621           1 :           TALER_ARL_adb->cls,
    1622             :           &rnci);
    1623           1 :         if (qs < 0)
    1624             :         {
    1625           0 :           GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1626           0 :           rc->qs = qs;
    1627           0 :           return GNUNET_SYSERR;
    1628             :         }
    1629             :       }
    1630             :     }
    1631             :     else
    1632             :     {
    1633             :       /* We failed to determine the closing fee, complain! */
    1634           0 :       struct TALER_AUDITORDB_ReserveNotClosedInconsistency rncid = {
    1635             :         .reserve_pub = rs->reserve_pub,
    1636             :         .balance = nbalance,
    1637             :         .expiration_time = rs->a_expiration_date.abs_time,
    1638             :         .diagnostic = (char *) "could not determine closing fee"
    1639             :       };
    1640             : 
    1641             :       /* Even if we don't know the closing fee, update the
    1642             :          total_balance_reserve_not_closed */
    1643           0 :       TALER_ARL_amount_add (&TALER_ARL_USE_AB (
    1644             :                               total_balance_reserve_not_closed),
    1645             :                             &TALER_ARL_USE_AB (
    1646             :                               total_balance_reserve_not_closed),
    1647             :                             &nbalance);
    1648           0 :       qs = TALER_ARL_adb->insert_reserve_not_closed_inconsistency (
    1649           0 :         TALER_ARL_adb->cls,
    1650             :         &rncid);
    1651           0 :       if (qs < 0)
    1652             :       {
    1653           0 :         GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1654           0 :         rc->qs = qs;
    1655           0 :         return GNUNET_SYSERR;
    1656             :       }
    1657             :     }
    1658             :   }
    1659             :   /* We already computed the 'new' balance in 'curr_balance'
    1660             :      to include the previous balance, so this one is just
    1661             :      an assignment, not adding up! */
    1662          68 :   rs->prev_balance.reserve_balance = rs->curr_balance.reserve_balance;
    1663             : 
    1664             :   /* Add up new totals to previous totals  */
    1665          68 :   TALER_ARL_amount_add (&rs->prev_balance.reserve_loss,
    1666             :                         &rs->prev_balance.reserve_loss,
    1667             :                         &rs->curr_balance.reserve_loss);
    1668          68 :   TALER_ARL_amount_add (&rs->prev_balance.withdraw_fee_balance,
    1669             :                         &rs->prev_balance.withdraw_fee_balance,
    1670             :                         &rs->curr_balance.withdraw_fee_balance);
    1671          68 :   TALER_ARL_amount_add (&rs->prev_balance.close_fee_balance,
    1672             :                         &rs->prev_balance.close_fee_balance,
    1673             :                         &rs->curr_balance.close_fee_balance);
    1674          68 :   TALER_ARL_amount_add (&rs->prev_balance.purse_fee_balance,
    1675             :                         &rs->prev_balance.purse_fee_balance,
    1676             :                         &rs->curr_balance.purse_fee_balance);
    1677          68 :   TALER_ARL_amount_add (&rs->prev_balance.open_fee_balance,
    1678             :                         &rs->prev_balance.open_fee_balance,
    1679             :                         &rs->curr_balance.open_fee_balance);
    1680          68 :   TALER_ARL_amount_add (&rs->prev_balance.history_fee_balance,
    1681             :                         &rs->prev_balance.history_fee_balance,
    1682             :                         &rs->curr_balance.history_fee_balance);
    1683             :   /* Update global balance: add incoming first, then try
    1684             :      to subtract outgoing... */
    1685          68 :   TALER_ARL_amount_add (&TALER_ARL_USE_AB (reserves_reserve_total_balance),
    1686             :                         &TALER_ARL_USE_AB (reserves_reserve_total_balance),
    1687             :                         &rs->total_in);
    1688             :   {
    1689             :     struct TALER_Amount r;
    1690             : 
    1691          68 :     if (TALER_ARL_SR_INVALID_NEGATIVE ==
    1692          68 :         TALER_ARL_amount_subtract_neg (&r,
    1693             :                                        &TALER_ARL_USE_AB (
    1694             :                                          reserves_reserve_total_balance),
    1695             :                                        &rs->total_out))
    1696             :     {
    1697             :       /* We could not reduce our total balance, i.e. exchange allowed IN TOTAL (!)
    1698             :          to be withdrawn more than it was IN TOTAL ever given (exchange balance
    1699             :          went negative!).  Woopsie. Calculate how badly it went and log. */
    1700           1 :       report_amount_arithmetic_inconsistency ("global escrow balance",
    1701             :                                               0,
    1702             :                                               &TALER_ARL_USE_AB (
    1703             :                                                 reserves_reserve_total_balance),                   /* what we had */
    1704           1 :                                               &rs->total_out,   /* what we needed */
    1705             :                                               0 /* specific profit/loss does not apply to the total summary */
    1706             :                                               );
    1707           1 :       if (global_qs < 0)
    1708           0 :         return GNUNET_SYSERR;
    1709             :       /* We unexpectedly went negative, so a sane value to continue from
    1710             :          would be zero. */
    1711           1 :       GNUNET_assert (GNUNET_OK ==
    1712             :                      TALER_amount_set_zero (TALER_ARL_currency,
    1713             :                                             &TALER_ARL_USE_AB (
    1714             :                                               reserves_reserve_total_balance)));
    1715             :     }
    1716             :     else
    1717             :     {
    1718          67 :       TALER_ARL_USE_AB (reserves_reserve_total_balance) = r;
    1719             :     }
    1720             :   }
    1721          68 :   if (TALER_amount_is_zero (&rs->prev_balance.reserve_balance))
    1722             :   {
    1723             :     /* balance is zero, drop reserve details (and then do not update/insert) */
    1724           3 :     if (rs->had_ri)
    1725             :     {
    1726           0 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1727             :                   "Final balance of reserve `%s' is zero, dropping it\n",
    1728             :                   TALER_B2S (&rs->reserve_pub));
    1729           0 :       qs = TALER_ARL_adb->del_reserve_info (TALER_ARL_adb->cls,
    1730           0 :                                             &rs->reserve_pub);
    1731           0 :       if (0 >= qs)
    1732             :       {
    1733           0 :         GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1734           0 :         ret = GNUNET_SYSERR;
    1735           0 :         rc->qs = qs;
    1736             :       }
    1737             :     }
    1738             :     else
    1739             :     {
    1740           3 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1741             :                   "Final balance of reserve `%s' is zero, no need to remember it\n",
    1742             :                   TALER_B2S (&rs->reserve_pub));
    1743             :     }
    1744             :   }
    1745             :   else
    1746             :   {
    1747             :     /* balance is non-zero, persist for future audits */
    1748          65 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1749             :                 "Remembering final balance of reserve `%s' as %s\n",
    1750             :                 TALER_B2S (&rs->reserve_pub),
    1751             :                 TALER_amount2s (&rs->prev_balance.reserve_balance));
    1752          65 :     if (rs->had_ri)
    1753           0 :       qs = TALER_ARL_adb->update_reserve_info (TALER_ARL_adb->cls,
    1754           0 :                                                &rs->reserve_pub,
    1755           0 :                                                &rs->prev_balance,
    1756             :                                                rs->a_expiration_date);
    1757             :     else
    1758          65 :       qs = TALER_ARL_adb->insert_reserve_info (TALER_ARL_adb->cls,
    1759          65 :                                                &rs->reserve_pub,
    1760          65 :                                                &rs->prev_balance,
    1761             :                                                rs->a_expiration_date,
    1762             :                                                rs->sender_account);
    1763          65 :     if (0 >= qs)
    1764             :     {
    1765           0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1766           0 :       ret = GNUNET_SYSERR;
    1767           0 :       rc->qs = qs;
    1768             :     }
    1769             :   }
    1770             :   /* now we can discard the cached entry */
    1771          68 :   GNUNET_assert (GNUNET_YES ==
    1772             :                  GNUNET_CONTAINER_multihashmap_remove (rc->reserves,
    1773             :                                                        key,
    1774             :                                                        rs));
    1775          68 :   GNUNET_free (rs->sender_account.full_payto);
    1776          68 :   GNUNET_free (rs);
    1777          68 :   return ret;
    1778             : }
    1779             : 
    1780             : 
    1781             : #define CHECK_DB() do {                                       \
    1782             :           if (qs < 0) {                                       \
    1783             :             GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs); \
    1784             :             goto cleanup;                                     \
    1785             :           }                                                   \
    1786             :           if (global_qs < 0) {                                \
    1787             :             GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == global_qs); \
    1788             :             qs = global_qs;                                          \
    1789             :             goto cleanup;                                            \
    1790             :           }                                                          \
    1791             :           if (rc.qs < 0) {                                           \
    1792             :             GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == rc.qs);     \
    1793             :             qs = rc.qs;                                              \
    1794             :             goto cleanup;                                            \
    1795             :           }                                                          \
    1796             : } while (0)
    1797             : 
    1798             : 
    1799             : /**
    1800             :  * Analyze reserves for being well-formed.
    1801             :  *
    1802             :  * @param cls NULL
    1803             :  * @return transaction status code
    1804             :  */
    1805             : static enum GNUNET_DB_QueryStatus
    1806          74 : analyze_reserves (void *cls)
    1807             : {
    1808          74 :   struct ReserveContext rc = {
    1809             :     .qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT
    1810             :   };
    1811             :   enum GNUNET_DB_QueryStatus qs;
    1812             : 
    1813             :   (void) cls;
    1814          74 :   global_qs = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
    1815          74 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1816             :               "Analyzing reserves\n");
    1817          74 :   qs = TALER_ARL_adb->get_auditor_progress (
    1818          74 :     TALER_ARL_adb->cls,
    1819             :     TALER_ARL_GET_PP (reserves_reserve_in_serial_id),
    1820             :     TALER_ARL_GET_PP (reserves_withdraw_serial_id),
    1821             :     TALER_ARL_GET_PP (reserves_reserve_recoup_serial_id),
    1822             :     TALER_ARL_GET_PP (reserves_reserve_open_serial_id),
    1823             :     TALER_ARL_GET_PP (reserves_reserve_close_serial_id),
    1824             :     TALER_ARL_GET_PP (reserves_purse_decisions_serial_id),
    1825             :     TALER_ARL_GET_PP (reserves_account_merges_serial_id),
    1826             :     TALER_ARL_GET_PP (reserves_history_requests_serial_id),
    1827             :     NULL);
    1828          74 :   if (0 > qs)
    1829             :   {
    1830           0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1831           0 :     return qs;
    1832             :   }
    1833          74 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    1834             :   {
    1835           0 :     GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
    1836             :                 "First analysis using this auditor, starting audit from scratch\n");
    1837             :   }
    1838             :   else
    1839             :   {
    1840          74 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1841             :                 "Resuming reserve audit at %llu/%llu/%llu/%llu/%llu/%llu/%llu/%llu\n",
    1842             :                 (unsigned long long) TALER_ARL_USE_PP (
    1843             :                   reserves_reserve_in_serial_id),
    1844             :                 (unsigned long long) TALER_ARL_USE_PP (
    1845             :                   reserves_withdraw_serial_id),
    1846             :                 (unsigned long long) TALER_ARL_USE_PP (
    1847             :                   reserves_reserve_recoup_serial_id),
    1848             :                 (unsigned long long) TALER_ARL_USE_PP (
    1849             :                   reserves_reserve_open_serial_id),
    1850             :                 (unsigned long long) TALER_ARL_USE_PP (
    1851             :                   reserves_reserve_close_serial_id),
    1852             :                 (unsigned long long) TALER_ARL_USE_PP (
    1853             :                   reserves_purse_decisions_serial_id),
    1854             :                 (unsigned long long) TALER_ARL_USE_PP (
    1855             :                   reserves_account_merges_serial_id),
    1856             :                 (unsigned long long) TALER_ARL_USE_PP (
    1857             :                   reserves_history_requests_serial_id));
    1858             :   }
    1859          74 :   qs = TALER_ARL_adb->get_balance (
    1860          74 :     TALER_ARL_adb->cls,
    1861             :     TALER_ARL_GET_AB (reserves_reserve_total_balance),
    1862             :     TALER_ARL_GET_AB (reserves_reserve_loss),
    1863             :     TALER_ARL_GET_AB (reserves_withdraw_fee_revenue),
    1864             :     TALER_ARL_GET_AB (reserves_close_fee_revenue),
    1865             :     TALER_ARL_GET_AB (reserves_purse_fee_revenue),
    1866             :     TALER_ARL_GET_AB (reserves_open_fee_revenue),
    1867             :     TALER_ARL_GET_AB (reserves_history_fee_revenue),
    1868             :     TALER_ARL_GET_AB (reserves_total_bad_sig_loss),
    1869             :     TALER_ARL_GET_AB (total_balance_reserve_not_closed),
    1870             :     TALER_ARL_GET_AB (reserves_total_arithmetic_delta_plus),
    1871             :     TALER_ARL_GET_AB (reserves_total_arithmetic_delta_minus),
    1872             :     TALER_ARL_GET_AB (total_balance_summary_delta_plus),
    1873             :     TALER_ARL_GET_AB (total_balance_summary_delta_minus),
    1874             :     NULL);
    1875          74 :   if (qs < 0)
    1876             :   {
    1877           0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1878           0 :     return qs;
    1879             :   }
    1880          74 :   rc.reserves = GNUNET_CONTAINER_multihashmap_create (512,
    1881             :                                                       GNUNET_NO);
    1882          74 :   rc.revoked = GNUNET_CONTAINER_multihashmap_create (4,
    1883             :                                                      GNUNET_NO);
    1884             : 
    1885          74 :   qs = TALER_ARL_edb->select_reserves_in_above_serial_id (
    1886          74 :     TALER_ARL_edb->cls,
    1887             :     TALER_ARL_USE_PP (reserves_reserve_in_serial_id),
    1888             :     &handle_reserve_in,
    1889             :     &rc);
    1890          74 :   CHECK_DB ();
    1891          74 :   qs = TALER_ARL_edb->select_withdrawals_above_serial_id (
    1892          74 :     TALER_ARL_edb->cls,
    1893             :     TALER_ARL_USE_PP (reserves_withdraw_serial_id),
    1894             :     &handle_withdrawals,
    1895             :     &rc);
    1896          74 :   CHECK_DB ();
    1897          74 :   qs = TALER_ARL_edb->select_recoup_above_serial_id (
    1898          74 :     TALER_ARL_edb->cls,
    1899             :     TALER_ARL_USE_PP (reserves_reserve_recoup_serial_id),
    1900             :     &handle_recoup_by_reserve,
    1901             :     &rc);
    1902          74 :   if ( (qs < 0) ||
    1903          74 :        (rc.qs < 0) ||
    1904          74 :        (global_qs < 0) )
    1905             :   {
    1906           0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1907           0 :     return qs;
    1908             :   }
    1909             : 
    1910          74 :   qs = TALER_ARL_edb->select_reserve_open_above_serial_id (
    1911          74 :     TALER_ARL_edb->cls,
    1912             :     TALER_ARL_USE_PP (reserves_reserve_open_serial_id),
    1913             :     &handle_reserve_open,
    1914             :     &rc);
    1915          74 :   CHECK_DB ();
    1916          74 :   qs = TALER_ARL_edb->select_reserve_closed_above_serial_id (
    1917          74 :     TALER_ARL_edb->cls,
    1918             :     TALER_ARL_USE_PP (reserves_reserve_close_serial_id),
    1919             :     &handle_reserve_closed,
    1920             :     &rc);
    1921          74 :   CHECK_DB ();
    1922             :   /* process purse_decisions (to credit reserve) */
    1923          74 :   qs = TALER_ARL_edb->select_purse_decisions_above_serial_id (
    1924          74 :     TALER_ARL_edb->cls,
    1925             :     TALER_ARL_USE_PP (reserves_purse_decisions_serial_id),
    1926             :     false,      /* only go for merged purses! */
    1927             :     &purse_decision_cb,
    1928             :     &rc);
    1929          74 :   CHECK_DB ();
    1930             :   /* Charge purse fee! */
    1931             : 
    1932          74 :   qs = TALER_ARL_edb->select_account_merges_above_serial_id (
    1933          74 :     TALER_ARL_edb->cls,
    1934             :     TALER_ARL_USE_PP (reserves_account_merges_serial_id),
    1935             :     &handle_account_merged,
    1936             :     &rc);
    1937          74 :   CHECK_DB ();
    1938          74 :   GNUNET_CONTAINER_multihashmap_iterate (rc.reserves,
    1939             :                                          &verify_reserve_balance,
    1940             :                                          &rc);
    1941          74 :   CHECK_DB ();
    1942          74 :   GNUNET_break (0 ==
    1943             :                 GNUNET_CONTAINER_multihashmap_size (rc.reserves));
    1944             : 
    1945          74 :   qs = TALER_ARL_adb->insert_balance (
    1946          74 :     TALER_ARL_adb->cls,
    1947             :     TALER_ARL_SET_AB (reserves_reserve_total_balance),
    1948             :     TALER_ARL_SET_AB (reserves_reserve_loss),
    1949             :     TALER_ARL_SET_AB (reserves_withdraw_fee_revenue),
    1950             :     TALER_ARL_SET_AB (reserves_close_fee_revenue),
    1951             :     TALER_ARL_SET_AB (reserves_purse_fee_revenue),
    1952             :     TALER_ARL_SET_AB (reserves_open_fee_revenue),
    1953             :     TALER_ARL_SET_AB (reserves_history_fee_revenue),
    1954             :     TALER_ARL_SET_AB (reserves_total_bad_sig_loss),
    1955             :     TALER_ARL_SET_AB (total_balance_reserve_not_closed),
    1956             :     TALER_ARL_SET_AB (reserves_total_arithmetic_delta_plus),
    1957             :     TALER_ARL_SET_AB (reserves_total_arithmetic_delta_minus),
    1958             :     TALER_ARL_SET_AB (total_balance_summary_delta_plus),
    1959             :     TALER_ARL_SET_AB (total_balance_summary_delta_minus),
    1960             :     NULL);
    1961          74 :   if (0 > qs)
    1962             :   {
    1963           2 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1964           2 :     goto cleanup;
    1965             :   }
    1966             : 
    1967          72 :   qs = TALER_ARL_adb->update_balance (
    1968          72 :     TALER_ARL_adb->cls,
    1969             :     TALER_ARL_SET_AB (reserves_reserve_total_balance),
    1970             :     TALER_ARL_SET_AB (reserves_reserve_loss),
    1971             :     TALER_ARL_SET_AB (reserves_withdraw_fee_revenue),
    1972             :     TALER_ARL_SET_AB (reserves_close_fee_revenue),
    1973             :     TALER_ARL_SET_AB (reserves_purse_fee_revenue),
    1974             :     TALER_ARL_SET_AB (reserves_open_fee_revenue),
    1975             :     TALER_ARL_SET_AB (reserves_history_fee_revenue),
    1976             :     TALER_ARL_SET_AB (reserves_total_bad_sig_loss),
    1977             :     TALER_ARL_SET_AB (total_balance_reserve_not_closed),
    1978             :     TALER_ARL_SET_AB (reserves_total_arithmetic_delta_plus),
    1979             :     TALER_ARL_SET_AB (reserves_total_arithmetic_delta_minus),
    1980             :     TALER_ARL_SET_AB (total_balance_summary_delta_plus),
    1981             :     TALER_ARL_SET_AB (total_balance_summary_delta_minus),
    1982             :     NULL);
    1983          72 :   if (0 > qs)
    1984             :   {
    1985           0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1986           0 :     goto cleanup;
    1987             :   }
    1988             : 
    1989          72 :   qs = TALER_ARL_adb->insert_auditor_progress (
    1990          72 :     TALER_ARL_adb->cls,
    1991             :     TALER_ARL_SET_PP (reserves_reserve_in_serial_id),
    1992             :     TALER_ARL_SET_PP (reserves_withdraw_serial_id),
    1993             :     TALER_ARL_SET_PP (reserves_reserve_recoup_serial_id),
    1994             :     TALER_ARL_SET_PP (reserves_reserve_open_serial_id),
    1995             :     TALER_ARL_SET_PP (reserves_reserve_close_serial_id),
    1996             :     TALER_ARL_SET_PP (reserves_purse_decisions_serial_id),
    1997             :     TALER_ARL_SET_PP (reserves_account_merges_serial_id),
    1998             :     TALER_ARL_SET_PP (reserves_history_requests_serial_id),
    1999             :     NULL);
    2000          72 :   if (0 > qs)
    2001             :   {
    2002           0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    2003             :                 "Failed to update auditor DB, not recording progress\n");
    2004           0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    2005           0 :     goto cleanup;
    2006             :   }
    2007          72 :   qs = TALER_ARL_adb->update_auditor_progress (
    2008          72 :     TALER_ARL_adb->cls,
    2009             :     TALER_ARL_SET_PP (reserves_reserve_in_serial_id),
    2010             :     TALER_ARL_SET_PP (reserves_withdraw_serial_id),
    2011             :     TALER_ARL_SET_PP (reserves_reserve_recoup_serial_id),
    2012             :     TALER_ARL_SET_PP (reserves_reserve_open_serial_id),
    2013             :     TALER_ARL_SET_PP (reserves_reserve_close_serial_id),
    2014             :     TALER_ARL_SET_PP (reserves_purse_decisions_serial_id),
    2015             :     TALER_ARL_SET_PP (reserves_account_merges_serial_id),
    2016             :     TALER_ARL_SET_PP (reserves_history_requests_serial_id),
    2017             :     NULL);
    2018          72 :   if (0 > qs)
    2019             :   {
    2020           0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    2021             :                 "Failed to update auditor DB, not recording progress\n");
    2022           0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    2023           0 :     goto cleanup;
    2024             :   }
    2025             : 
    2026          72 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    2027             :               "Concluded reserve audit step at %llu/%llu/%llu/%llu/%llu/%llu/%llu/%llu\n",
    2028             :               (unsigned long long) TALER_ARL_USE_PP (
    2029             :                 reserves_reserve_in_serial_id),
    2030             :               (unsigned long long) TALER_ARL_USE_PP (
    2031             :                 reserves_withdraw_serial_id),
    2032             :               (unsigned long long) TALER_ARL_USE_PP (
    2033             :                 reserves_reserve_recoup_serial_id),
    2034             :               (unsigned long long) TALER_ARL_USE_PP (
    2035             :                 reserves_reserve_open_serial_id),
    2036             :               (unsigned long long) TALER_ARL_USE_PP (
    2037             :                 reserves_reserve_close_serial_id),
    2038             :               (unsigned long long) TALER_ARL_USE_PP (
    2039             :                 reserves_purse_decisions_serial_id),
    2040             :               (unsigned long long) TALER_ARL_USE_PP (
    2041             :                 reserves_account_merges_serial_id),
    2042             :               (unsigned long long) TALER_ARL_USE_PP (
    2043             :                 reserves_history_requests_serial_id));
    2044          72 :   qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
    2045          74 : cleanup:
    2046          74 :   GNUNET_CONTAINER_multihashmap_destroy (rc.reserves);
    2047          74 :   GNUNET_CONTAINER_multihashmap_destroy (rc.revoked);
    2048          74 :   return qs;
    2049             : }
    2050             : 
    2051             : 
    2052             : #undef CHECK_DB
    2053             : 
    2054             : 
    2055             : /**
    2056             :  * Function called on events received from Postgres.
    2057             :  *
    2058             :  * @param cls closure, NULL
    2059             :  * @param extra additional event data provided
    2060             :  * @param extra_size number of bytes in @a extra
    2061             :  */
    2062             : static void
    2063           0 : db_notify (void *cls,
    2064             :            const void *extra,
    2065             :            size_t extra_size)
    2066             : {
    2067             :   (void) cls;
    2068             :   (void) extra;
    2069             :   (void) extra_size;
    2070             : 
    2071           0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    2072             :               "Received notification to wake reserves helper\n");
    2073           0 :   if (GNUNET_OK !=
    2074           0 :       TALER_ARL_setup_sessions_and_run (&analyze_reserves,
    2075             :                                         NULL))
    2076             :   {
    2077           0 :     GNUNET_SCHEDULER_shutdown ();
    2078           0 :     global_ret = EXIT_FAILURE;
    2079           0 :     return;
    2080             :   }
    2081             : }
    2082             : 
    2083             : 
    2084             : /**
    2085             :  * Function called on shutdown.
    2086             :  */
    2087             : static void
    2088          74 : do_shutdown (void *cls)
    2089             : {
    2090             :   (void) cls;
    2091          74 :   if (NULL != eh)
    2092             :   {
    2093           6 :     TALER_ARL_adb->event_listen_cancel (eh);
    2094           6 :     eh = NULL;
    2095             :   }
    2096          74 :   TALER_ARL_done ();
    2097          74 : }
    2098             : 
    2099             : 
    2100             : /**
    2101             :  * Main function that will be run.
    2102             :  *
    2103             :  * @param cls closure
    2104             :  * @param args remaining command-line arguments
    2105             :  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
    2106             :  * @param c configuration
    2107             :  */
    2108             : static void
    2109          74 : run (void *cls,
    2110             :      char *const *args,
    2111             :      const char *cfgfile,
    2112             :      const struct GNUNET_CONFIGURATION_Handle *c)
    2113             : {
    2114             :   (void) cls;
    2115             :   (void) args;
    2116             :   (void) cfgfile;
    2117             : 
    2118          74 :   cfg = c;
    2119          74 :   GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
    2120             :                                  NULL);
    2121          74 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    2122             :               "Launching reserves auditor\n");
    2123          74 :   if (GNUNET_OK !=
    2124          74 :       TALER_ARL_init (c))
    2125             :   {
    2126           0 :     global_ret = EXIT_FAILURE;
    2127           0 :     return;
    2128             :   }
    2129          74 :   if (GNUNET_OK !=
    2130          74 :       GNUNET_CONFIGURATION_get_value_time (TALER_ARL_cfg,
    2131             :                                            "exchangedb",
    2132             :                                            "IDLE_RESERVE_EXPIRATION_TIME",
    2133             :                                            &idle_reserve_expiration_time))
    2134             :   {
    2135           0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    2136             :                                "exchangedb",
    2137             :                                "IDLE_RESERVE_EXPIRATION_TIME");
    2138           0 :     GNUNET_SCHEDULER_shutdown ();
    2139           0 :     global_ret = EXIT_FAILURE;
    2140           0 :     return;
    2141             :   }
    2142          74 :   if (test_mode != 1)
    2143             :   {
    2144           6 :     struct GNUNET_DB_EventHeaderP es = {
    2145           6 :       .size = htons (sizeof (es)),
    2146           6 :       .type = htons (TALER_DBEVENT_EXCHANGE_AUDITOR_WAKE_HELPER_RESERVES)
    2147             :     };
    2148             : 
    2149           6 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    2150             :                 "Running helper indefinitely\n");
    2151           6 :     eh = TALER_ARL_adb->event_listen (TALER_ARL_adb->cls,
    2152             :                                       &es,
    2153           6 :                                       GNUNET_TIME_UNIT_FOREVER_REL,
    2154             :                                       &db_notify,
    2155             :                                       NULL);
    2156             :   }
    2157          74 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    2158             :               "Starting audit\n");
    2159          74 :   if (GNUNET_OK !=
    2160          74 :       TALER_ARL_setup_sessions_and_run (&analyze_reserves,
    2161             :                                         NULL))
    2162             :   {
    2163           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    2164             :                 "Audit failed\n");
    2165           0 :     GNUNET_SCHEDULER_shutdown ();
    2166           0 :     global_ret = EXIT_FAILURE;
    2167           0 :     return;
    2168             :   }
    2169             : }
    2170             : 
    2171             : 
    2172             : /**
    2173             :  * The main function to check the database's handling of reserves.
    2174             :  *
    2175             :  * @param argc number of arguments from the command line
    2176             :  * @param argv command line arguments
    2177             :  * @return 0 ok, 1 on error
    2178             :  */
    2179             : int
    2180          74 : main (int argc,
    2181             :       char *const *argv)
    2182             : {
    2183          74 :   const struct GNUNET_GETOPT_CommandLineOption options[] = {
    2184          74 :     GNUNET_GETOPT_option_flag ('i',
    2185             :                                "internal",
    2186             :                                "perform checks only applicable for exchange-internal audits",
    2187             :                                &internal_checks),
    2188          74 :     GNUNET_GETOPT_option_flag ('t',
    2189             :                                "test",
    2190             :                                "run in test mode and exit when idle",
    2191             :                                &test_mode),
    2192          74 :     GNUNET_GETOPT_option_timetravel ('T',
    2193             :                                      "timetravel"),
    2194             :     GNUNET_GETOPT_OPTION_END
    2195             :   };
    2196             :   enum GNUNET_GenericReturnValue ret;
    2197             : 
    2198          74 :   ret = GNUNET_PROGRAM_run (
    2199             :     TALER_AUDITOR_project_data (),
    2200             :     argc,
    2201             :     argv,
    2202             :     "taler-helper-auditor-reserves",
    2203             :     gettext_noop ("Audit Taler exchange reserve handling"),
    2204             :     options,
    2205             :     &run,
    2206             :     NULL);
    2207          74 :   if (GNUNET_SYSERR == ret)
    2208           0 :     return EXIT_INVALIDARGUMENT;
    2209          74 :   if (GNUNET_NO == ret)
    2210           0 :     return EXIT_SUCCESS;
    2211          74 :   return global_ret;
    2212             : }
    2213             : 
    2214             : 
    2215             : /* end of taler-helper-auditor-reserves.c */

Generated by: LCOV version 1.16