LCOV - code coverage report
Current view: top level - auditor - taler-helper-auditor-reserves.c (source / functions) Hit Total Coverage
Test: GNU Taler exchange coverage report Lines: 274 421 65.1 %
Date: 2021-08-30 06:43:37 Functions: 12 12 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   Copyright (C) 2016-2021 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 "platform.h"
      22             : #include <gnunet/gnunet_util_lib.h>
      23             : #include "taler_auditordb_plugin.h"
      24             : #include "taler_exchangedb_lib.h"
      25             : #include "taler_json_lib.h"
      26             : #include "taler_bank_service.h"
      27             : #include "taler_signatures.h"
      28             : #include "report-lib.h"
      29             : 
      30             : 
      31             : /**
      32             :  * Use a 1 day grace period to deal with clocks not being perfectly synchronized.
      33             :  */
      34             : #define CLOSING_GRACE_PERIOD GNUNET_TIME_UNIT_DAYS
      35             : 
      36             : /**
      37             :  * Return value from main().
      38             :  */
      39             : static int global_ret;
      40             : 
      41             : /**
      42             :  * After how long should idle reserves be closed?
      43             :  */
      44             : static struct GNUNET_TIME_Relative idle_reserve_expiration_time;
      45             : 
      46             : /**
      47             :  * Checkpointing our progress for reserves.
      48             :  */
      49             : static struct TALER_AUDITORDB_ProgressPointReserve ppr;
      50             : 
      51             : /**
      52             :  * Checkpointing our progress for reserves.
      53             :  */
      54             : static struct TALER_AUDITORDB_ProgressPointReserve ppr_start;
      55             : 
      56             : /**
      57             :  * Array of reports about row inconsitencies.
      58             :  */
      59             : static json_t *report_row_inconsistencies;
      60             : 
      61             : /**
      62             :  * Array of reports about the denomination key not being
      63             :  * valid at the time of withdrawal.
      64             :  */
      65             : static json_t *denomination_key_validity_withdraw_inconsistencies;
      66             : 
      67             : /**
      68             :  * Array of reports about reserve balance insufficient inconsitencies.
      69             :  */
      70             : static json_t *report_reserve_balance_insufficient_inconsistencies;
      71             : 
      72             : /**
      73             :  * Total amount reserves were charged beyond their balance.
      74             :  */
      75             : static struct TALER_Amount total_balance_insufficient_loss;
      76             : 
      77             : /**
      78             :  * Array of reports about reserve balance summary wrong in database.
      79             :  */
      80             : static json_t *report_reserve_balance_summary_wrong_inconsistencies;
      81             : 
      82             : /**
      83             :  * Total delta between expected and stored reserve balance summaries,
      84             :  * for positive deltas.
      85             :  */
      86             : static struct TALER_Amount total_balance_summary_delta_plus;
      87             : 
      88             : /**
      89             :  * Total delta between expected and stored reserve balance summaries,
      90             :  * for negative deltas.
      91             :  */
      92             : static struct TALER_Amount total_balance_summary_delta_minus;
      93             : 
      94             : /**
      95             :  * Array of reports about reserve's not being closed inconsitencies.
      96             :  */
      97             : static json_t *report_reserve_not_closed_inconsistencies;
      98             : 
      99             : /**
     100             :  * Total amount affected by reserves not having been closed on time.
     101             :  */
     102             : static struct TALER_Amount total_balance_reserve_not_closed;
     103             : 
     104             : /**
     105             :  * Report about amount calculation differences (causing profit
     106             :  * or loss at the exchange).
     107             :  */
     108             : static json_t *report_amount_arithmetic_inconsistencies;
     109             : 
     110             : /**
     111             :  * Profits the exchange made by bad amount calculations.
     112             :  */
     113             : static struct TALER_Amount total_arithmetic_delta_plus;
     114             : 
     115             : /**
     116             :  * Losses the exchange made by bad amount calculations.
     117             :  */
     118             : static struct TALER_Amount total_arithmetic_delta_minus;
     119             : 
     120             : /**
     121             :  * Expected balance in the escrow account.
     122             :  */
     123             : static struct TALER_Amount total_escrow_balance;
     124             : 
     125             : /**
     126             :  * Recoups we made on denominations that were not revoked (!?).
     127             :  */
     128             : static struct TALER_Amount total_irregular_recoups;
     129             : 
     130             : /**
     131             :  * Total withdraw fees earned.
     132             :  */
     133             : static struct TALER_Amount total_withdraw_fee_income;
     134             : 
     135             : /**
     136             :  * Array of reports about coin operations with bad signatures.
     137             :  */
     138             : static json_t *report_bad_sig_losses;
     139             : 
     140             : /**
     141             :  * Total amount lost by operations for which signatures were invalid.
     142             :  */
     143             : static struct TALER_Amount total_bad_sig_loss;
     144             : 
     145             : /**
     146             :  * Should we run checks that only work for exchange-internal audits?
     147             :  */
     148             : static int internal_checks;
     149             : 
     150             : /* ***************************** Report logic **************************** */
     151             : 
     152             : 
     153             : /**
     154             :  * Report a (serious) inconsistency in the exchange's database with
     155             :  * respect to calculations involving amounts.
     156             :  *
     157             :  * @param operation what operation had the inconsistency
     158             :  * @param rowid affected row, 0 if row is missing
     159             :  * @param exchange amount calculated by exchange
     160             :  * @param auditor amount calculated by auditor
     161             :  * @param profitable 1 if @a exchange being larger than @a auditor is
     162             :  *           profitable for the exchange for this operation,
     163             :  *           -1 if @a exchange being smaller than @a auditor is
     164             :  *           profitable for the exchange, and 0 if it is unclear
     165             :  */
     166             : static void
     167           2 : report_amount_arithmetic_inconsistency (
     168             :   const char *operation,
     169             :   uint64_t rowid,
     170             :   const struct TALER_Amount *exchange,
     171             :   const struct TALER_Amount *auditor,
     172             :   int profitable)
     173             : {
     174             :   struct TALER_Amount delta;
     175             :   struct TALER_Amount *target;
     176             : 
     177           2 :   if (0 < TALER_amount_cmp (exchange,
     178             :                             auditor))
     179             :   {
     180             :     /* exchange > auditor */
     181           0 :     TALER_ARL_amount_subtract (&delta,
     182             :                                exchange,
     183             :                                auditor);
     184             :   }
     185             :   else
     186             :   {
     187             :     /* auditor < exchange */
     188           2 :     profitable = -profitable;
     189           2 :     TALER_ARL_amount_subtract (&delta,
     190             :                                auditor,
     191             :                                exchange);
     192             :   }
     193           2 :   TALER_ARL_report (report_amount_arithmetic_inconsistencies,
     194           2 :                     GNUNET_JSON_PACK (
     195             :                       GNUNET_JSON_pack_string ("operation",
     196             :                                                operation),
     197             :                       GNUNET_JSON_pack_uint64 ("rowid",
     198             :                                                rowid),
     199             :                       TALER_JSON_pack_amount ("exchange",
     200             :                                               exchange),
     201             :                       TALER_JSON_pack_amount ("auditor",
     202             :                                               auditor),
     203             :                       GNUNET_JSON_pack_int64 ("profitable",
     204             :                                               profitable)));
     205           2 :   if (0 != profitable)
     206             :   {
     207           0 :     target = (1 == profitable)
     208             :              ? &total_arithmetic_delta_plus
     209           0 :              : &total_arithmetic_delta_minus;
     210           0 :     TALER_ARL_amount_add (target,
     211             :                           target,
     212             :                           &delta);
     213             :   }
     214           2 : }
     215             : 
     216             : 
     217             : /**
     218             :  * Report a (serious) inconsistency in the exchange's database.
     219             :  *
     220             :  * @param table affected table
     221             :  * @param rowid affected row, 0 if row is missing
     222             :  * @param diagnostic message explaining the problem
     223             :  */
     224             : static void
     225           2 : report_row_inconsistency (const char *table,
     226             :                           uint64_t rowid,
     227             :                           const char *diagnostic)
     228             : {
     229           2 :   TALER_ARL_report (report_row_inconsistencies,
     230           2 :                     GNUNET_JSON_PACK (
     231             :                       GNUNET_JSON_pack_string ("table",
     232             :                                                table),
     233             :                       GNUNET_JSON_pack_uint64 ("row",
     234             :                                                rowid),
     235             :                       GNUNET_JSON_pack_string ("diagnostic",
     236             :                                                diagnostic)));
     237           2 : }
     238             : 
     239             : 
     240             : /* ***************************** Analyze reserves ************************ */
     241             : /* This logic checks the reserves_in, reserves_out and reserves-tables */
     242             : 
     243             : /**
     244             :  * Summary data we keep per reserve.
     245             :  */
     246             : struct ReserveSummary
     247             : {
     248             :   /**
     249             :    * Public key of the reserve.
     250             :    * Always set when the struct is first initialized.
     251             :    */
     252             :   struct TALER_ReservePublicKeyP reserve_pub;
     253             : 
     254             :   /**
     255             :    * Sum of all incoming transfers during this transaction.
     256             :    * Updated only in #handle_reserve_in().
     257             :    */
     258             :   struct TALER_Amount total_in;
     259             : 
     260             :   /**
     261             :    * Sum of all outgoing transfers during this transaction (includes fees).
     262             :    * Updated only in #handle_reserve_out().
     263             :    */
     264             :   struct TALER_Amount total_out;
     265             : 
     266             :   /**
     267             :    * Sum of withdraw fees encountered during this transaction.
     268             :    */
     269             :   struct TALER_Amount total_fee;
     270             : 
     271             :   /**
     272             :    * Previous balance of the reserve as remembered by the auditor.
     273             :    * (updated based on @e total_in and @e total_out at the end).
     274             :    */
     275             :   struct TALER_Amount balance_at_previous_audit;
     276             : 
     277             :   /**
     278             :    * Previous withdraw fee balance of the reserve, as remembered by the auditor.
     279             :    * (updated based on @e total_fee at the end).
     280             :    */
     281             :   struct TALER_Amount a_withdraw_fee_balance;
     282             : 
     283             :   /**
     284             :    * Previous reserve expiration data, as remembered by the auditor.
     285             :    * (updated on-the-fly in #handle_reserve_in()).
     286             :    */
     287             :   struct GNUNET_TIME_Absolute a_expiration_date;
     288             : 
     289             :   /**
     290             :    * Which account did originally put money into the reserve?
     291             :    */
     292             :   char *sender_account;
     293             : 
     294             :   /**
     295             :    * Did we have a previous reserve info?  Used to decide between
     296             :    * UPDATE and INSERT later.  Initialized in
     297             :    * #load_auditor_reserve_summary() together with the a-* values
     298             :    * (if available).
     299             :    */
     300             :   int had_ri;
     301             : 
     302             : };
     303             : 
     304             : 
     305             : /**
     306             :  * Load the auditor's remembered state about the reserve into @a rs.
     307             :  * The "total_in" and "total_out" amounts of @a rs must already be
     308             :  * initialized (so we can determine the currency).
     309             :  *
     310             :  * @param[in,out] rs reserve summary to (fully) initialize
     311             :  * @return transaction status code
     312             :  */
     313             : static enum GNUNET_DB_QueryStatus
     314          83 : load_auditor_reserve_summary (struct ReserveSummary *rs)
     315             : {
     316             :   enum GNUNET_DB_QueryStatus qs;
     317             :   uint64_t rowid;
     318             : 
     319          83 :   qs = TALER_ARL_adb->get_reserve_info (TALER_ARL_adb->cls,
     320          83 :                                         &rs->reserve_pub,
     321             :                                         &TALER_ARL_master_pub,
     322             :                                         &rowid,
     323             :                                         &rs->balance_at_previous_audit,
     324             :                                         &rs->a_withdraw_fee_balance,
     325             :                                         &rs->a_expiration_date,
     326             :                                         &rs->sender_account);
     327          83 :   if (0 > qs)
     328             :   {
     329           0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     330           0 :     return qs;
     331             :   }
     332          83 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
     333             :   {
     334          83 :     rs->had_ri = GNUNET_NO;
     335          83 :     GNUNET_assert (GNUNET_OK ==
     336             :                    TALER_amount_set_zero (rs->total_in.currency,
     337             :                                           &rs->balance_at_previous_audit));
     338          83 :     GNUNET_assert (GNUNET_OK ==
     339             :                    TALER_amount_set_zero (rs->total_in.currency,
     340             :                                           &rs->a_withdraw_fee_balance));
     341          83 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     342             :                 "Creating fresh reserve `%s' with starting balance %s\n",
     343             :                 TALER_B2S (&rs->reserve_pub),
     344             :                 TALER_amount2s (&rs->balance_at_previous_audit));
     345          83 :     return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
     346             :   }
     347           0 :   rs->had_ri = GNUNET_YES;
     348           0 :   if ( (GNUNET_YES !=
     349           0 :         TALER_amount_cmp_currency (&rs->balance_at_previous_audit,
     350           0 :                                    &rs->a_withdraw_fee_balance)) ||
     351             :        (GNUNET_YES !=
     352           0 :         TALER_amount_cmp_currency (&rs->total_in,
     353           0 :                                    &rs->balance_at_previous_audit)) )
     354             :   {
     355           0 :     GNUNET_break (0);
     356           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
     357             :   }
     358           0 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     359             :               "Auditor remembers reserve `%s' has balance %s\n",
     360             :               TALER_B2S (&rs->reserve_pub),
     361             :               TALER_amount2s (&rs->balance_at_previous_audit));
     362           0 :   return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
     363             : }
     364             : 
     365             : 
     366             : /**
     367             :  * Closure to the various callbacks we make while checking a reserve.
     368             :  */
     369             : struct ReserveContext
     370             : {
     371             :   /**
     372             :    * Map from hash of reserve's public key to a `struct ReserveSummary`.
     373             :    */
     374             :   struct GNUNET_CONTAINER_MultiHashMap *reserves;
     375             : 
     376             :   /**
     377             :    * Map from hash of denomination's public key to a
     378             :    * static string "revoked" for keys that have been revoked,
     379             :    * or "master signature invalid" in case the revocation is
     380             :    * there but bogus.
     381             :    */
     382             :   struct GNUNET_CONTAINER_MultiHashMap *revoked;
     383             : 
     384             :   /**
     385             :    * Transaction status code, set to error codes if applicable.
     386             :    */
     387             :   enum GNUNET_DB_QueryStatus qs;
     388             : 
     389             : };
     390             : 
     391             : 
     392             : /**
     393             :  * Function called with details about incoming wire transfers.
     394             :  *
     395             :  * @param cls our `struct ReserveContext`
     396             :  * @param rowid unique serial ID for the refresh session in our DB
     397             :  * @param reserve_pub public key of the reserve (also the WTID)
     398             :  * @param credit amount that was received
     399             :  * @param sender_account_details information about the sender's bank account
     400             :  * @param wire_reference unique reference identifying the wire transfer
     401             :  * @param execution_date when did we receive the funds
     402             :  * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
     403             :  */
     404             : static int
     405          83 : handle_reserve_in (void *cls,
     406             :                    uint64_t rowid,
     407             :                    const struct TALER_ReservePublicKeyP *reserve_pub,
     408             :                    const struct TALER_Amount *credit,
     409             :                    const char *sender_account_details,
     410             :                    uint64_t wire_reference,
     411             :                    struct GNUNET_TIME_Absolute execution_date)
     412             : {
     413          83 :   struct ReserveContext *rc = cls;
     414             :   struct GNUNET_HashCode key;
     415             :   struct ReserveSummary *rs;
     416             :   struct GNUNET_TIME_Absolute expiry;
     417             :   enum GNUNET_DB_QueryStatus qs;
     418             : 
     419             :   (void) wire_reference;
     420             :   /* should be monotonically increasing */
     421          83 :   GNUNET_assert (rowid >= ppr.last_reserve_in_serial_id);
     422          83 :   ppr.last_reserve_in_serial_id = rowid + 1;
     423             : 
     424          83 :   GNUNET_CRYPTO_hash (reserve_pub,
     425             :                       sizeof (*reserve_pub),
     426             :                       &key);
     427          83 :   rs = GNUNET_CONTAINER_multihashmap_get (rc->reserves,
     428             :                                           &key);
     429          83 :   if (NULL == rs)
     430             :   {
     431          83 :     rs = GNUNET_new (struct ReserveSummary);
     432          83 :     rs->sender_account = GNUNET_strdup (sender_account_details);
     433          83 :     rs->reserve_pub = *reserve_pub;
     434          83 :     rs->total_in = *credit;
     435          83 :     GNUNET_assert (GNUNET_OK ==
     436             :                    TALER_amount_set_zero (credit->currency,
     437             :                                           &rs->total_out));
     438          83 :     GNUNET_assert (GNUNET_OK ==
     439             :                    TALER_amount_set_zero (credit->currency,
     440             :                                           &rs->total_fee));
     441          83 :     if (0 > (qs = load_auditor_reserve_summary (rs)))
     442             :     {
     443           0 :       GNUNET_break (0);
     444           0 :       GNUNET_free (rs);
     445           0 :       rc->qs = qs;
     446           0 :       return GNUNET_SYSERR;
     447             :     }
     448          83 :     GNUNET_assert (GNUNET_OK ==
     449             :                    GNUNET_CONTAINER_multihashmap_put (rc->reserves,
     450             :                                                       &key,
     451             :                                                       rs,
     452             :                                                       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
     453             :   }
     454             :   else
     455             :   {
     456           0 :     TALER_ARL_amount_add (&rs->total_in,
     457             :                           &rs->total_in,
     458             :                           credit);
     459           0 :     if (NULL == rs->sender_account)
     460           0 :       rs->sender_account = GNUNET_strdup (sender_account_details);
     461             :   }
     462          83 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     463             :               "Additional incoming wire transfer for reserve `%s' of %s\n",
     464             :               TALER_B2S (reserve_pub),
     465             :               TALER_amount2s (credit));
     466          83 :   expiry = GNUNET_TIME_absolute_add (execution_date,
     467             :                                      idle_reserve_expiration_time);
     468          83 :   rs->a_expiration_date = GNUNET_TIME_absolute_max (rs->a_expiration_date,
     469             :                                                     expiry);
     470          83 :   if (TALER_ARL_do_abort ())
     471           0 :     return GNUNET_SYSERR;
     472          83 :   return GNUNET_OK;
     473             : }
     474             : 
     475             : 
     476             : /**
     477             :  * Function called with details about withdraw operations.  Verifies
     478             :  * the signature and updates the reserve's balance.
     479             :  *
     480             :  * @param cls our `struct ReserveContext`
     481             :  * @param rowid unique serial ID for the refresh session in our DB
     482             :  * @param h_blind_ev blinded hash of the coin's public key
     483             :  * @param denom_pub public denomination key of the deposited coin
     484             :  * @param reserve_pub public key of the reserve
     485             :  * @param reserve_sig signature over the withdraw operation
     486             :  * @param execution_date when did the wallet withdraw the coin
     487             :  * @param amount_with_fee amount that was withdrawn
     488             :  * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
     489             :  */
     490             : static int
     491        1042 : handle_reserve_out (void *cls,
     492             :                     uint64_t rowid,
     493             :                     const struct GNUNET_HashCode *h_blind_ev,
     494             :                     const struct TALER_DenominationPublicKey *denom_pub,
     495             :                     const struct TALER_ReservePublicKeyP *reserve_pub,
     496             :                     const struct TALER_ReserveSignatureP *reserve_sig,
     497             :                     struct GNUNET_TIME_Absolute execution_date,
     498             :                     const struct TALER_Amount *amount_with_fee)
     499             : {
     500        1042 :   struct ReserveContext *rc = cls;
     501             :   struct GNUNET_HashCode key;
     502             :   struct ReserveSummary *rs;
     503             :   const struct TALER_DenominationKeyValidityPS *issue;
     504             :   struct TALER_Amount withdraw_fee;
     505             :   struct TALER_Amount auditor_value;
     506             :   struct TALER_Amount auditor_amount_with_fee;
     507             :   struct GNUNET_TIME_Absolute valid_start;
     508             :   struct GNUNET_TIME_Absolute expire_withdraw;
     509             :   enum GNUNET_DB_QueryStatus qs;
     510        1042 :   struct TALER_WithdrawRequestPS wsrd = {
     511        1042 :     .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW),
     512        1042 :     .purpose.size = htonl (sizeof (wsrd)),
     513             :     .reserve_pub = *reserve_pub,
     514             :     .h_coin_envelope = *h_blind_ev
     515             :   };
     516             : 
     517             :   /* should be monotonically increasing */
     518        1042 :   GNUNET_assert (rowid >= ppr.last_reserve_out_serial_id);
     519        1042 :   ppr.last_reserve_out_serial_id = rowid + 1;
     520             : 
     521             :   /* lookup denomination pub data (make sure denom_pub is valid, establish fees);
     522             :      initializes wsrd.h_denomination_pub! */
     523        1042 :   qs = TALER_ARL_get_denomination_info (denom_pub,
     524             :                                         &issue,
     525             :                                         &wsrd.h_denomination_pub);
     526        1042 :   if (0 > qs)
     527             :   {
     528           0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     529           0 :     if (GNUNET_DB_STATUS_HARD_ERROR == qs)
     530           0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     531             :                   "Hard database error trying to get denomination %s (%s) from database!\n",
     532             :                   TALER_B2S (denom_pub),
     533             :                   TALER_amount2s (amount_with_fee));
     534           0 :     rc->qs = qs;
     535           0 :     return GNUNET_SYSERR;
     536             :   }
     537        1042 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
     538             :   {
     539           0 :     report_row_inconsistency ("withdraw",
     540             :                               rowid,
     541             :                               "denomination key not found");
     542           0 :     if (TALER_ARL_do_abort ())
     543           0 :       return GNUNET_SYSERR;
     544           0 :     return GNUNET_OK;
     545             :   }
     546             : 
     547             :   /* check that execution date is within withdraw range for denom_pub  */
     548        1042 :   valid_start = GNUNET_TIME_absolute_ntoh (issue->start);
     549        1042 :   expire_withdraw = GNUNET_TIME_absolute_ntoh (issue->expire_withdraw);
     550        1042 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     551             :               "Checking withdraw timing: %llu, expire: %llu, timing: %llu\n",
     552             :               (unsigned long long) valid_start.abs_value_us,
     553             :               (unsigned long long) expire_withdraw.abs_value_us,
     554             :               (unsigned long long) execution_date.abs_value_us);
     555        1042 :   if ( (valid_start.abs_value_us > execution_date.abs_value_us) ||
     556        1042 :        (expire_withdraw.abs_value_us < execution_date.abs_value_us) )
     557             :   {
     558           1 :     TALER_ARL_report (denomination_key_validity_withdraw_inconsistencies,
     559           1 :                       GNUNET_JSON_PACK (
     560             :                         GNUNET_JSON_pack_uint64 ("row",
     561             :                                                  rowid),
     562             :                         TALER_JSON_pack_time_abs_human ("execution_date",
     563             :                                                         execution_date),
     564             :                         GNUNET_JSON_pack_data_auto ("reserve_pub",
     565             :                                                     reserve_pub),
     566             :                         GNUNET_JSON_pack_data_auto ("denompub_h",
     567             :                                                     &wsrd.h_denomination_pub)));
     568             :   }
     569             : 
     570             :   /* check reserve_sig (first: setup remaining members of wsrd) */
     571        1042 :   TALER_amount_hton (&wsrd.amount_with_fee,
     572             :                      amount_with_fee);
     573        1042 :   if (GNUNET_OK !=
     574        1042 :       GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW,
     575             :                                   &wsrd,
     576             :                                   &reserve_sig->eddsa_signature,
     577             :                                   &reserve_pub->eddsa_pub))
     578             :   {
     579           1 :     TALER_ARL_report (report_bad_sig_losses,
     580           1 :                       GNUNET_JSON_PACK (
     581             :                         GNUNET_JSON_pack_string ("operation",
     582             :                                                  "withdraw"),
     583             :                         GNUNET_JSON_pack_uint64 ("row",
     584             :                                                  rowid),
     585             :                         TALER_JSON_pack_amount ("loss",
     586             :                                                 amount_with_fee),
     587             :                         GNUNET_JSON_pack_data_auto ("key_pub",
     588             :                                                     reserve_pub)));
     589           1 :     TALER_ARL_amount_add (&total_bad_sig_loss,
     590             :                           &total_bad_sig_loss,
     591             :                           amount_with_fee);
     592           1 :     if (TALER_ARL_do_abort ())
     593           0 :       return GNUNET_SYSERR;
     594           1 :     return GNUNET_OK;   /* exit function here, we cannot add this to the legitimate withdrawals */
     595             :   }
     596             : 
     597        1041 :   TALER_amount_ntoh (&withdraw_fee,
     598        1041 :                      &issue->fee_withdraw);
     599        1041 :   TALER_amount_ntoh (&auditor_value,
     600        1041 :                      &issue->value);
     601        1041 :   TALER_ARL_amount_add (&auditor_amount_with_fee,
     602             :                         &auditor_value,
     603             :                         &withdraw_fee);
     604        1041 :   if (0 !=
     605        1041 :       TALER_amount_cmp (&auditor_amount_with_fee,
     606             :                         amount_with_fee))
     607             :   {
     608           1 :     report_row_inconsistency ("withdraw",
     609             :                               rowid,
     610             :                               "amount with fee from exchange does not match denomination value plus fee");
     611             :   }
     612             : 
     613             : 
     614        1041 :   GNUNET_CRYPTO_hash (reserve_pub,
     615             :                       sizeof (*reserve_pub),
     616             :                       &key);
     617        1041 :   rs = GNUNET_CONTAINER_multihashmap_get (rc->reserves,
     618             :                                           &key);
     619        1041 :   if (NULL == rs)
     620             :   {
     621           0 :     rs = GNUNET_new (struct ReserveSummary);
     622           0 :     rs->reserve_pub = *reserve_pub;
     623           0 :     rs->total_out = auditor_amount_with_fee;
     624           0 :     GNUNET_assert (GNUNET_OK ==
     625             :                    TALER_amount_set_zero (amount_with_fee->currency,
     626             :                                           &rs->total_in));
     627           0 :     GNUNET_assert (GNUNET_OK ==
     628             :                    TALER_amount_set_zero (amount_with_fee->currency,
     629             :                                           &rs->total_fee));
     630           0 :     qs = load_auditor_reserve_summary (rs);
     631           0 :     if (0 > qs)
     632             :     {
     633           0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     634           0 :       GNUNET_free (rs);
     635           0 :       rc->qs = qs;
     636           0 :       return GNUNET_SYSERR;
     637             :     }
     638           0 :     GNUNET_assert (GNUNET_OK ==
     639             :                    GNUNET_CONTAINER_multihashmap_put (rc->reserves,
     640             :                                                       &key,
     641             :                                                       rs,
     642             :                                                       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
     643             :   }
     644             :   else
     645             :   {
     646        1041 :     TALER_ARL_amount_add (&rs->total_out,
     647             :                           &rs->total_out,
     648             :                           &auditor_amount_with_fee);
     649             :   }
     650        1041 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     651             :               "Reserve `%s' reduced by %s from withdraw\n",
     652             :               TALER_B2S (reserve_pub),
     653             :               TALER_amount2s (&auditor_amount_with_fee));
     654        1041 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     655             :               "Increasing withdraw profits by fee %s\n",
     656             :               TALER_amount2s (&withdraw_fee));
     657        1041 :   TALER_ARL_amount_add (&rs->total_fee,
     658             :                         &rs->total_fee,
     659             :                         &withdraw_fee);
     660        1041 :   if (TALER_ARL_do_abort ())
     661           0 :     return GNUNET_SYSERR;
     662        1041 :   return GNUNET_OK;
     663             : }
     664             : 
     665             : 
     666             : /**
     667             :  * Function called with details about withdraw operations.  Verifies
     668             :  * the signature and updates the reserve's balance.
     669             :  *
     670             :  * @param cls our `struct ReserveContext`
     671             :  * @param rowid unique serial ID for the refresh session in our DB
     672             :  * @param timestamp when did we receive the recoup request
     673             :  * @param amount how much should be added back to the reserve
     674             :  * @param reserve_pub public key of the reserve
     675             :  * @param coin public information about the coin, denomination signature is
     676             :  *        already verified in #check_recoup()
     677             :  * @param denom_pub public key of the denomionation of @a coin
     678             :  * @param coin_sig signature with @e coin_pub of type #TALER_SIGNATURE_WALLET_COIN_RECOUP
     679             :  * @param coin_blind blinding factor used to blind the coin
     680             :  * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
     681             :  */
     682             : static int
     683           7 : handle_recoup_by_reserve (
     684             :   void *cls,
     685             :   uint64_t rowid,
     686             :   struct GNUNET_TIME_Absolute timestamp,
     687             :   const struct TALER_Amount *amount,
     688             :   const struct TALER_ReservePublicKeyP *reserve_pub,
     689             :   const struct TALER_CoinPublicInfo *coin,
     690             :   const struct TALER_DenominationPublicKey *denom_pub,
     691             :   const struct TALER_CoinSpendSignatureP *coin_sig,
     692             :   const struct TALER_DenominationBlindingKeyP *coin_blind)
     693             : {
     694           7 :   struct ReserveContext *rc = cls;
     695             :   struct GNUNET_HashCode key;
     696             :   struct ReserveSummary *rs;
     697             :   struct GNUNET_TIME_Absolute expiry;
     698             :   struct TALER_MasterSignatureP msig;
     699             :   uint64_t rev_rowid;
     700             :   enum GNUNET_DB_QueryStatus qs;
     701             :   const char *rev;
     702             : 
     703             :   (void) denom_pub;
     704             :   /* should be monotonically increasing */
     705           7 :   GNUNET_assert (rowid >= ppr.last_reserve_recoup_serial_id);
     706           7 :   ppr.last_reserve_recoup_serial_id = rowid + 1;
     707             :   /* We know that denom_pub matches denom_pub_hash because this
     708             :      is how the SQL statement joined the tables. */
     709             :   {
     710           7 :     struct TALER_RecoupRequestPS pr = {
     711           7 :       .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_RECOUP),
     712           7 :       .purpose.size = htonl (sizeof (pr)),
     713             :       .h_denom_pub = coin->denom_pub_hash,
     714             :       .coin_pub = coin->coin_pub,
     715             :       .coin_blind = *coin_blind
     716             :     };
     717             : 
     718           7 :     if (GNUNET_OK !=
     719           7 :         GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_RECOUP,
     720             :                                     &pr,
     721             :                                     &coin_sig->eddsa_signature,
     722             :                                     &coin->coin_pub.eddsa_pub))
     723             :     {
     724           0 :       TALER_ARL_report (report_bad_sig_losses,
     725           0 :                         GNUNET_JSON_PACK (
     726             :                           GNUNET_JSON_pack_string ("operation",
     727             :                                                    "recoup"),
     728             :                           GNUNET_JSON_pack_uint64 ("row",
     729             :                                                    rowid),
     730             :                           TALER_JSON_pack_amount ("loss",
     731             :                                                   amount),
     732             :                           GNUNET_JSON_pack_data_auto ("key_pub",
     733             :                                                       &coin->coin_pub)));
     734           0 :       TALER_ARL_amount_add (&total_bad_sig_loss,
     735             :                             &total_bad_sig_loss,
     736             :                             amount);
     737             :     }
     738             :   }
     739             : 
     740             :   /* check that the coin was eligible for recoup!*/
     741           7 :   rev = GNUNET_CONTAINER_multihashmap_get (rc->revoked,
     742             :                                            &coin->denom_pub_hash);
     743           7 :   if (NULL == rev)
     744             :   {
     745           7 :     qs = TALER_ARL_edb->get_denomination_revocation (TALER_ARL_edb->cls,
     746             :                                                      &coin->denom_pub_hash,
     747             :                                                      &msig,
     748             :                                                      &rev_rowid);
     749           7 :     if (0 > qs)
     750             :     {
     751           0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     752           0 :       rc->qs = qs;
     753           0 :       return GNUNET_SYSERR;
     754             :     }
     755           7 :     if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
     756             :     {
     757           1 :       report_row_inconsistency ("recoup",
     758             :                                 rowid,
     759             :                                 "denomination key not in revocation set");
     760           1 :       TALER_ARL_amount_add (&total_irregular_recoups,
     761             :                             &total_irregular_recoups,
     762             :                             amount);
     763             :     }
     764             :     else
     765             :     {
     766           6 :       if (GNUNET_OK !=
     767           6 :           TALER_exchange_offline_denomination_revoke_verify (
     768             :             &coin->denom_pub_hash,
     769             :             &TALER_ARL_master_pub,
     770             :             &msig))
     771             :       {
     772           0 :         rev = "master signature invalid";
     773             :       }
     774             :       else
     775             :       {
     776           6 :         rev = "revoked";
     777             :       }
     778           6 :       GNUNET_assert (GNUNET_OK ==
     779             :                      GNUNET_CONTAINER_multihashmap_put (rc->revoked,
     780             :                                                         &coin->denom_pub_hash,
     781             :                                                         (void *) rev,
     782             :                                                         GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
     783             :     }
     784             :   }
     785             :   else
     786             :   {
     787           0 :     rev_rowid = 0; /* reported elsewhere */
     788             :   }
     789           7 :   if ( (NULL != rev) &&
     790           6 :        (0 == strcmp (rev, "master signature invalid")) )
     791             :   {
     792           0 :     TALER_ARL_report (report_bad_sig_losses,
     793           0 :                       GNUNET_JSON_PACK (
     794             :                         GNUNET_JSON_pack_string ("operation",
     795             :                                                  "recoup-master"),
     796             :                         GNUNET_JSON_pack_uint64 ("row",
     797             :                                                  rev_rowid),
     798             :                         TALER_JSON_pack_amount ("loss",
     799             :                                                 amount),
     800             :                         GNUNET_JSON_pack_data_auto ("key_pub",
     801             :                                                     &TALER_ARL_master_pub)));
     802           0 :     TALER_ARL_amount_add (&total_bad_sig_loss,
     803             :                           &total_bad_sig_loss,
     804             :                           amount);
     805             :   }
     806             : 
     807           7 :   GNUNET_CRYPTO_hash (reserve_pub,
     808             :                       sizeof (*reserve_pub),
     809             :                       &key);
     810           7 :   rs = GNUNET_CONTAINER_multihashmap_get (rc->reserves,
     811             :                                           &key);
     812           7 :   if (NULL == rs)
     813             :   {
     814           0 :     rs = GNUNET_new (struct ReserveSummary);
     815           0 :     rs->reserve_pub = *reserve_pub;
     816           0 :     rs->total_in = *amount;
     817           0 :     GNUNET_assert (GNUNET_OK ==
     818             :                    TALER_amount_set_zero (amount->currency,
     819             :                                           &rs->total_out));
     820           0 :     GNUNET_assert (GNUNET_OK ==
     821             :                    TALER_amount_set_zero (amount->currency,
     822             :                                           &rs->total_fee));
     823           0 :     qs = load_auditor_reserve_summary (rs);
     824           0 :     if (0 > qs)
     825             :     {
     826           0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     827           0 :       GNUNET_free (rs);
     828           0 :       rc->qs = qs;
     829           0 :       return GNUNET_SYSERR;
     830             :     }
     831           0 :     GNUNET_assert (GNUNET_OK ==
     832             :                    GNUNET_CONTAINER_multihashmap_put (rc->reserves,
     833             :                                                       &key,
     834             :                                                       rs,
     835             :                                                       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
     836             :   }
     837             :   else
     838             :   {
     839           7 :     TALER_ARL_amount_add (&rs->total_in,
     840             :                           &rs->total_in,
     841             :                           amount);
     842             :   }
     843           7 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     844             :               "Additional /recoup value to for reserve `%s' of %s\n",
     845             :               TALER_B2S (reserve_pub),
     846             :               TALER_amount2s (amount));
     847           7 :   expiry = GNUNET_TIME_absolute_add (timestamp,
     848             :                                      idle_reserve_expiration_time);
     849           7 :   rs->a_expiration_date = GNUNET_TIME_absolute_max (rs->a_expiration_date,
     850             :                                                     expiry);
     851           7 :   if (TALER_ARL_do_abort ())
     852           0 :     return GNUNET_SYSERR;
     853           7 :   return GNUNET_OK;
     854             : }
     855             : 
     856             : 
     857             : /**
     858             :  * Obtain the closing fee for a transfer at @a time for target
     859             :  * @a receiver_account.
     860             :  *
     861             :  * @param receiver_account payto:// URI of the target account
     862             :  * @param atime when was the transfer made
     863             :  * @param[out] fee set to the closing fee
     864             :  * @return #GNUNET_OK on success
     865             :  */
     866             : static int
     867         105 : get_closing_fee (const char *receiver_account,
     868             :                  struct GNUNET_TIME_Absolute atime,
     869             :                  struct TALER_Amount *fee)
     870             : {
     871             :   struct TALER_MasterSignatureP master_sig;
     872             :   struct GNUNET_TIME_Absolute start_date;
     873             :   struct GNUNET_TIME_Absolute end_date;
     874             :   struct TALER_Amount wire_fee;
     875             :   char *method;
     876             : 
     877         105 :   method = TALER_payto_get_method (receiver_account);
     878         105 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     879             :               "Method is `%s'\n",
     880             :               method);
     881         105 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     882         105 :       TALER_ARL_edb->get_wire_fee (TALER_ARL_edb->cls,
     883             :                                    method,
     884             :                                    atime,
     885             :                                    &start_date,
     886             :                                    &end_date,
     887             :                                    &wire_fee,
     888             :                                    fee,
     889             :                                    &master_sig))
     890             :   {
     891             :     char *diag;
     892             : 
     893           0 :     GNUNET_asprintf (&diag,
     894             :                      "closing fee for `%s' unavailable at %s\n",
     895             :                      method,
     896             :                      GNUNET_STRINGS_absolute_time_to_string (atime));
     897           0 :     report_row_inconsistency ("closing-fee",
     898             :                               atime.abs_value_us,
     899             :                               diag);
     900           0 :     GNUNET_free (diag);
     901           0 :     GNUNET_free (method);
     902           0 :     return GNUNET_SYSERR;
     903             :   }
     904         105 :   GNUNET_free (method);
     905         105 :   return GNUNET_OK;
     906             : }
     907             : 
     908             : 
     909             : /**
     910             :  * Function called about reserve closing operations
     911             :  * the aggregator triggered.
     912             :  *
     913             :  * @param cls closure
     914             :  * @param rowid row identifier used to uniquely identify the reserve closing operation
     915             :  * @param execution_date when did we execute the close operation
     916             :  * @param amount_with_fee how much did we debit the reserve
     917             :  * @param closing_fee how much did we charge for closing the reserve
     918             :  * @param reserve_pub public key of the reserve
     919             :  * @param receiver_account where did we send the funds
     920             :  * @param transfer_details details about the wire transfer
     921             :  * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
     922             :  */
     923             : static int
     924          30 : handle_reserve_closed (
     925             :   void *cls,
     926             :   uint64_t rowid,
     927             :   struct GNUNET_TIME_Absolute execution_date,
     928             :   const struct TALER_Amount *amount_with_fee,
     929             :   const struct TALER_Amount *closing_fee,
     930             :   const struct TALER_ReservePublicKeyP *reserve_pub,
     931             :   const char *receiver_account,
     932             :   const struct TALER_WireTransferIdentifierRawP *transfer_details)
     933             : {
     934          30 :   struct ReserveContext *rc = cls;
     935             :   struct GNUNET_HashCode key;
     936             :   struct ReserveSummary *rs;
     937             :   enum GNUNET_DB_QueryStatus qs;
     938             : 
     939             :   (void) transfer_details;
     940             :   /* should be monotonically increasing */
     941          30 :   GNUNET_assert (rowid >= ppr.last_reserve_close_serial_id);
     942          30 :   ppr.last_reserve_close_serial_id = rowid + 1;
     943             : 
     944          30 :   GNUNET_CRYPTO_hash (reserve_pub,
     945             :                       sizeof (*reserve_pub),
     946             :                       &key);
     947          30 :   rs = GNUNET_CONTAINER_multihashmap_get (rc->reserves,
     948             :                                           &key);
     949          30 :   if (NULL == rs)
     950             :   {
     951           0 :     rs = GNUNET_new (struct ReserveSummary);
     952           0 :     rs->reserve_pub = *reserve_pub;
     953           0 :     rs->total_out = *amount_with_fee;
     954           0 :     rs->total_fee = *closing_fee;
     955           0 :     GNUNET_assert (GNUNET_OK ==
     956             :                    TALER_amount_set_zero (amount_with_fee->currency,
     957             :                                           &rs->total_in));
     958           0 :     qs = load_auditor_reserve_summary (rs);
     959           0 :     if (0 > qs)
     960             :     {
     961           0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     962           0 :       GNUNET_free (rs);
     963           0 :       rc->qs = qs;
     964           0 :       return GNUNET_SYSERR;
     965             :     }
     966           0 :     GNUNET_assert (GNUNET_OK ==
     967             :                    GNUNET_CONTAINER_multihashmap_put (rc->reserves,
     968             :                                                       &key,
     969             :                                                       rs,
     970             :                                                       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
     971             :   }
     972             :   else
     973             :   {
     974             :     struct TALER_Amount expected_fee;
     975             : 
     976          30 :     TALER_ARL_amount_add (&rs->total_out,
     977             :                           &rs->total_out,
     978             :                           amount_with_fee);
     979          30 :     TALER_ARL_amount_add (&rs->total_fee,
     980             :                           &rs->total_fee,
     981             :                           closing_fee);
     982             :     /* verify closing_fee is correct! */
     983          30 :     if (GNUNET_OK !=
     984          30 :         get_closing_fee (receiver_account,
     985             :                          execution_date,
     986             :                          &expected_fee))
     987             :     {
     988           0 :       GNUNET_break (0);
     989             :     }
     990          30 :     else if (0 != TALER_amount_cmp (&expected_fee,
     991             :                                     closing_fee))
     992             :     {
     993           0 :       report_amount_arithmetic_inconsistency (
     994             :         "closing aggregation fee",
     995             :         rowid,
     996             :         closing_fee,
     997             :         &expected_fee,
     998             :         1);
     999             :     }
    1000             :   }
    1001          30 :   if (NULL == rs->sender_account)
    1002             :   {
    1003           0 :     GNUNET_break (GNUNET_NO == rs->had_ri);
    1004           0 :     report_row_inconsistency ("reserves_close",
    1005             :                               rowid,
    1006             :                               "target account not verified, auditor does not know reserve");
    1007             :   }
    1008          30 :   else if (0 != strcmp (rs->sender_account,
    1009             :                         receiver_account))
    1010             :   {
    1011           0 :     report_row_inconsistency ("reserves_close",
    1012             :                               rowid,
    1013             :                               "target account does not match origin account");
    1014             :   }
    1015          30 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1016             :               "Additional closing operation for reserve `%s' of %s\n",
    1017             :               TALER_B2S (reserve_pub),
    1018             :               TALER_amount2s (amount_with_fee));
    1019          30 :   if (TALER_ARL_do_abort ())
    1020           0 :     return GNUNET_SYSERR;
    1021          30 :   return GNUNET_OK;
    1022             : }
    1023             : 
    1024             : 
    1025             : /**
    1026             :  * Check that the reserve summary matches what the exchange database
    1027             :  * thinks about the reserve, and update our own state of the reserve.
    1028             :  *
    1029             :  * Remove all reserves that we are happy with from the DB.
    1030             :  *
    1031             :  * @param cls our `struct ReserveContext`
    1032             :  * @param key hash of the reserve public key
    1033             :  * @param value a `struct ReserveSummary`
    1034             :  * @return #GNUNET_OK to process more entries
    1035             :  */
    1036             : static int
    1037          83 : verify_reserve_balance (void *cls,
    1038             :                         const struct GNUNET_HashCode *key,
    1039             :                         void *value)
    1040             : {
    1041          83 :   struct ReserveContext *rc = cls;
    1042          83 :   struct ReserveSummary *rs = value;
    1043             :   struct TALER_Amount balance;
    1044             :   struct TALER_Amount nbalance;
    1045             :   enum GNUNET_DB_QueryStatus qs;
    1046             :   int ret;
    1047             : 
    1048          83 :   ret = GNUNET_OK;
    1049             :   /* Check our reserve summary balance calculation shows that
    1050             :      the reserve balance is acceptable (i.e. non-negative) */
    1051          83 :   TALER_ARL_amount_add (&balance,
    1052             :                         &rs->total_in,
    1053             :                         &rs->balance_at_previous_audit);
    1054          83 :   if (TALER_ARL_SR_INVALID_NEGATIVE ==
    1055          83 :       TALER_ARL_amount_subtract_neg (&nbalance,
    1056             :                                      &balance,
    1057             :                                      &rs->total_out))
    1058             :   {
    1059             :     struct TALER_Amount loss;
    1060             : 
    1061           2 :     TALER_ARL_amount_subtract (&loss,
    1062             :                                &rs->total_out,
    1063             :                                &balance);
    1064           2 :     TALER_ARL_amount_add (&total_balance_insufficient_loss,
    1065             :                           &total_balance_insufficient_loss,
    1066             :                           &loss);
    1067           2 :     TALER_ARL_report (report_reserve_balance_insufficient_inconsistencies,
    1068           2 :                       GNUNET_JSON_PACK (
    1069             :                         GNUNET_JSON_pack_data_auto ("reserve_pub",
    1070             :                                                     &rs->reserve_pub),
    1071             :                         TALER_JSON_pack_amount ("loss",
    1072             :                                                 &loss)));
    1073             :     /* Continue with a reserve balance of zero */
    1074           2 :     GNUNET_assert (GNUNET_OK ==
    1075             :                    TALER_amount_set_zero (balance.currency,
    1076             :                                           &nbalance));
    1077             :   }
    1078             : 
    1079          83 :   if (internal_checks)
    1080             :   {
    1081             :     /* Now check OUR balance calculation vs. the one the exchange has
    1082             :        in its database. This can only be done when we are doing an
    1083             :        internal audit, as otherwise the balance of the 'reserves' table
    1084             :        is not replicated at the auditor. */
    1085             :     struct TALER_EXCHANGEDB_Reserve reserve;
    1086             : 
    1087          75 :     reserve.pub = rs->reserve_pub;
    1088          75 :     qs = TALER_ARL_edb->reserves_get (TALER_ARL_edb->cls,
    1089             :                                       &reserve);
    1090          75 :     if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
    1091             :     {
    1092             :       /* If the exchange doesn't have this reserve in the summary, it
    1093             :          is like the exchange 'lost' that amount from its records,
    1094             :          making an illegitimate gain over the amount it dropped.
    1095             :          We don't add the amount to some total simply because it is
    1096             :          not an actualized gain and could be trivially corrected by
    1097             :          restoring the summary. *///
    1098           0 :       TALER_ARL_report (report_reserve_balance_insufficient_inconsistencies,
    1099           0 :                         GNUNET_JSON_PACK (
    1100             :                           GNUNET_JSON_pack_data_auto ("reserve_pub",
    1101             :                                                       &rs->reserve_pub),
    1102             :                           TALER_JSON_pack_amount ("gain",
    1103             :                                                   &nbalance)));
    1104           0 :       if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    1105             :       {
    1106           0 :         GNUNET_break (0);
    1107           0 :         qs = GNUNET_DB_STATUS_HARD_ERROR;
    1108             :       }
    1109           0 :       rc->qs = qs;
    1110             :     }
    1111             :     else
    1112             :     {
    1113             :       /* Check that exchange's balance matches our expected balance for the reserve */
    1114          75 :       if (0 != TALER_amount_cmp (&nbalance,
    1115             :                                  &reserve.balance))
    1116             :       {
    1117             :         struct TALER_Amount delta;
    1118             : 
    1119           7 :         if (0 < TALER_amount_cmp (&nbalance,
    1120             :                                   &reserve.balance))
    1121             :         {
    1122             :           /* balance > reserve.balance */
    1123           5 :           TALER_ARL_amount_subtract (&delta,
    1124             :                                      &nbalance,
    1125             :                                      &reserve.balance);
    1126           5 :           TALER_ARL_amount_add (&total_balance_summary_delta_plus,
    1127             :                                 &total_balance_summary_delta_plus,
    1128             :                                 &delta);
    1129             :         }
    1130             :         else
    1131             :         {
    1132             :           /* balance < reserve.balance */
    1133           2 :           TALER_ARL_amount_subtract (&delta,
    1134             :                                      &reserve.balance,
    1135             :                                      &nbalance);
    1136           2 :           TALER_ARL_amount_add (&total_balance_summary_delta_minus,
    1137             :                                 &total_balance_summary_delta_minus,
    1138             :                                 &delta);
    1139             :         }
    1140           7 :         TALER_ARL_report (report_reserve_balance_summary_wrong_inconsistencies,
    1141           7 :                           GNUNET_JSON_PACK (
    1142             :                             GNUNET_JSON_pack_data_auto ("reserve_pub",
    1143             :                                                         &rs->reserve_pub),
    1144             :                             TALER_JSON_pack_amount ("exchange",
    1145             :                                                     &reserve.balance),
    1146             :                             TALER_JSON_pack_amount ("auditor",
    1147             :                                                     &nbalance)));
    1148             :       }
    1149             :     }
    1150             :   } /* end of 'if (internal_checks)' */
    1151             : 
    1152             :   /* Check that reserve is being closed if it is past its expiration date
    1153             :      (and the closing fee would not exceed the remaining balance) */
    1154          83 :   if (CLOSING_GRACE_PERIOD.rel_value_us <
    1155          83 :       GNUNET_TIME_absolute_get_duration (rs->a_expiration_date).rel_value_us)
    1156             :   {
    1157             :     /* Reserve is expired */
    1158             :     struct TALER_Amount cfee;
    1159             : 
    1160         150 :     if ( (NULL != rs->sender_account) &&
    1161             :          (GNUNET_OK ==
    1162          75 :           get_closing_fee (rs->sender_account,
    1163             :                            rs->a_expiration_date,
    1164             :                            &cfee)) )
    1165             :     {
    1166             :       /* We got the closing fee */
    1167          75 :       if (1 == TALER_amount_cmp (&nbalance,
    1168             :                                  &cfee))
    1169             :       {
    1170             :         /* remaining balance (according to us) exceeds closing fee */
    1171           6 :         TALER_ARL_amount_add (&total_balance_reserve_not_closed,
    1172             :                               &total_balance_reserve_not_closed,
    1173             :                               &nbalance);
    1174           6 :         TALER_ARL_report (
    1175             :           report_reserve_not_closed_inconsistencies,
    1176           6 :           GNUNET_JSON_PACK (
    1177             :             GNUNET_JSON_pack_data_auto ("reserve_pub",
    1178             :                                         &rs->reserve_pub),
    1179             :             TALER_JSON_pack_amount ("balance",
    1180             :                                     &nbalance),
    1181             :             TALER_JSON_pack_time_abs_human ("expiration_time",
    1182             :                                             rs->a_expiration_date)));
    1183             :       }
    1184             :     }
    1185             :     else
    1186             :     {
    1187             :       /* We failed to determine the closing fee, complain! */
    1188           0 :       TALER_ARL_amount_add (&total_balance_reserve_not_closed,
    1189             :                             &total_balance_reserve_not_closed,
    1190             :                             &nbalance);
    1191           0 :       TALER_ARL_report (report_reserve_not_closed_inconsistencies,
    1192           0 :                         GNUNET_JSON_PACK (
    1193             :                           GNUNET_JSON_pack_data_auto ("reserve_pub",
    1194             :                                                       &rs->reserve_pub),
    1195             :                           TALER_JSON_pack_amount ("balance",
    1196             :                                                   &nbalance),
    1197             :                           TALER_JSON_pack_time_abs_human ("expiration_time",
    1198             :                                                           rs->a_expiration_date),
    1199             :                           GNUNET_JSON_pack_string ("diagnostic",
    1200             :                                                    "could not determine closing fee")));
    1201             :     }
    1202             :   }
    1203             : 
    1204             :   /* Add withdraw fees we encountered to totals */
    1205          83 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1206             :               "Reserve reserve `%s' made %s in withdraw fees\n",
    1207             :               TALER_B2S (&rs->reserve_pub),
    1208             :               TALER_amount2s (&rs->total_fee));
    1209          83 :   TALER_ARL_amount_add (&rs->a_withdraw_fee_balance,
    1210             :                         &rs->a_withdraw_fee_balance,
    1211             :                         &rs->total_fee);
    1212          83 :   TALER_ARL_amount_add (&total_escrow_balance,
    1213             :                         &total_escrow_balance,
    1214             :                         &rs->total_in);
    1215          83 :   TALER_ARL_amount_add (&total_withdraw_fee_income,
    1216             :                         &total_withdraw_fee_income,
    1217             :                         &rs->total_fee);
    1218             :   {
    1219             :     struct TALER_Amount r;
    1220             : 
    1221          83 :     if (TALER_ARL_SR_INVALID_NEGATIVE ==
    1222          83 :         TALER_ARL_amount_subtract_neg (&r,
    1223             :                                        &total_escrow_balance,
    1224             :                                        &rs->total_out))
    1225             :     {
    1226             :       /* We could not reduce our total balance, i.e. exchange allowed IN TOTAL (!)
    1227             :          to be withdrawn more than it was IN TOTAL ever given (exchange balance
    1228             :          went negative!).  Woopsie. Calculate how badly it went and log. */
    1229           2 :       report_amount_arithmetic_inconsistency ("global escrow balance",
    1230             :                                               0,
    1231             :                                               &total_escrow_balance, /* what we had */
    1232           2 :                                               &rs->total_out, /* what we needed */
    1233             :                                               0 /* specific profit/loss does not apply to the total summary */);
    1234             :       /* We unexpectedly went negative, so a sane value to continue from
    1235             :          would be zero. */
    1236           2 :       GNUNET_assert (GNUNET_OK ==
    1237             :                      TALER_amount_set_zero (TALER_ARL_currency,
    1238             :                                             &total_escrow_balance));
    1239             :     }
    1240             :     else
    1241             :     {
    1242          81 :       total_escrow_balance = r;
    1243             :     }
    1244             :   }
    1245             : 
    1246          83 :   if ( (0ULL == balance.value) &&
    1247           0 :        (0U == balance.fraction) )
    1248             :   {
    1249             :     /* balance is zero, drop reserve details (and then do not update/insert) */
    1250           0 :     if (rs->had_ri)
    1251             :     {
    1252           0 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1253             :                   "Final balance of reserve `%s' is %s, dropping it\n",
    1254             :                   TALER_B2S (&rs->reserve_pub),
    1255             :                   TALER_amount2s (&nbalance));
    1256           0 :       qs = TALER_ARL_adb->del_reserve_info (TALER_ARL_adb->cls,
    1257           0 :                                             &rs->reserve_pub,
    1258             :                                             &TALER_ARL_master_pub);
    1259           0 :       if (0 >= qs)
    1260             :       {
    1261           0 :         GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1262           0 :         ret = GNUNET_SYSERR;
    1263           0 :         rc->qs = qs;
    1264             :       }
    1265             :     }
    1266             :     else
    1267             :     {
    1268           0 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1269             :                   "Final balance of reserve `%s' is %s, no need to remember it\n",
    1270             :                   TALER_B2S (&rs->reserve_pub),
    1271             :                   TALER_amount2s (&nbalance));
    1272             :     }
    1273             :   }
    1274             :   else
    1275             :   {
    1276             :     /* balance is non-zero, persist for future audits */
    1277          83 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1278             :                 "Remembering final balance of reserve `%s' as %s\n",
    1279             :                 TALER_B2S (&rs->reserve_pub),
    1280             :                 TALER_amount2s (&nbalance));
    1281          83 :     if (rs->had_ri)
    1282           0 :       qs = TALER_ARL_adb->update_reserve_info (TALER_ARL_adb->cls,
    1283           0 :                                                &rs->reserve_pub,
    1284             :                                                &TALER_ARL_master_pub,
    1285             :                                                &nbalance,
    1286           0 :                                                &rs->a_withdraw_fee_balance,
    1287             :                                                rs->a_expiration_date);
    1288             :     else
    1289          83 :       qs = TALER_ARL_adb->insert_reserve_info (TALER_ARL_adb->cls,
    1290          83 :                                                &rs->reserve_pub,
    1291             :                                                &TALER_ARL_master_pub,
    1292             :                                                &nbalance,
    1293          83 :                                                &rs->a_withdraw_fee_balance,
    1294             :                                                rs->a_expiration_date,
    1295          83 :                                                rs->sender_account);
    1296          83 :     if (0 >= qs)
    1297             :     {
    1298           0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1299           0 :       ret = GNUNET_SYSERR;
    1300           0 :       rc->qs = qs;
    1301             :     }
    1302             :   }
    1303             : 
    1304          83 :   GNUNET_assert (GNUNET_YES ==
    1305             :                  GNUNET_CONTAINER_multihashmap_remove (rc->reserves,
    1306             :                                                        key,
    1307             :                                                        rs));
    1308          83 :   GNUNET_free (rs->sender_account);
    1309          83 :   GNUNET_free (rs);
    1310          83 :   return ret;
    1311             : }
    1312             : 
    1313             : 
    1314             : /**
    1315             :  * Analyze reserves for being well-formed.
    1316             :  *
    1317             :  * @param cls NULL
    1318             :  * @return transaction status code
    1319             :  */
    1320             : static enum GNUNET_DB_QueryStatus
    1321          83 : analyze_reserves (void *cls)
    1322             : {
    1323             :   struct ReserveContext rc;
    1324             :   enum GNUNET_DB_QueryStatus qsx;
    1325             :   enum GNUNET_DB_QueryStatus qs;
    1326             :   enum GNUNET_DB_QueryStatus qsp;
    1327             : 
    1328             :   (void) cls;
    1329          83 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1330             :               "Analyzing reserves\n");
    1331          83 :   qsp = TALER_ARL_adb->get_auditor_progress_reserve (TALER_ARL_adb->cls,
    1332             :                                                      &TALER_ARL_master_pub,
    1333             :                                                      &ppr);
    1334          83 :   if (0 > qsp)
    1335             :   {
    1336           0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qsp);
    1337           0 :     return qsp;
    1338             :   }
    1339          83 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qsp)
    1340             :   {
    1341          41 :     GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
    1342             :                 "First analysis using this auditor, starting audit from scratch\n");
    1343             :   }
    1344             :   else
    1345             :   {
    1346          42 :     ppr_start = ppr;
    1347          42 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1348             :                 "Resuming reserve audit at %llu/%llu/%llu/%llu\n",
    1349             :                 (unsigned long long) ppr.last_reserve_in_serial_id,
    1350             :                 (unsigned long long) ppr.last_reserve_out_serial_id,
    1351             :                 (unsigned long long) ppr.last_reserve_recoup_serial_id,
    1352             :                 (unsigned long long) ppr.last_reserve_close_serial_id);
    1353             :   }
    1354          83 :   rc.qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
    1355          83 :   qsx = TALER_ARL_adb->get_reserve_summary (TALER_ARL_adb->cls,
    1356             :                                             &TALER_ARL_master_pub,
    1357             :                                             &total_escrow_balance,
    1358             :                                             &total_withdraw_fee_income);
    1359          83 :   if (qsx < 0)
    1360             :   {
    1361           0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qsx);
    1362           0 :     return qsx;
    1363             :   }
    1364          83 :   rc.reserves = GNUNET_CONTAINER_multihashmap_create (512,
    1365             :                                                       GNUNET_NO);
    1366          83 :   rc.revoked = GNUNET_CONTAINER_multihashmap_create (4,
    1367             :                                                      GNUNET_NO);
    1368             : 
    1369          83 :   qs = TALER_ARL_edb->select_reserves_in_above_serial_id (
    1370          83 :     TALER_ARL_edb->cls,
    1371             :     ppr.last_reserve_in_serial_id,
    1372             :     &handle_reserve_in,
    1373             :     &rc);
    1374          83 :   if (qs < 0)
    1375             :   {
    1376           0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1377           0 :     return qs;
    1378             :   }
    1379          83 :   qs = TALER_ARL_edb->select_withdrawals_above_serial_id (
    1380          83 :     TALER_ARL_edb->cls,
    1381             :     ppr.last_reserve_out_serial_id,
    1382             :     &handle_reserve_out,
    1383             :     &rc);
    1384          83 :   if (qs < 0)
    1385             :   {
    1386           0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1387           0 :     return qs;
    1388             :   }
    1389          83 :   qs = TALER_ARL_edb->select_recoup_above_serial_id (
    1390          83 :     TALER_ARL_edb->cls,
    1391             :     ppr.last_reserve_recoup_serial_id,
    1392             :     &handle_recoup_by_reserve,
    1393             :     &rc);
    1394          83 :   if (qs < 0)
    1395             :   {
    1396           0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1397           0 :     return qs;
    1398             :   }
    1399          83 :   qs = TALER_ARL_edb->select_reserve_closed_above_serial_id (
    1400          83 :     TALER_ARL_edb->cls,
    1401             :     ppr.last_reserve_close_serial_id,
    1402             :     &handle_reserve_closed,
    1403             :     &rc);
    1404          83 :   if (qs < 0)
    1405             :   {
    1406           0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1407           0 :     return qs;
    1408             :   }
    1409             : 
    1410          83 :   GNUNET_CONTAINER_multihashmap_iterate (rc.reserves,
    1411             :                                          &verify_reserve_balance,
    1412             :                                          &rc);
    1413          83 :   GNUNET_break (0 ==
    1414             :                 GNUNET_CONTAINER_multihashmap_size (rc.reserves));
    1415          83 :   GNUNET_CONTAINER_multihashmap_destroy (rc.reserves);
    1416          83 :   GNUNET_CONTAINER_multihashmap_destroy (rc.revoked);
    1417             : 
    1418          83 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != rc.qs)
    1419           0 :     return qs;
    1420             : 
    1421          83 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qsx)
    1422             :   {
    1423          41 :     qs = TALER_ARL_adb->insert_reserve_summary (TALER_ARL_adb->cls,
    1424             :                                                 &TALER_ARL_master_pub,
    1425             :                                                 &total_escrow_balance,
    1426             :                                                 &total_withdraw_fee_income);
    1427             :   }
    1428             :   else
    1429             :   {
    1430          42 :     qs = TALER_ARL_adb->update_reserve_summary (TALER_ARL_adb->cls,
    1431             :                                                 &TALER_ARL_master_pub,
    1432             :                                                 &total_escrow_balance,
    1433             :                                                 &total_withdraw_fee_income);
    1434             :   }
    1435          83 :   if (0 >= qs)
    1436             :   {
    1437           0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1438           0 :     return qs;
    1439             :   }
    1440          83 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qsp)
    1441          42 :     qs = TALER_ARL_adb->update_auditor_progress_reserve (TALER_ARL_adb->cls,
    1442             :                                                          &TALER_ARL_master_pub,
    1443             :                                                          &ppr);
    1444             :   else
    1445          41 :     qs = TALER_ARL_adb->insert_auditor_progress_reserve (TALER_ARL_adb->cls,
    1446             :                                                          &TALER_ARL_master_pub,
    1447             :                                                          &ppr);
    1448          83 :   if (0 >= qs)
    1449             :   {
    1450           0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1451             :                 "Failed to update auditor DB, not recording progress\n");
    1452           0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1453           0 :     return qs;
    1454             :   }
    1455          83 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1456             :               "Concluded reserve audit step at %llu/%llu/%llu/%llu\n",
    1457             :               (unsigned long long) ppr.last_reserve_in_serial_id,
    1458             :               (unsigned long long) ppr.last_reserve_out_serial_id,
    1459             :               (unsigned long long) ppr.last_reserve_recoup_serial_id,
    1460             :               (unsigned long long) ppr.last_reserve_close_serial_id);
    1461          83 :   return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
    1462             : }
    1463             : 
    1464             : 
    1465             : /**
    1466             :  * Main function that will be run.
    1467             :  *
    1468             :  * @param cls closure
    1469             :  * @param args remaining command-line arguments
    1470             :  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
    1471             :  * @param c configuration
    1472             :  */
    1473             : static void
    1474          83 : run (void *cls,
    1475             :      char *const *args,
    1476             :      const char *cfgfile,
    1477             :      const struct GNUNET_CONFIGURATION_Handle *c)
    1478             : {
    1479             :   (void) cls;
    1480             :   (void) args;
    1481             :   (void) cfgfile;
    1482          83 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1483             :               "Launching auditor\n");
    1484          83 :   if (GNUNET_OK !=
    1485          83 :       TALER_ARL_init (c))
    1486             :   {
    1487           0 :     global_ret = EXIT_FAILURE;
    1488           0 :     return;
    1489             :   }
    1490          83 :   if (GNUNET_OK !=
    1491          83 :       GNUNET_CONFIGURATION_get_value_time (TALER_ARL_cfg,
    1492             :                                            "exchangedb",
    1493             :                                            "IDLE_RESERVE_EXPIRATION_TIME",
    1494             :                                            &idle_reserve_expiration_time))
    1495             :   {
    1496           0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    1497             :                                "exchangedb",
    1498             :                                "IDLE_RESERVE_EXPIRATION_TIME");
    1499           0 :     global_ret = EXIT_FAILURE;
    1500           0 :     return;
    1501             :   }
    1502          83 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1503             :               "Starting audit\n");
    1504          83 :   GNUNET_assert (GNUNET_OK ==
    1505             :                  TALER_amount_set_zero (TALER_ARL_currency,
    1506             :                                         &total_escrow_balance));
    1507          83 :   GNUNET_assert (GNUNET_OK ==
    1508             :                  TALER_amount_set_zero (TALER_ARL_currency,
    1509             :                                         &total_irregular_recoups));
    1510          83 :   GNUNET_assert (GNUNET_OK ==
    1511             :                  TALER_amount_set_zero (TALER_ARL_currency,
    1512             :                                         &total_withdraw_fee_income));
    1513          83 :   GNUNET_assert (GNUNET_OK ==
    1514             :                  TALER_amount_set_zero (TALER_ARL_currency,
    1515             :                                         &total_balance_insufficient_loss));
    1516          83 :   GNUNET_assert (GNUNET_OK ==
    1517             :                  TALER_amount_set_zero (TALER_ARL_currency,
    1518             :                                         &total_balance_summary_delta_plus));
    1519          83 :   GNUNET_assert (GNUNET_OK ==
    1520             :                  TALER_amount_set_zero (TALER_ARL_currency,
    1521             :                                         &total_balance_summary_delta_minus));
    1522          83 :   GNUNET_assert (GNUNET_OK ==
    1523             :                  TALER_amount_set_zero (TALER_ARL_currency,
    1524             :                                         &total_arithmetic_delta_plus));
    1525          83 :   GNUNET_assert (GNUNET_OK ==
    1526             :                  TALER_amount_set_zero (TALER_ARL_currency,
    1527             :                                         &total_arithmetic_delta_minus));
    1528          83 :   GNUNET_assert (GNUNET_OK ==
    1529             :                  TALER_amount_set_zero (TALER_ARL_currency,
    1530             :                                         &total_balance_reserve_not_closed));
    1531          83 :   GNUNET_assert (GNUNET_OK ==
    1532             :                  TALER_amount_set_zero (TALER_ARL_currency,
    1533             :                                         &total_bad_sig_loss));
    1534          83 :   GNUNET_assert (NULL !=
    1535             :                  (report_row_inconsistencies = json_array ()));
    1536          83 :   GNUNET_assert (NULL !=
    1537             :                  (denomination_key_validity_withdraw_inconsistencies
    1538             :                     = json_array ()));
    1539          83 :   GNUNET_assert (NULL !=
    1540             :                  (report_reserve_balance_summary_wrong_inconsistencies
    1541             :                     = json_array ()));
    1542          83 :   GNUNET_assert (NULL !=
    1543             :                  (report_reserve_balance_insufficient_inconsistencies
    1544             :                     = json_array ()));
    1545          83 :   GNUNET_assert (NULL !=
    1546             :                  (report_reserve_not_closed_inconsistencies
    1547             :                     = json_array ()));
    1548          83 :   GNUNET_assert (NULL !=
    1549             :                  (report_amount_arithmetic_inconsistencies
    1550             :                     = json_array ()));
    1551          83 :   GNUNET_assert (NULL !=
    1552             :                  (report_bad_sig_losses = json_array ()));
    1553          83 :   if (GNUNET_OK !=
    1554          83 :       TALER_ARL_setup_sessions_and_run (&analyze_reserves,
    1555             :                                         NULL))
    1556             :   {
    1557           0 :     global_ret = EXIT_FAILURE;
    1558           0 :     return;
    1559             :   }
    1560          83 :   TALER_ARL_done (
    1561          83 :     GNUNET_JSON_PACK (
    1562             :       GNUNET_JSON_pack_array_steal (
    1563             :         "reserve_balance_insufficient_inconsistencies",
    1564             :         report_reserve_balance_insufficient_inconsistencies),
    1565             :       /* Tested in test-auditor.sh #3 */
    1566             :       TALER_JSON_pack_amount ("total_loss_balance_insufficient",
    1567             :                               &total_balance_insufficient_loss),
    1568             :       /* Tested in test-auditor.sh #3 */
    1569             :       GNUNET_JSON_pack_array_steal (
    1570             :         "reserve_balance_summary_wrong_inconsistencies",
    1571             :         report_reserve_balance_summary_wrong_inconsistencies),
    1572             :       TALER_JSON_pack_amount ("total_balance_summary_delta_plus",
    1573             :                               &total_balance_summary_delta_plus),
    1574             :       TALER_JSON_pack_amount ("total_balance_summary_delta_minus",
    1575             :                               &total_balance_summary_delta_minus),
    1576             :       /* blocks #2 */
    1577             :       TALER_JSON_pack_amount ("total_escrow_balance",
    1578             :                               &total_escrow_balance),
    1579             :       TALER_JSON_pack_amount ("total_withdraw_fee_income",
    1580             :                               &total_withdraw_fee_income),
    1581             :       /* Tested in test-auditor.sh #21 */
    1582             :       GNUNET_JSON_pack_array_steal ("reserve_not_closed_inconsistencies",
    1583             :                                     report_reserve_not_closed_inconsistencies),
    1584             :       /* Tested in test-auditor.sh #21 */
    1585             :       TALER_JSON_pack_amount ("total_balance_reserve_not_closed",
    1586             :                               &total_balance_reserve_not_closed),
    1587             :       /* Tested in test-auditor.sh #7 */
    1588             :       GNUNET_JSON_pack_array_steal ("bad_sig_losses",
    1589             :                                     report_bad_sig_losses),
    1590             :       /* Tested in test-auditor.sh #7 */
    1591             :       TALER_JSON_pack_amount ("total_bad_sig_loss",
    1592             :                               &total_bad_sig_loss),
    1593             :       /* Tested in test-revocation.sh #4 */
    1594             :       GNUNET_JSON_pack_array_steal ("row_inconsistencies",
    1595             :                                     report_row_inconsistencies),
    1596             :       /* Tested in test-auditor.sh #23 */
    1597             :       GNUNET_JSON_pack_array_steal (
    1598             :         "denomination_key_validity_withdraw_inconsistencies",
    1599             :         denomination_key_validity_withdraw_inconsistencies),
    1600             :       GNUNET_JSON_pack_array_steal ("amount_arithmetic_inconsistencies",
    1601             :                                     report_amount_arithmetic_inconsistencies),
    1602             :       TALER_JSON_pack_amount ("total_arithmetic_delta_plus",
    1603             :                               &total_arithmetic_delta_plus),
    1604             :       TALER_JSON_pack_amount ("total_arithmetic_delta_minus",
    1605             :                               &total_arithmetic_delta_minus),
    1606             :       TALER_JSON_pack_time_abs_human ("auditor_start_time",
    1607             :                                       start_time),
    1608             :       TALER_JSON_pack_time_abs_human ("auditor_end_time",
    1609             :                                       GNUNET_TIME_absolute_get ()),
    1610             :       TALER_JSON_pack_amount ("total_irregular_recoups",
    1611             :                               &total_irregular_recoups),
    1612             :       GNUNET_JSON_pack_uint64 ("start_ppr_reserve_in_serial_id",
    1613             :                                ppr_start.last_reserve_in_serial_id),
    1614             :       GNUNET_JSON_pack_uint64 ("start_ppr_reserve_out_serial_id",
    1615             :                                ppr_start.last_reserve_out_serial_id),
    1616             :       GNUNET_JSON_pack_uint64 ("start_ppr_reserve_recoup_serial_id",
    1617             :                                ppr_start.last_reserve_recoup_serial_id),
    1618             :       GNUNET_JSON_pack_uint64 ("start_ppr_reserve_close_serial_id",
    1619             :                                ppr_start.last_reserve_close_serial_id),
    1620             :       GNUNET_JSON_pack_uint64 ("end_ppr_reserve_in_serial_id",
    1621             :                                ppr.last_reserve_in_serial_id),
    1622             :       GNUNET_JSON_pack_uint64 ("end_ppr_reserve_out_serial_id",
    1623             :                                ppr.last_reserve_out_serial_id),
    1624             :       GNUNET_JSON_pack_uint64 ("end_ppr_reserve_recoup_serial_id",
    1625             :                                ppr.last_reserve_recoup_serial_id),
    1626             :       GNUNET_JSON_pack_uint64 ("end_ppr_reserve_close_serial_id",
    1627             :                                ppr.last_reserve_close_serial_id)));
    1628             : }
    1629             : 
    1630             : 
    1631             : /**
    1632             :  * The main function to check the database's handling of reserves.
    1633             :  *
    1634             :  * @param argc number of arguments from the command line
    1635             :  * @param argv command line arguments
    1636             :  * @return 0 ok, 1 on error
    1637             :  */
    1638             : int
    1639          83 : main (int argc,
    1640             :       char *const *argv)
    1641             : {
    1642          83 :   const struct GNUNET_GETOPT_CommandLineOption options[] = {
    1643          83 :     GNUNET_GETOPT_option_flag ('i',
    1644             :                                "internal",
    1645             :                                "perform checks only applicable for exchange-internal audits",
    1646             :                                &internal_checks),
    1647          83 :     GNUNET_GETOPT_option_base32_auto ('m',
    1648             :                                       "exchange-key",
    1649             :                                       "KEY",
    1650             :                                       "public key of the exchange (Crockford base32 encoded)",
    1651             :                                       &TALER_ARL_master_pub),
    1652          83 :     GNUNET_GETOPT_option_timetravel ('T',
    1653             :                                      "timetravel"),
    1654             :     GNUNET_GETOPT_OPTION_END
    1655             :   };
    1656             :   enum GNUNET_GenericReturnValue ret;
    1657             : 
    1658             :   /* force linker to link against libtalerutil; if we do
    1659             :      not do this, the linker may "optimize" libtalerutil
    1660             :      away and skip #TALER_OS_init(), which we do need */
    1661          83 :   (void) TALER_project_data_default ();
    1662          83 :   if (GNUNET_OK !=
    1663          83 :       GNUNET_STRINGS_get_utf8_args (argc, argv,
    1664             :                                     &argc, &argv))
    1665           0 :     return EXIT_INVALIDARGUMENT;
    1666          83 :   ret = GNUNET_PROGRAM_run (
    1667             :     argc,
    1668             :     argv,
    1669             :     "taler-helper-auditor-reserves",
    1670             :     gettext_noop ("Audit Taler exchange reserve handling"),
    1671             :     options,
    1672             :     &run,
    1673             :     NULL);
    1674          83 :   GNUNET_free_nz ((void *) argv);
    1675          83 :   if (GNUNET_SYSERR == ret)
    1676           0 :     return EXIT_INVALIDARGUMENT;
    1677          83 :   if (GNUNET_NO == ret)
    1678           0 :     return EXIT_SUCCESS;
    1679          83 :   return global_ret;
    1680             : }
    1681             : 
    1682             : 
    1683             : /* end of taler-helper-auditor-reserves.c */

Generated by: LCOV version 1.14