LCOV - code coverage report
Current view: top level - auditor - taler-helper-auditor-aggregation.c (source / functions) Hit Total Coverage
Test: GNU Taler exchange coverage report Lines: 250 362 69.1 %
Date: 2021-08-30 06:43:37 Functions: 9 10 90.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-aggregation.c
      18             :  * @brief audits an exchange's aggregations.
      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             :  * Return value from main().
      33             :  */
      34             : static int global_ret;
      35             : 
      36             : /**
      37             :  * Checkpointing our progress for aggregations.
      38             :  */
      39             : static struct TALER_AUDITORDB_ProgressPointAggregation ppa;
      40             : 
      41             : /**
      42             :  * Checkpointing our progress for aggregations.
      43             :  */
      44             : static struct TALER_AUDITORDB_ProgressPointAggregation ppa_start;
      45             : 
      46             : /**
      47             :  * Array of reports about row inconsitencies.
      48             :  */
      49             : static json_t *report_row_inconsistencies;
      50             : 
      51             : /**
      52             :  * Array of reports about irregular wire out entries.
      53             :  */
      54             : static json_t *report_wire_out_inconsistencies;
      55             : 
      56             : /**
      57             :  * Total delta between calculated and stored wire out transfers,
      58             :  * for positive deltas.
      59             :  */
      60             : static struct TALER_Amount total_wire_out_delta_plus;
      61             : 
      62             : /**
      63             :  * Total delta between calculated and stored wire out transfers
      64             :  * for negative deltas.
      65             :  */
      66             : static struct TALER_Amount total_wire_out_delta_minus;
      67             : 
      68             : /**
      69             :  * Array of reports about inconsistencies about coins.
      70             :  */
      71             : static json_t *report_coin_inconsistencies;
      72             : 
      73             : /**
      74             :  * Profits the exchange made by bad amount calculations on coins.
      75             :  */
      76             : static struct TALER_Amount total_coin_delta_plus;
      77             : 
      78             : /**
      79             :  * Losses the exchange made by bad amount calculations on coins.
      80             :  */
      81             : static struct TALER_Amount total_coin_delta_minus;
      82             : 
      83             : /**
      84             :  * Report about amount calculation differences (causing profit
      85             :  * or loss at the exchange).
      86             :  */
      87             : static json_t *report_amount_arithmetic_inconsistencies;
      88             : 
      89             : /**
      90             :  * Array of reports about wire fees being ambiguous in terms of validity periods.
      91             :  */
      92             : static json_t *report_fee_time_inconsistencies;
      93             : 
      94             : /**
      95             :  * Profits the exchange made by bad amount calculations.
      96             :  */
      97             : static struct TALER_Amount total_arithmetic_delta_plus;
      98             : 
      99             : /**
     100             :  * Losses the exchange made by bad amount calculations.
     101             :  */
     102             : static struct TALER_Amount total_arithmetic_delta_minus;
     103             : 
     104             : /**
     105             :  * Total aggregation fees earned.
     106             :  */
     107             : static struct TALER_Amount total_aggregation_fee_income;
     108             : 
     109             : /**
     110             :  * Array of reports about coin operations with bad signatures.
     111             :  */
     112             : static json_t *report_bad_sig_losses;
     113             : 
     114             : /**
     115             :  * Total amount lost by operations for which signatures were invalid.
     116             :  */
     117             : static struct TALER_Amount total_bad_sig_loss;
     118             : 
     119             : /**
     120             :  * Should we run checks that only work for exchange-internal audits?
     121             :  */
     122             : static int internal_checks;
     123             : 
     124             : 
     125             : /**
     126             :  * Report a (serious) inconsistency in the exchange's database with
     127             :  * respect to calculations involving amounts.
     128             :  *
     129             :  * @param operation what operation had the inconsistency
     130             :  * @param rowid affected row, 0 if row is missing
     131             :  * @param exchange amount calculated by exchange
     132             :  * @param auditor amount calculated by auditor
     133             :  * @param profitable 1 if @a exchange being larger than @a auditor is
     134             :  *           profitable for the exchange for this operation,
     135             :  *           -1 if @a exchange being smaller than @a auditor is
     136             :  *           profitable for the exchange, and 0 if it is unclear
     137             :  */
     138             : static void
     139           0 : report_amount_arithmetic_inconsistency (
     140             :   const char *operation,
     141             :   uint64_t rowid,
     142             :   const struct TALER_Amount *exchange,
     143             :   const struct TALER_Amount *auditor,
     144             :   int profitable)
     145             : {
     146             :   struct TALER_Amount delta;
     147             :   struct TALER_Amount *target;
     148             : 
     149           0 :   if (0 < TALER_amount_cmp (exchange,
     150             :                             auditor))
     151             :   {
     152             :     /* exchange > auditor */
     153           0 :     TALER_ARL_amount_subtract (&delta,
     154             :                                exchange,
     155             :                                auditor);
     156             :   }
     157             :   else
     158             :   {
     159             :     /* auditor < exchange */
     160           0 :     profitable = -profitable;
     161           0 :     TALER_ARL_amount_subtract (&delta,
     162             :                                auditor,
     163             :                                exchange);
     164             :   }
     165           0 :   TALER_ARL_report (report_amount_arithmetic_inconsistencies,
     166           0 :                     GNUNET_JSON_PACK (
     167             :                       GNUNET_JSON_pack_string ("operation",
     168             :                                                operation),
     169             :                       GNUNET_JSON_pack_uint64 ("rowid",
     170             :                                                rowid),
     171             :                       TALER_JSON_pack_amount ("exchange",
     172             :                                               exchange),
     173             :                       TALER_JSON_pack_amount ("auditor",
     174             :                                               auditor),
     175             :                       GNUNET_JSON_pack_int64 ("profitable",
     176             :                                               profitable)));
     177           0 :   if (0 != profitable)
     178             :   {
     179           0 :     target = (1 == profitable)
     180             :              ? &total_arithmetic_delta_plus
     181           0 :              : &total_arithmetic_delta_minus;
     182           0 :     TALER_ARL_amount_add (target,
     183             :                           target,
     184             :                           &delta);
     185             :   }
     186           0 : }
     187             : 
     188             : 
     189             : /**
     190             :  * Report a (serious) inconsistency in the exchange's database with
     191             :  * respect to calculations involving amounts of a coin.
     192             :  *
     193             :  * @param operation what operation had the inconsistency
     194             :  * @param coin_pub affected coin
     195             :  * @param exchange amount calculated by exchange
     196             :  * @param auditor amount calculated by auditor
     197             :  * @param profitable 1 if @a exchange being larger than @a auditor is
     198             :  *           profitable for the exchange for this operation,
     199             :  *           -1 if @a exchange being smaller than @a auditor is
     200             :  *           profitable for the exchange, and 0 if it is unclear
     201             :  */
     202             : static void
     203           1 : report_coin_arithmetic_inconsistency (
     204             :   const char *operation,
     205             :   const struct TALER_CoinSpendPublicKeyP *coin_pub,
     206             :   const struct TALER_Amount *exchange,
     207             :   const struct TALER_Amount *auditor,
     208             :   int profitable)
     209             : {
     210             :   struct TALER_Amount delta;
     211             :   struct TALER_Amount *target;
     212             : 
     213           1 :   if (0 < TALER_amount_cmp (exchange,
     214             :                             auditor))
     215             :   {
     216             :     /* exchange > auditor */
     217           1 :     TALER_ARL_amount_subtract (&delta,
     218             :                                exchange,
     219             :                                auditor);
     220             :   }
     221             :   else
     222             :   {
     223             :     /* auditor < exchange */
     224           0 :     profitable = -profitable;
     225           0 :     TALER_ARL_amount_subtract (&delta,
     226             :                                auditor,
     227             :                                exchange);
     228             :   }
     229           1 :   TALER_ARL_report (report_coin_inconsistencies,
     230           1 :                     GNUNET_JSON_PACK (
     231             :                       GNUNET_JSON_pack_string ("operation",
     232             :                                                operation),
     233             :                       GNUNET_JSON_pack_data_auto ("coin_pub",
     234             :                                                   coin_pub),
     235             :                       TALER_JSON_pack_amount ("exchange",
     236             :                                               exchange),
     237             :                       TALER_JSON_pack_amount ("auditor",
     238             :                                               auditor),
     239             :                       GNUNET_JSON_pack_int64 ("profitable",
     240             :                                               profitable)));
     241           1 :   if (0 != profitable)
     242             :   {
     243           1 :     target = (1 == profitable)
     244             :              ? &total_coin_delta_plus
     245           1 :              : &total_coin_delta_minus;
     246           1 :     TALER_ARL_amount_add (target,
     247             :                           target,
     248             :                           &delta);
     249             :   }
     250           1 : }
     251             : 
     252             : 
     253             : /**
     254             :  * Report a (serious) inconsistency in the exchange's database.
     255             :  *
     256             :  * @param table affected table
     257             :  * @param rowid affected row, 0 if row is missing
     258             :  * @param diagnostic message explaining the problem
     259             :  */
     260             : static void
     261           6 : report_row_inconsistency (const char *table,
     262             :                           uint64_t rowid,
     263             :                           const char *diagnostic)
     264             : {
     265           6 :   TALER_ARL_report (report_row_inconsistencies,
     266           6 :                     GNUNET_JSON_PACK (
     267             :                       GNUNET_JSON_pack_string ("table",
     268             :                                                table),
     269             :                       GNUNET_JSON_pack_uint64 ("row",
     270             :                                                rowid),
     271             :                       GNUNET_JSON_pack_string ("diagnostic",
     272             :                                                diagnostic)));
     273           6 : }
     274             : 
     275             : 
     276             : /* *********************** Analyze aggregations ******************** */
     277             : /* This logic checks that the aggregator did the right thing
     278             :    paying each merchant what they were due (and on time). */
     279             : 
     280             : 
     281             : /**
     282             :  * Information about wire fees charged by the exchange.
     283             :  */
     284             : struct WireFeeInfo
     285             : {
     286             : 
     287             :   /**
     288             :    * Kept in a DLL.
     289             :    */
     290             :   struct WireFeeInfo *next;
     291             : 
     292             :   /**
     293             :    * Kept in a DLL.
     294             :    */
     295             :   struct WireFeeInfo *prev;
     296             : 
     297             :   /**
     298             :    * When does the fee go into effect (inclusive).
     299             :    */
     300             :   struct GNUNET_TIME_Absolute start_date;
     301             : 
     302             :   /**
     303             :    * When does the fee stop being in effect (exclusive).
     304             :    */
     305             :   struct GNUNET_TIME_Absolute end_date;
     306             : 
     307             :   /**
     308             :    * How high is the wire fee.
     309             :    */
     310             :   struct TALER_Amount wire_fee;
     311             : 
     312             :   /**
     313             :    * How high is the closing fee.
     314             :    */
     315             :   struct TALER_Amount closing_fee;
     316             : 
     317             : };
     318             : 
     319             : 
     320             : /**
     321             :  * Closure for callbacks during #analyze_merchants().
     322             :  */
     323             : struct AggregationContext
     324             : {
     325             : 
     326             :   /**
     327             :    * DLL of wire fees charged by the exchange.
     328             :    */
     329             :   struct WireFeeInfo *fee_head;
     330             : 
     331             :   /**
     332             :    * DLL of wire fees charged by the exchange.
     333             :    */
     334             :   struct WireFeeInfo *fee_tail;
     335             : 
     336             :   /**
     337             :    * Final result status.
     338             :    */
     339             :   enum GNUNET_DB_QueryStatus qs;
     340             : };
     341             : 
     342             : 
     343             : /**
     344             :  * Closure for #wire_transfer_information_cb.
     345             :  */
     346             : struct WireCheckContext
     347             : {
     348             : 
     349             :   /**
     350             :    * Corresponding merchant context.
     351             :    */
     352             :   struct AggregationContext *ac;
     353             : 
     354             :   /**
     355             :    * Total deposits claimed by all transactions that were aggregated
     356             :    * under the given @e wtid.
     357             :    */
     358             :   struct TALER_Amount total_deposits;
     359             : 
     360             :   /**
     361             :    * Hash of the wire transfer details of the receiver.
     362             :    */
     363             :   struct GNUNET_HashCode h_wire;
     364             : 
     365             :   /**
     366             :    * Execution time of the wire transfer.
     367             :    */
     368             :   struct GNUNET_TIME_Absolute date;
     369             : 
     370             :   /**
     371             :    * Database transaction status.
     372             :    */
     373             :   enum GNUNET_DB_QueryStatus qs;
     374             : 
     375             : };
     376             : 
     377             : 
     378             : /**
     379             :  * Check coin's transaction history for plausibility.  Does NOT check
     380             :  * the signatures (those are checked independently), but does calculate
     381             :  * the amounts for the aggregation table and checks that the total
     382             :  * claimed coin value is within the value of the coin's denomination.
     383             :  *
     384             :  * @param coin_pub public key of the coin (for reporting)
     385             :  * @param h_contract_terms hash of the proposal for which we calculate the amount
     386             :  * @param merchant_pub public key of the merchant (who is allowed to issue refunds)
     387             :  * @param issue denomination information about the coin
     388             :  * @param tl_head head of transaction history to verify
     389             :  * @param[out] merchant_gain amount the coin contributes to the wire transfer to the merchant
     390             :  * @param[out] deposit_gain amount the coin contributes excluding refunds
     391             :  * @return #GNUNET_OK on success, #GNUNET_SYSERR if the transaction must fail (hard error)
     392             :  */
     393             : static int
     394          58 : check_transaction_history_for_deposit (
     395             :   const struct TALER_CoinSpendPublicKeyP *coin_pub,
     396             :   const struct GNUNET_HashCode *h_contract_terms,
     397             :   const struct TALER_MerchantPublicKeyP *merchant_pub,
     398             :   const struct TALER_DenominationKeyValidityPS *issue,
     399             :   const struct TALER_EXCHANGEDB_TransactionList *tl_head,
     400             :   struct TALER_Amount *merchant_gain,
     401             :   struct TALER_Amount *deposit_gain)
     402             : {
     403             :   struct TALER_Amount expenditures;
     404             :   struct TALER_Amount refunds;
     405             :   struct TALER_Amount spent;
     406             :   struct TALER_Amount merchant_loss;
     407             :   const struct TALER_Amount *deposit_fee;
     408             :   int refund_deposit_fee;
     409             : 
     410          58 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     411             :               "Checking transaction history of coin %s\n",
     412             :               TALER_B2S (coin_pub));
     413          58 :   GNUNET_assert (GNUNET_OK ==
     414             :                  TALER_amount_set_zero (TALER_ARL_currency,
     415             :                                         &expenditures));
     416          58 :   GNUNET_assert (GNUNET_OK ==
     417             :                  TALER_amount_set_zero (TALER_ARL_currency,
     418             :                                         &refunds));
     419          58 :   GNUNET_assert (GNUNET_OK ==
     420             :                  TALER_amount_set_zero (TALER_ARL_currency,
     421             :                                         merchant_gain));
     422          58 :   GNUNET_assert (GNUNET_OK ==
     423             :                  TALER_amount_set_zero (TALER_ARL_currency,
     424             :                                         &merchant_loss));
     425             :   /* Go over transaction history to compute totals; note that we do not bother
     426             :      to reconstruct the order of the events, so instead of subtracting we
     427             :      compute positive (deposit, melt) and negative (refund) values separately
     428             :      here, and then subtract the negative from the positive at the end (after
     429             :      the loops). *///
     430          58 :   refund_deposit_fee = GNUNET_NO;
     431          58 :   deposit_fee = NULL;
     432         191 :   for (const struct TALER_EXCHANGEDB_TransactionList *tl = tl_head;
     433             :        NULL != tl;
     434         133 :        tl = tl->next)
     435             :   {
     436             :     const struct TALER_Amount *amount_with_fee;
     437             :     const struct TALER_Amount *fee_claimed;
     438             : 
     439         133 :     switch (tl->type)
     440             :     {
     441          59 :     case TALER_EXCHANGEDB_TT_DEPOSIT:
     442             :       /* check wire and h_wire are consistent */
     443             :       {
     444             :         struct GNUNET_HashCode hw;
     445             : 
     446          59 :         if (GNUNET_OK !=
     447          59 :             TALER_JSON_merchant_wire_signature_hash (
     448          59 :               tl->details.deposit->receiver_wire_account,
     449             :               &hw))
     450             :         {
     451           0 :           report_row_inconsistency ("deposits",
     452             :                                     tl->serial_id,
     453             :                                     "wire account given is malformed");
     454             :         }
     455          59 :         else if (0 !=
     456          59 :                  GNUNET_memcmp (&hw,
     457             :                                 &tl->details.deposit->h_wire))
     458             :         {
     459           1 :           report_row_inconsistency ("deposits",
     460             :                                     tl->serial_id,
     461             :                                     "h(wire) does not match wire");
     462             :         }
     463             :       }
     464          59 :       amount_with_fee = &tl->details.deposit->amount_with_fee; /* according to exchange*/
     465          59 :       fee_claimed = &tl->details.deposit->deposit_fee; /* Fee according to exchange DB */
     466          59 :       TALER_ARL_amount_add (&expenditures,
     467             :                             &expenditures,
     468             :                             amount_with_fee);
     469             :       /* Check if this deposit is within the remit of the aggregation
     470             :          we are investigating, if so, include it in the totals. */
     471          59 :       if ( (0 == GNUNET_memcmp (merchant_pub,
     472          58 :                                 &tl->details.deposit->merchant_pub)) &&
     473          58 :            (0 == GNUNET_memcmp (h_contract_terms,
     474             :                                 &tl->details.deposit->h_contract_terms)) )
     475             :       {
     476             :         struct TALER_Amount amount_without_fee;
     477             : 
     478          58 :         TALER_ARL_amount_subtract (&amount_without_fee,
     479             :                                    amount_with_fee,
     480             :                                    fee_claimed);
     481          58 :         TALER_ARL_amount_add (merchant_gain,
     482             :                               merchant_gain,
     483             :                               &amount_without_fee);
     484          58 :         GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     485             :                     "Detected applicable deposit of %s\n",
     486             :                     TALER_amount2s (&amount_without_fee));
     487          58 :         deposit_fee = fee_claimed; /* We had a deposit, remember the fee, we may need it */
     488             :       }
     489             :       /* Check that the fees given in the transaction list and in dki match */
     490             :       {
     491             :         struct TALER_Amount fee_expected;
     492             : 
     493             :         /* Fee according to denomination data of auditor */
     494          59 :         TALER_amount_ntoh (&fee_expected,
     495             :                            &issue->fee_deposit);
     496          59 :         if (0 !=
     497          59 :             TALER_amount_cmp (&fee_expected,
     498             :                               fee_claimed))
     499             :         {
     500             :           /* Disagreement in fee structure between auditor and exchange DB! */
     501           0 :           report_amount_arithmetic_inconsistency ("deposit fee",
     502             :                                                   0,
     503             :                                                   fee_claimed,
     504             :                                                   &fee_expected,
     505             :                                                   1);
     506             :         }
     507             :       }
     508          59 :       break;
     509          59 :     case TALER_EXCHANGEDB_TT_MELT:
     510          59 :       amount_with_fee = &tl->details.melt->amount_with_fee;
     511          59 :       fee_claimed = &tl->details.melt->melt_fee;
     512          59 :       TALER_ARL_amount_add (&expenditures,
     513             :                             &expenditures,
     514             :                             amount_with_fee);
     515             :       /* Check that the fees given in the transaction list and in dki match */
     516             :       {
     517             :         struct TALER_Amount fee_expected;
     518             : 
     519          59 :         TALER_amount_ntoh (&fee_expected,
     520             :                            &issue->fee_refresh);
     521          59 :         if (0 !=
     522          59 :             TALER_amount_cmp (&fee_expected,
     523             :                               fee_claimed))
     524             :         {
     525             :           /* Disagreement in fee structure between exchange and auditor */
     526           0 :           report_amount_arithmetic_inconsistency ("melt fee",
     527             :                                                   0,
     528             :                                                   fee_claimed,
     529             :                                                   &fee_expected,
     530             :                                                   1);
     531             :         }
     532             :       }
     533          59 :       break;
     534          15 :     case TALER_EXCHANGEDB_TT_REFUND:
     535          15 :       amount_with_fee = &tl->details.refund->refund_amount;
     536          15 :       fee_claimed = &tl->details.refund->refund_fee;
     537          15 :       TALER_ARL_amount_add (&refunds,
     538             :                             &refunds,
     539             :                             amount_with_fee);
     540          15 :       TALER_ARL_amount_add (&expenditures,
     541             :                             &expenditures,
     542             :                             fee_claimed);
     543             :       /* Check if this refund is within the remit of the aggregation
     544             :          we are investigating, if so, include it in the totals. */
     545          15 :       if ( (0 == GNUNET_memcmp (merchant_pub,
     546          14 :                                 &tl->details.refund->merchant_pub)) &&
     547          14 :            (0 == GNUNET_memcmp (h_contract_terms,
     548             :                                 &tl->details.refund->h_contract_terms)) )
     549             :       {
     550          14 :         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     551             :                     "Detected applicable refund of %s\n",
     552             :                     TALER_amount2s (amount_with_fee));
     553          14 :         TALER_ARL_amount_add (&merchant_loss,
     554             :                               &merchant_loss,
     555             :                               amount_with_fee);
     556             :         /* If there is a refund, we give back the deposit fee */
     557          14 :         refund_deposit_fee = GNUNET_YES;
     558             :       }
     559             :       /* Check that the fees given in the transaction list and in dki match */
     560             :       {
     561             :         struct TALER_Amount fee_expected;
     562             : 
     563          15 :         TALER_amount_ntoh (&fee_expected,
     564             :                            &issue->fee_refund);
     565          15 :         if (0 !=
     566          15 :             TALER_amount_cmp (&fee_expected,
     567             :                               fee_claimed))
     568             :         {
     569             :           /* Disagreement in fee structure between exchange and auditor! */
     570           0 :           report_amount_arithmetic_inconsistency ("refund fee",
     571             :                                                   0,
     572             :                                                   fee_claimed,
     573             :                                                   &fee_expected,
     574             :                                                   1);
     575             :         }
     576             :       }
     577          15 :       break;
     578           0 :     case TALER_EXCHANGEDB_TT_OLD_COIN_RECOUP:
     579           0 :       amount_with_fee = &tl->details.old_coin_recoup->value;
     580             :       /* We count recoups of refreshed coins like refunds for the dirty old
     581             :          coin, as they equivalently _increase_ the remaining value on the
     582             :          _old_ coin */
     583           0 :       TALER_ARL_amount_add (&refunds,
     584             :                             &refunds,
     585             :                             amount_with_fee);
     586           0 :       break;
     587           0 :     case TALER_EXCHANGEDB_TT_RECOUP:
     588             :       /* We count recoups of the coin as expenditures, as it
     589             :          equivalently decreases the remaining value of the recouped coin. */
     590           0 :       amount_with_fee = &tl->details.recoup->value;
     591           0 :       TALER_ARL_amount_add (&expenditures,
     592             :                             &expenditures,
     593             :                             amount_with_fee);
     594           0 :       break;
     595           0 :     case TALER_EXCHANGEDB_TT_RECOUP_REFRESH:
     596             :       /* We count recoups of the coin as expenditures, as it
     597             :          equivalently decreases the remaining value of the recouped coin. */
     598           0 :       amount_with_fee = &tl->details.recoup_refresh->value;
     599           0 :       TALER_ARL_amount_add (&expenditures,
     600             :                             &expenditures,
     601             :                             amount_with_fee);
     602           0 :       break;
     603             :     }
     604         133 :   } /* for 'tl' */
     605             : 
     606          58 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     607             :               "Deposits for this aggregation (after fees) are %s\n",
     608             :               TALER_amount2s (merchant_gain));
     609          58 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     610             :               "Aggregation loss due to refunds is %s\n",
     611             :               TALER_amount2s (&merchant_loss));
     612          58 :   *deposit_gain = *merchant_gain;
     613          58 :   if ( (GNUNET_YES == refund_deposit_fee) &&
     614             :        (NULL != deposit_fee) )
     615             :   {
     616             :     /* We had a /deposit operation AND a /refund operation,
     617             :        and should thus not charge the merchant the /deposit fee */
     618          14 :     TALER_ARL_amount_add (merchant_gain,
     619             :                           merchant_gain,
     620             :                           deposit_fee);
     621             :   }
     622             :   {
     623             :     struct TALER_Amount final_gain;
     624             : 
     625          58 :     if (TALER_ARL_SR_INVALID_NEGATIVE ==
     626          58 :         TALER_ARL_amount_subtract_neg (&final_gain,
     627             :                                        merchant_gain,
     628             :                                        &merchant_loss))
     629             :     {
     630             :       /* refunds above deposits? Bad! */
     631           0 :       report_coin_arithmetic_inconsistency ("refund (merchant)",
     632             :                                             coin_pub,
     633             :                                             merchant_gain,
     634             :                                             &merchant_loss,
     635             :                                             1);
     636             :       /* For the overall aggregation, we should not count this
     637             :          as a NEGATIVE contribution as that is not allowed; so
     638             :          let's count it as zero as that's the best we can do. */
     639           0 :       GNUNET_assert (GNUNET_OK ==
     640             :                      TALER_amount_set_zero (TALER_ARL_currency,
     641             :                                             merchant_gain));
     642             :     }
     643             :     else
     644             :     {
     645          58 :       *merchant_gain = final_gain;
     646             :     }
     647             :   }
     648             : 
     649             : 
     650             :   /* Calculate total balance change, i.e. expenditures (recoup, deposit, refresh)
     651             :      minus refunds (refunds, recoup-to-old) */
     652          58 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     653             :               "Subtracting refunds of %s from coin value loss\n",
     654             :               TALER_amount2s (&refunds));
     655          58 :   if (TALER_ARL_SR_INVALID_NEGATIVE ==
     656          58 :       TALER_ARL_amount_subtract_neg (&spent,
     657             :                                      &expenditures,
     658             :                                      &refunds))
     659             :   {
     660             :     /* refunds above expenditures? Bad! */
     661           0 :     report_coin_arithmetic_inconsistency ("refund (balance)",
     662             :                                           coin_pub,
     663             :                                           &expenditures,
     664             :                                           &refunds,
     665             :                                           1);
     666             :   }
     667             :   else
     668             :   {
     669             :     /* Now check that 'spent' is less or equal than the total coin value */
     670             :     struct TALER_Amount value;
     671             : 
     672          58 :     TALER_amount_ntoh (&value,
     673             :                        &issue->value);
     674          58 :     if (1 == TALER_amount_cmp (&spent,
     675             :                                &value))
     676             :     {
     677             :       /* spent > value */
     678           1 :       report_coin_arithmetic_inconsistency ("spend",
     679             :                                             coin_pub,
     680             :                                             &spent,
     681             :                                             &value,
     682             :                                             -1);
     683             :     }
     684             :   }
     685             : 
     686             : 
     687          58 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     688             :               "Final merchant gain after refunds is %s\n",
     689             :               TALER_amount2s (deposit_gain));
     690          58 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     691             :               "Coin %s contributes %s to contract %s\n",
     692             :               TALER_B2S (coin_pub),
     693             :               TALER_amount2s (merchant_gain),
     694             :               GNUNET_h2s (h_contract_terms));
     695          58 :   return GNUNET_OK;
     696             : }
     697             : 
     698             : 
     699             : /**
     700             :  * Function called with the results of the lookup of the
     701             :  * transaction data associated with a wire transfer identifier.
     702             :  *
     703             :  * @param[in,out] cls a `struct WireCheckContext`
     704             :  * @param rowid which row in the table is the information from (for diagnostics)
     705             :  * @param merchant_pub public key of the merchant (should be same for all callbacks with the same @e cls)
     706             :  * @param h_wire hash of wire transfer details of the merchant (should be same for all callbacks with the same @e cls)
     707             :  * @param account_details where did we transfer the funds?
     708             :  * @param exec_time execution time of the wire transfer (should be same for all callbacks with the same @e cls)
     709             :  * @param h_contract_terms which proposal was this payment about
     710             :  * @param denom_pub denomination of @a coin_pub
     711             :  * @param coin_pub which public key was this payment about
     712             :  * @param coin_value amount contributed by this coin in total (with fee),
     713             :  *                   but excluding refunds by this coin
     714             :  * @param deposit_fee applicable deposit fee for this coin, actual
     715             :  *        fees charged may differ if coin was refunded
     716             :  */
     717             : static void
     718          60 : wire_transfer_information_cb (
     719             :   void *cls,
     720             :   uint64_t rowid,
     721             :   const struct TALER_MerchantPublicKeyP *merchant_pub,
     722             :   const struct GNUNET_HashCode *h_wire,
     723             :   const json_t *account_details,
     724             :   struct GNUNET_TIME_Absolute exec_time,
     725             :   const struct GNUNET_HashCode *h_contract_terms,
     726             :   const struct TALER_DenominationPublicKey *denom_pub,
     727             :   const struct TALER_CoinSpendPublicKeyP *coin_pub,
     728             :   const struct TALER_Amount *coin_value,
     729             :   const struct TALER_Amount *deposit_fee)
     730             : {
     731          60 :   struct WireCheckContext *wcc = cls;
     732             :   const struct TALER_DenominationKeyValidityPS *issue;
     733             :   struct TALER_Amount computed_value;
     734             :   struct TALER_Amount total_deposit_without_refunds;
     735             :   struct TALER_EXCHANGEDB_TransactionList *tl;
     736             :   struct TALER_CoinPublicInfo coin;
     737             :   enum GNUNET_DB_QueryStatus qs;
     738             :   struct GNUNET_HashCode hw;
     739             : 
     740          60 :   if (GNUNET_OK !=
     741          60 :       TALER_JSON_merchant_wire_signature_hash (account_details,
     742             :                                                &hw))
     743             :   {
     744           0 :     report_row_inconsistency ("aggregation",
     745             :                               rowid,
     746             :                               "failed to compute hash of given wire data");
     747             :   }
     748          60 :   else if (0 !=
     749          60 :            GNUNET_memcmp (&hw,
     750             :                           h_wire))
     751             :   {
     752           1 :     report_row_inconsistency ("aggregation",
     753             :                               rowid,
     754             :                               "database contains wrong hash code for wire details");
     755             :   }
     756             : 
     757             :   /* Obtain coin's transaction history */
     758          60 :   qs = TALER_ARL_edb->get_coin_transactions (TALER_ARL_edb->cls,
     759             :                                              coin_pub,
     760             :                                              GNUNET_YES,
     761             :                                              &tl);
     762          60 :   if ( (qs < 0) ||
     763          60 :        (NULL == tl) )
     764             :   {
     765           0 :     wcc->qs = qs;
     766           0 :     report_row_inconsistency ("aggregation",
     767             :                               rowid,
     768             :                               "no transaction history for coin claimed in aggregation");
     769           2 :     return;
     770             :   }
     771          60 :   qs = TALER_ARL_edb->get_known_coin (TALER_ARL_edb->cls,
     772             :                                       coin_pub,
     773             :                                       &coin);
     774          60 :   if (qs <= 0)
     775             :   {
     776             :     /* this should be a foreign key violation at this point! */
     777           0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     778           0 :     wcc->qs = qs;
     779           0 :     report_row_inconsistency ("aggregation",
     780             :                               rowid,
     781             :                               "could not get coin details for coin claimed in aggregation");
     782           0 :     TALER_ARL_edb->free_coin_transaction_list (TALER_ARL_edb->cls,
     783             :                                                tl);
     784           0 :     return;
     785             :   }
     786             : 
     787          60 :   qs = TALER_ARL_get_denomination_info_by_hash (&coin.denom_pub_hash,
     788             :                                                 &issue);
     789          60 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
     790             :   {
     791           0 :     GNUNET_CRYPTO_rsa_signature_free (coin.denom_sig.rsa_signature);
     792           0 :     TALER_ARL_edb->free_coin_transaction_list (TALER_ARL_edb->cls,
     793             :                                                tl);
     794           0 :     if (0 == qs)
     795           0 :       report_row_inconsistency ("aggregation",
     796             :                                 rowid,
     797             :                                 "could not find denomination key for coin claimed in aggregation");
     798             :     else
     799           0 :       wcc->qs = qs;
     800           0 :     return;
     801             :   }
     802          60 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     803             :               "Testing coin `%s' for validity\n",
     804             :               TALER_B2S (&coin.coin_pub));
     805          60 :   if (GNUNET_OK !=
     806          60 :       TALER_test_coin_valid (&coin,
     807             :                              denom_pub))
     808             :   {
     809           2 :     TALER_ARL_report (report_bad_sig_losses,
     810           2 :                       GNUNET_JSON_PACK (
     811             :                         GNUNET_JSON_pack_string ("operation",
     812             :                                                  "wire"),
     813             :                         GNUNET_JSON_pack_uint64 ("row",
     814             :                                                  rowid),
     815             :                         TALER_JSON_pack_amount ("loss",
     816             :                                                 coin_value),
     817             :                         GNUNET_JSON_pack_data_auto ("coin_pub",
     818             :                                                     &coin.coin_pub)));
     819           2 :     TALER_ARL_amount_add (&total_bad_sig_loss,
     820             :                           &total_bad_sig_loss,
     821             :                           coin_value);
     822           2 :     GNUNET_CRYPTO_rsa_signature_free (coin.denom_sig.rsa_signature);
     823           2 :     TALER_ARL_edb->free_coin_transaction_list (TALER_ARL_edb->cls,
     824             :                                                tl);
     825           2 :     report_row_inconsistency ("deposit",
     826             :                               rowid,
     827             :                               "coin denomination signature invalid");
     828           2 :     return;
     829             :   }
     830          58 :   GNUNET_CRYPTO_rsa_signature_free (coin.denom_sig.rsa_signature);
     831          58 :   coin.denom_sig.rsa_signature = NULL; /* just to be sure */
     832          58 :   GNUNET_assert (NULL != issue); /* mostly to help static analysis */
     833             :   /* Check transaction history to see if it supports aggregate
     834             :      valuation */
     835          58 :   if (GNUNET_OK !=
     836          58 :       check_transaction_history_for_deposit (coin_pub,
     837             :                                              h_contract_terms,
     838             :                                              merchant_pub,
     839             :                                              issue,
     840             :                                              tl,
     841             :                                              &computed_value,
     842             :                                              &total_deposit_without_refunds))
     843             :   {
     844           0 :     TALER_ARL_edb->free_coin_transaction_list (TALER_ARL_edb->cls,
     845             :                                                tl);
     846           0 :     wcc->qs = GNUNET_DB_STATUS_HARD_ERROR;
     847           0 :     return;
     848             :   }
     849          58 :   TALER_ARL_edb->free_coin_transaction_list (TALER_ARL_edb->cls,
     850             :                                              tl);
     851          58 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     852             :               "Coin contributes %s to aggregate (deposits after fees and refunds)\n",
     853             :               TALER_amount2s (&computed_value));
     854             :   {
     855             :     struct TALER_Amount coin_value_without_fee;
     856             : 
     857          58 :     if (TALER_ARL_SR_INVALID_NEGATIVE ==
     858          58 :         TALER_ARL_amount_subtract_neg (&coin_value_without_fee,
     859             :                                        coin_value,
     860             :                                        deposit_fee))
     861             :     {
     862           0 :       wcc->qs = GNUNET_DB_STATUS_HARD_ERROR;
     863           0 :       report_amount_arithmetic_inconsistency (
     864             :         "aggregation (fee structure)",
     865             :         rowid,
     866             :         coin_value,
     867             :         deposit_fee,
     868             :         -1);
     869           0 :       return;
     870             :     }
     871          58 :     if (0 !=
     872          58 :         TALER_amount_cmp (&total_deposit_without_refunds,
     873             :                           &coin_value_without_fee))
     874             :     {
     875           0 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     876             :                   "Expected coin contribution of %s to aggregate\n",
     877             :                   TALER_amount2s (&coin_value_without_fee));
     878           0 :       report_amount_arithmetic_inconsistency (
     879             :         "aggregation (contribution)",
     880             :         rowid,
     881             :         &coin_value_without_fee,
     882             :         &
     883             :         total_deposit_without_refunds,
     884             :         -1);
     885             :     }
     886             :   }
     887             :   /* Check other details of wire transfer match */
     888          58 :   if (0 != GNUNET_memcmp (h_wire,
     889             :                           &wcc->h_wire))
     890             :   {
     891           1 :     report_row_inconsistency ("aggregation",
     892             :                               rowid,
     893             :                               "target of outgoing wire transfer do not match hash of wire from deposit");
     894             :   }
     895          58 :   if (exec_time.abs_value_us != wcc->date.abs_value_us)
     896             :   {
     897             :     /* This should be impossible from database constraints */
     898           0 :     GNUNET_break (0);
     899           0 :     report_row_inconsistency ("aggregation",
     900             :                               rowid,
     901             :                               "date given in aggregate does not match wire transfer date");
     902             :   }
     903             : 
     904             :   /* Add coin's contribution to total aggregate value */
     905             :   {
     906             :     struct TALER_Amount res;
     907             : 
     908          58 :     TALER_ARL_amount_add (&res,
     909             :                           &wcc->total_deposits,
     910             :                           &computed_value);
     911          58 :     wcc->total_deposits = res;
     912             :   }
     913             : }
     914             : 
     915             : 
     916             : /**
     917             :  * Lookup the wire fee that the exchange charges at @a timestamp.
     918             :  *
     919             :  * @param ac context for caching the result
     920             :  * @param method method of the wire plugin
     921             :  * @param timestamp time for which we need the fee
     922             :  * @return NULL on error (fee unknown)
     923             :  */
     924             : static const struct TALER_Amount *
     925          30 : get_wire_fee (struct AggregationContext *ac,
     926             :               const char *method,
     927             :               struct GNUNET_TIME_Absolute timestamp)
     928             : {
     929             :   struct WireFeeInfo *wfi;
     930             :   struct WireFeeInfo *pos;
     931             :   struct TALER_MasterSignatureP master_sig;
     932             : 
     933             :   /* Check if fee is already loaded in cache */
     934          30 :   for (pos = ac->fee_head; NULL != pos; pos = pos->next)
     935             :   {
     936          13 :     if ( (pos->start_date.abs_value_us <= timestamp.abs_value_us) &&
     937          13 :          (pos->end_date.abs_value_us > timestamp.abs_value_us) )
     938          13 :       return &pos->wire_fee;
     939           0 :     if (pos->start_date.abs_value_us > timestamp.abs_value_us)
     940           0 :       break;
     941             :   }
     942             : 
     943             :   /* Lookup fee in exchange database */
     944          17 :   wfi = GNUNET_new (struct WireFeeInfo);
     945          17 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
     946          17 :       TALER_ARL_edb->get_wire_fee (TALER_ARL_edb->cls,
     947             :                                    method,
     948             :                                    timestamp,
     949             :                                    &wfi->start_date,
     950             :                                    &wfi->end_date,
     951             :                                    &wfi->wire_fee,
     952             :                                    &wfi->closing_fee,
     953             :                                    &master_sig))
     954             :   {
     955           0 :     GNUNET_break (0);
     956           0 :     GNUNET_free (wfi);
     957           0 :     return NULL;
     958             :   }
     959             : 
     960             :   /* Check signature. (This is not terribly meaningful as the exchange can
     961             :      easily make this one up, but it means that we have proof that the master
     962             :      key was used for inconsistent wire fees if a merchant complains.) */
     963             :   {
     964          17 :     if (GNUNET_OK !=
     965          17 :         TALER_exchange_offline_wire_fee_verify (
     966             :           method,
     967             :           wfi->start_date,
     968             :           wfi->end_date,
     969          17 :           &wfi->wire_fee,
     970          17 :           &wfi->closing_fee,
     971             :           &TALER_ARL_master_pub,
     972             :           &master_sig))
     973             :     {
     974           1 :       report_row_inconsistency ("wire-fee",
     975             :                                 timestamp.abs_value_us,
     976             :                                 "wire fee signature invalid at given time");
     977             :     }
     978             :   }
     979             : 
     980             :   /* Established fee, keep in sorted list */
     981          17 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     982             :               "Wire fee is %s starting at %s\n",
     983             :               TALER_amount2s (&wfi->wire_fee),
     984             :               GNUNET_STRINGS_absolute_time_to_string (wfi->start_date));
     985          17 :   if ( (NULL == pos) ||
     986           0 :        (NULL == pos->prev) )
     987          17 :     GNUNET_CONTAINER_DLL_insert (ac->fee_head,
     988             :                                  ac->fee_tail,
     989             :                                  wfi);
     990             :   else
     991           0 :     GNUNET_CONTAINER_DLL_insert_after (ac->fee_head,
     992             :                                        ac->fee_tail,
     993             :                                        pos->prev,
     994             :                                        wfi);
     995             :   /* Check non-overlaping fee invariant */
     996          17 :   if ( (NULL != wfi->prev) &&
     997           0 :        (wfi->prev->end_date.abs_value_us > wfi->start_date.abs_value_us) )
     998             :   {
     999           0 :     TALER_ARL_report (report_fee_time_inconsistencies,
    1000           0 :                       GNUNET_JSON_PACK (
    1001             :                         GNUNET_JSON_pack_string ("type",
    1002             :                                                  method),
    1003             :                         GNUNET_JSON_pack_string ("diagnostic",
    1004             :                                                  "start date before previous end date"),
    1005             :                         TALER_JSON_pack_time_abs_human ("time",
    1006             :                                                         wfi->start_date)));
    1007             :   }
    1008          17 :   if ( (NULL != wfi->next) &&
    1009           0 :        (wfi->next->start_date.abs_value_us >= wfi->end_date.abs_value_us) )
    1010             :   {
    1011           0 :     TALER_ARL_report (report_fee_time_inconsistencies,
    1012           0 :                       GNUNET_JSON_PACK (
    1013             :                         GNUNET_JSON_pack_string ("type",
    1014             :                                                  method),
    1015             :                         GNUNET_JSON_pack_string ("diagnostic",
    1016             :                                                  "end date date after next start date"),
    1017             :                         TALER_JSON_pack_time_abs_human ("time",
    1018             :                                                         wfi->end_date)));
    1019             :   }
    1020          17 :   return &wfi->wire_fee;
    1021             : }
    1022             : 
    1023             : 
    1024             : /**
    1025             :  * Check that a wire transfer made by the exchange is valid
    1026             :  * (has matching deposits).
    1027             :  *
    1028             :  * @param cls a `struct AggregationContext`
    1029             :  * @param rowid identifier of the respective row in the database
    1030             :  * @param date timestamp of the wire transfer (roughly)
    1031             :  * @param wtid wire transfer subject
    1032             :  * @param wire wire transfer details of the receiver
    1033             :  * @param amount amount that was wired
    1034             :  * @return #GNUNET_OK to continue, #GNUNET_SYSERR to stop iteration
    1035             :  */
    1036             : static int
    1037          30 : check_wire_out_cb (void *cls,
    1038             :                    uint64_t rowid,
    1039             :                    struct GNUNET_TIME_Absolute date,
    1040             :                    const struct TALER_WireTransferIdentifierRawP *wtid,
    1041             :                    const json_t *wire,
    1042             :                    const struct TALER_Amount *amount)
    1043             : {
    1044          30 :   struct AggregationContext *ac = cls;
    1045             :   struct WireCheckContext wcc;
    1046             :   struct TALER_Amount final_amount;
    1047             :   struct TALER_Amount exchange_gain;
    1048             :   enum GNUNET_DB_QueryStatus qs;
    1049             :   char *method;
    1050             : 
    1051             :   /* should be monotonically increasing */
    1052          30 :   GNUNET_assert (rowid >= ppa.last_wire_out_serial_id);
    1053          30 :   ppa.last_wire_out_serial_id = rowid + 1;
    1054             : 
    1055          30 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1056             :               "Checking wire transfer %s over %s performed on %s\n",
    1057             :               TALER_B2S (wtid),
    1058             :               TALER_amount2s (amount),
    1059             :               GNUNET_STRINGS_absolute_time_to_string (date));
    1060          30 :   if (NULL == (method = TALER_JSON_wire_to_method (wire)))
    1061             :   {
    1062           0 :     report_row_inconsistency ("wire_out",
    1063             :                               rowid,
    1064             :                               "specified wire address lacks method");
    1065           0 :     return GNUNET_OK;
    1066             :   }
    1067             : 
    1068          30 :   wcc.ac = ac;
    1069          30 :   wcc.qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
    1070          30 :   wcc.date = date;
    1071          30 :   GNUNET_assert (GNUNET_OK ==
    1072             :                  TALER_amount_set_zero (amount->currency,
    1073             :                                         &wcc.total_deposits));
    1074          30 :   if (GNUNET_OK !=
    1075          30 :       TALER_JSON_merchant_wire_signature_hash (wire,
    1076             :                                                &wcc.h_wire))
    1077             :   {
    1078           0 :     GNUNET_break (0);
    1079           0 :     GNUNET_free (method);
    1080           0 :     return GNUNET_SYSERR;
    1081             :   }
    1082          30 :   qs = TALER_ARL_edb->lookup_wire_transfer (TALER_ARL_edb->cls,
    1083             :                                             wtid,
    1084             :                                             &wire_transfer_information_cb,
    1085             :                                             &wcc);
    1086          30 :   if (0 > qs)
    1087             :   {
    1088           0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1089           0 :     ac->qs = qs;
    1090           0 :     GNUNET_free (method);
    1091           0 :     return GNUNET_SYSERR;
    1092             :   }
    1093          30 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != wcc.qs)
    1094             :   {
    1095             :     /* Note: detailed information was already logged
    1096             :        in #wire_transfer_information_cb, so here we
    1097             :        only log for debugging */
    1098           0 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1099             :                 "Inconsitency for wire_out %llu (WTID %s) detected\n",
    1100             :                 (unsigned long long) rowid,
    1101             :                 TALER_B2S (wtid));
    1102             :   }
    1103             : 
    1104             : 
    1105             :   /* Subtract aggregation fee from total (if possible) */
    1106             :   {
    1107             :     const struct TALER_Amount *wire_fee;
    1108             : 
    1109          30 :     wire_fee = get_wire_fee (ac,
    1110             :                              method,
    1111             :                              date);
    1112          30 :     if (NULL == wire_fee)
    1113             :     {
    1114           0 :       report_row_inconsistency ("wire-fee",
    1115             :                                 date.abs_value_us,
    1116             :                                 "wire fee unavailable for given time");
    1117             :       /* If fee is unknown, we just assume the fee is zero */
    1118           0 :       final_amount = wcc.total_deposits;
    1119             :     }
    1120          30 :     else if (TALER_ARL_SR_INVALID_NEGATIVE ==
    1121          30 :              TALER_ARL_amount_subtract_neg (&final_amount,
    1122             :                                             &wcc.total_deposits,
    1123             :                                             wire_fee))
    1124             :     {
    1125           0 :       report_amount_arithmetic_inconsistency (
    1126             :         "wire out (fee structure)",
    1127             :         rowid,
    1128             :         &wcc.total_deposits,
    1129             :         wire_fee,
    1130             :         -1);
    1131             :       /* If fee arithmetic fails, we just assume the fee is zero */
    1132           0 :       final_amount = wcc.total_deposits;
    1133             :     }
    1134             :   }
    1135          30 :   GNUNET_free (method);
    1136             : 
    1137             :   /* Round down to amount supported by wire method */
    1138          30 :   GNUNET_break (GNUNET_SYSERR !=
    1139             :                 TALER_amount_round_down (&final_amount,
    1140             :                                          &TALER_ARL_currency_round_unit));
    1141             : 
    1142             :   /* Calculate the exchange's gain as the fees plus rounding differences! */
    1143          30 :   TALER_ARL_amount_subtract (&exchange_gain,
    1144             :                              &wcc.total_deposits,
    1145             :                              &final_amount);
    1146             :   /* Sum up aggregation fees (we simply include the rounding gains) */
    1147          30 :   TALER_ARL_amount_add (&total_aggregation_fee_income,
    1148             :                         &total_aggregation_fee_income,
    1149             :                         &exchange_gain);
    1150             : 
    1151             :   /* Check that calculated amount matches actual amount */
    1152          30 :   if (0 != TALER_amount_cmp (amount,
    1153             :                              &final_amount))
    1154             :   {
    1155             :     struct TALER_Amount delta;
    1156             : 
    1157           4 :     if (0 < TALER_amount_cmp (amount,
    1158             :                               &final_amount))
    1159             :     {
    1160             :       /* amount > final_amount */
    1161           3 :       TALER_ARL_amount_subtract (&delta,
    1162             :                                  amount,
    1163             :                                  &final_amount);
    1164           3 :       TALER_ARL_amount_add (&total_wire_out_delta_plus,
    1165             :                             &total_wire_out_delta_plus,
    1166             :                             &delta);
    1167             :     }
    1168             :     else
    1169             :     {
    1170             :       /* amount < final_amount */
    1171           1 :       TALER_ARL_amount_subtract (&delta,
    1172             :                                  &final_amount,
    1173             :                                  amount);
    1174           1 :       TALER_ARL_amount_add (&total_wire_out_delta_minus,
    1175             :                             &total_wire_out_delta_minus,
    1176             :                             &delta);
    1177             :     }
    1178             : 
    1179           4 :     TALER_ARL_report (report_wire_out_inconsistencies,
    1180           4 :                       GNUNET_JSON_PACK (
    1181             :                         GNUNET_JSON_pack_object_incref ("destination_account",
    1182             :                                                         (json_t *) wire),
    1183             :                         GNUNET_JSON_pack_uint64 ("rowid",
    1184             :                                                  rowid),
    1185             :                         TALER_JSON_pack_amount ("expected",
    1186             :                                                 &final_amount),
    1187             :                         TALER_JSON_pack_amount ("claimed",
    1188             :                                                 amount)));
    1189           4 :     if (TALER_ARL_do_abort ())
    1190           0 :       return GNUNET_SYSERR;
    1191           4 :     return GNUNET_OK;
    1192             :   }
    1193          26 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1194             :               "Aggregation unit %s is OK\n",
    1195             :               TALER_B2S (wtid));
    1196          26 :   if (TALER_ARL_do_abort ())
    1197           0 :     return GNUNET_SYSERR;
    1198          26 :   return GNUNET_OK;
    1199             : }
    1200             : 
    1201             : 
    1202             : /**
    1203             :  * Analyze the exchange aggregator's payment processing.
    1204             :  *
    1205             :  * @param cls closure
    1206             :  * @return transaction status code
    1207             :  */
    1208             : static enum GNUNET_DB_QueryStatus
    1209          83 : analyze_aggregations (void *cls)
    1210             : {
    1211             :   struct AggregationContext ac;
    1212             :   struct WireFeeInfo *wfi;
    1213             :   enum GNUNET_DB_QueryStatus qsx;
    1214             :   enum GNUNET_DB_QueryStatus qs;
    1215             :   enum GNUNET_DB_QueryStatus qsp;
    1216             : 
    1217             :   (void) cls;
    1218          83 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1219             :               "Analyzing aggregations\n");
    1220          83 :   qsp = TALER_ARL_adb->get_auditor_progress_aggregation (TALER_ARL_adb->cls,
    1221             :                                                          &TALER_ARL_master_pub,
    1222             :                                                          &ppa);
    1223          83 :   if (0 > qsp)
    1224             :   {
    1225           0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qsp);
    1226           0 :     return qsp;
    1227             :   }
    1228          83 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qsp)
    1229             :   {
    1230          67 :     GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
    1231             :                 "First analysis using this auditor, starting audit from scratch\n");
    1232             :   }
    1233             :   else
    1234             :   {
    1235          16 :     ppa_start = ppa;
    1236          16 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1237             :                 "Resuming aggregation audit at %llu\n",
    1238             :                 (unsigned long long) ppa.last_wire_out_serial_id);
    1239             :   }
    1240             : 
    1241          83 :   memset (&ac,
    1242             :           0,
    1243             :           sizeof (ac));
    1244          83 :   qsx = TALER_ARL_adb->get_wire_fee_summary (TALER_ARL_adb->cls,
    1245             :                                              &TALER_ARL_master_pub,
    1246             :                                              &total_aggregation_fee_income);
    1247          83 :   if (0 > qsx)
    1248             :   {
    1249           0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qsx);
    1250           0 :     return qsx;
    1251             :   }
    1252          83 :   ac.qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
    1253          83 :   qs = TALER_ARL_edb->select_wire_out_above_serial_id (
    1254          83 :     TALER_ARL_edb->cls,
    1255             :     ppa.last_wire_out_serial_id,
    1256             :     &check_wire_out_cb,
    1257             :     &ac);
    1258          83 :   if (0 > qs)
    1259             :   {
    1260           0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1261           0 :     ac.qs = qs;
    1262             :   }
    1263         100 :   while (NULL != (wfi = ac.fee_head))
    1264             :   {
    1265          17 :     GNUNET_CONTAINER_DLL_remove (ac.fee_head,
    1266             :                                  ac.fee_tail,
    1267             :                                  wfi);
    1268          17 :     GNUNET_free (wfi);
    1269             :   }
    1270          83 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    1271             :   {
    1272             :     /* there were no wire out entries to be looked at, we are done */
    1273          66 :     return qs;
    1274             :   }
    1275          17 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != ac.qs)
    1276             :   {
    1277           0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == ac.qs);
    1278           0 :     return ac.qs;
    1279             :   }
    1280          17 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qsx)
    1281          17 :     ac.qs = TALER_ARL_adb->insert_wire_fee_summary (
    1282          17 :       TALER_ARL_adb->cls,
    1283             :       &TALER_ARL_master_pub,
    1284             :       &total_aggregation_fee_income);
    1285             :   else
    1286           0 :     ac.qs = TALER_ARL_adb->update_wire_fee_summary (
    1287           0 :       TALER_ARL_adb->cls,
    1288             :       &TALER_ARL_master_pub,
    1289             :       &total_aggregation_fee_income);
    1290          17 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != ac.qs)
    1291             :   {
    1292           0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == ac.qs);
    1293           0 :     return ac.qs;
    1294             :   }
    1295          17 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qsp)
    1296           0 :     qs = TALER_ARL_adb->update_auditor_progress_aggregation (
    1297           0 :       TALER_ARL_adb->cls,
    1298             :       &TALER_ARL_master_pub,
    1299             :       &ppa);
    1300             :   else
    1301          17 :     qs = TALER_ARL_adb->insert_auditor_progress_aggregation (
    1302          17 :       TALER_ARL_adb->cls,
    1303             :       &TALER_ARL_master_pub,
    1304             :       &ppa);
    1305          17 :   if (0 >= qs)
    1306             :   {
    1307           0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1308             :                 "Failed to update auditor DB, not recording progress\n");
    1309           0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1310           0 :     return qs;
    1311             :   }
    1312          17 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1313             :               "Concluded aggregation audit step at %llu\n",
    1314             :               (unsigned long long) ppa.last_wire_out_serial_id);
    1315             : 
    1316          17 :   return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
    1317             : }
    1318             : 
    1319             : 
    1320             : /**
    1321             :  * Main function that will be run.
    1322             :  *
    1323             :  * @param cls closure
    1324             :  * @param args remaining command-line arguments
    1325             :  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
    1326             :  * @param c configuration
    1327             :  */
    1328             : static void
    1329          83 : run (void *cls,
    1330             :      char *const *args,
    1331             :      const char *cfgfile,
    1332             :      const struct GNUNET_CONFIGURATION_Handle *c)
    1333             : {
    1334             :   (void) cls;
    1335             :   (void) args;
    1336             :   (void) cfgfile;
    1337          83 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1338             :               "Launching auditor\n");
    1339          83 :   if (GNUNET_OK !=
    1340          83 :       TALER_ARL_init (c))
    1341             :   {
    1342           0 :     global_ret = EXIT_FAILURE;
    1343           0 :     return;
    1344             :   }
    1345          83 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1346             :               "Starting audit\n");
    1347          83 :   GNUNET_assert (GNUNET_OK ==
    1348             :                  TALER_amount_set_zero (TALER_ARL_currency,
    1349             :                                         &total_aggregation_fee_income));
    1350          83 :   GNUNET_assert (GNUNET_OK ==
    1351             :                  TALER_amount_set_zero (TALER_ARL_currency,
    1352             :                                         &total_wire_out_delta_plus));
    1353          83 :   GNUNET_assert (GNUNET_OK ==
    1354             :                  TALER_amount_set_zero (TALER_ARL_currency,
    1355             :                                         &total_wire_out_delta_minus));
    1356          83 :   GNUNET_assert (GNUNET_OK ==
    1357             :                  TALER_amount_set_zero (TALER_ARL_currency,
    1358             :                                         &total_arithmetic_delta_plus));
    1359          83 :   GNUNET_assert (GNUNET_OK ==
    1360             :                  TALER_amount_set_zero (TALER_ARL_currency,
    1361             :                                         &total_arithmetic_delta_minus));
    1362          83 :   GNUNET_assert (GNUNET_OK ==
    1363             :                  TALER_amount_set_zero (TALER_ARL_currency,
    1364             :                                         &total_coin_delta_plus));
    1365          83 :   GNUNET_assert (GNUNET_OK ==
    1366             :                  TALER_amount_set_zero (TALER_ARL_currency,
    1367             :                                         &total_coin_delta_minus));
    1368          83 :   GNUNET_assert (GNUNET_OK ==
    1369             :                  TALER_amount_set_zero (TALER_ARL_currency,
    1370             :                                         &total_bad_sig_loss));
    1371          83 :   GNUNET_assert (NULL !=
    1372             :                  (report_row_inconsistencies
    1373             :                     = json_array ()));
    1374          83 :   GNUNET_assert (NULL !=
    1375             :                  (report_wire_out_inconsistencies
    1376             :                     = json_array ()));
    1377          83 :   GNUNET_assert (NULL !=
    1378             :                  (report_coin_inconsistencies
    1379             :                     = json_array ()));
    1380          83 :   GNUNET_assert (NULL !=
    1381             :                  (report_amount_arithmetic_inconsistencies
    1382             :                     = json_array ()));
    1383          83 :   GNUNET_assert (NULL !=
    1384             :                  (report_bad_sig_losses
    1385             :                     = json_array ()));
    1386          83 :   GNUNET_assert (NULL !=
    1387             :                  (report_fee_time_inconsistencies
    1388             :                     = json_array ()));
    1389          83 :   if (GNUNET_OK !=
    1390          83 :       TALER_ARL_setup_sessions_and_run (&analyze_aggregations,
    1391             :                                         NULL))
    1392             :   {
    1393           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1394             :                 "Audit failed\n");
    1395           0 :     TALER_ARL_done (NULL);
    1396           0 :     global_ret = EXIT_FAILURE;
    1397           0 :     return;
    1398             :   }
    1399          83 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1400             :               "Audit complete\n");
    1401          83 :   TALER_ARL_done (GNUNET_JSON_PACK (
    1402             :                     /* blocks #1 */
    1403             :                     GNUNET_JSON_pack_array_steal (
    1404             :                       "wire_out_inconsistencies",
    1405             :                       report_wire_out_inconsistencies),
    1406             :                     /* Tested in test-auditor.sh #23 */
    1407             :                     TALER_JSON_pack_amount (
    1408             :                       "total_wire_out_delta_plus",
    1409             :                       &total_wire_out_delta_plus),
    1410             :                     /* Tested in test-auditor.sh #23 */
    1411             :                     TALER_JSON_pack_amount (
    1412             :                       "total_wire_out_delta_minus",
    1413             :                       &total_wire_out_delta_minus),
    1414             :                     /* Tested in test-auditor.sh #28/32 */
    1415             :                     GNUNET_JSON_pack_array_steal ("bad_sig_losses",
    1416             :                                                   report_bad_sig_losses),
    1417             :                     /* Tested in test-auditor.sh #28/32 */
    1418             :                     TALER_JSON_pack_amount ("total_bad_sig_loss",
    1419             :                                             &total_bad_sig_loss),
    1420             :                     /* block #2 */
    1421             :                     /* Tested in test-auditor.sh #15 */
    1422             :                     GNUNET_JSON_pack_array_steal (
    1423             :                       "row_inconsistencies",
    1424             :                       report_row_inconsistencies),
    1425             :                     GNUNET_JSON_pack_array_steal (
    1426             :                       "coin_inconsistencies",
    1427             :                       report_coin_inconsistencies),
    1428             :                     TALER_JSON_pack_amount ("total_coin_delta_plus",
    1429             :                                             &total_coin_delta_plus),
    1430             :                     TALER_JSON_pack_amount ("total_coin_delta_minus",
    1431             :                                             &total_coin_delta_minus),
    1432             :                     GNUNET_JSON_pack_array_steal (
    1433             :                       "amount_arithmetic_inconsistencies",
    1434             :                       report_amount_arithmetic_inconsistencies),
    1435             :                     /* block #3 */
    1436             :                     TALER_JSON_pack_amount (
    1437             :                       "total_arithmetic_delta_plus",
    1438             :                       &total_arithmetic_delta_plus),
    1439             :                     TALER_JSON_pack_amount (
    1440             :                       "total_arithmetic_delta_minus",
    1441             :                       &total_arithmetic_delta_minus),
    1442             :                     TALER_JSON_pack_amount (
    1443             :                       "total_aggregation_fee_income",
    1444             :                       &total_aggregation_fee_income),
    1445             :                     GNUNET_JSON_pack_uint64 (
    1446             :                       "start_ppa_wire_out_serial_id",
    1447             :                       ppa_start.last_wire_out_serial_id),
    1448             :                     GNUNET_JSON_pack_uint64 (
    1449             :                       "end_ppa_wire_out_serial_id",
    1450             :                       ppa.last_wire_out_serial_id),
    1451             :                     /* block #4 */
    1452             :                     TALER_JSON_pack_time_abs_human (
    1453             :                       "auditor_start_time",
    1454             :                       start_time),
    1455             :                     TALER_JSON_pack_time_abs_human (
    1456             :                       "auditor_end_time",
    1457             :                       GNUNET_TIME_absolute_get ()),
    1458             :                     GNUNET_JSON_pack_array_steal (
    1459             :                       "wire_fee_time_inconsistencies",
    1460             :                       report_fee_time_inconsistencies)));
    1461             : }
    1462             : 
    1463             : 
    1464             : /**
    1465             :  * The main function to audit the exchange's aggregation processing.
    1466             :  *
    1467             :  * @param argc number of arguments from the command line
    1468             :  * @param argv command line arguments
    1469             :  * @return 0 ok, 1 on error
    1470             :  */
    1471             : int
    1472          83 : main (int argc,
    1473             :       char *const *argv)
    1474             : {
    1475          83 :   const struct GNUNET_GETOPT_CommandLineOption options[] = {
    1476          83 :     GNUNET_GETOPT_option_flag ('i',
    1477             :                                "internal",
    1478             :                                "perform checks only applicable for exchange-internal audits",
    1479             :                                &internal_checks),
    1480          83 :     GNUNET_GETOPT_option_base32_auto ('m',
    1481             :                                       "exchange-key",
    1482             :                                       "KEY",
    1483             :                                       "public key of the exchange (Crockford base32 encoded)",
    1484             :                                       &TALER_ARL_master_pub),
    1485          83 :     GNUNET_GETOPT_option_timetravel ('T',
    1486             :                                      "timetravel"),
    1487             :     GNUNET_GETOPT_OPTION_END
    1488             :   };
    1489             :   enum GNUNET_GenericReturnValue ret;
    1490             : 
    1491             :   /* force linker to link against libtalerutil; if we do
    1492             :      not do this, the linker may "optimize" libtalerutil
    1493             :      away and skip #TALER_OS_init(), which we do need */
    1494          83 :   (void) TALER_project_data_default ();
    1495          83 :   if (GNUNET_OK !=
    1496          83 :       GNUNET_STRINGS_get_utf8_args (argc, argv,
    1497             :                                     &argc, &argv))
    1498           0 :     return EXIT_INVALIDARGUMENT;
    1499          83 :   ret = GNUNET_PROGRAM_run (
    1500             :     argc,
    1501             :     argv,
    1502             :     "taler-helper-auditor-aggregation",
    1503             :     gettext_noop ("Audit Taler exchange aggregation activity"),
    1504             :     options,
    1505             :     &run,
    1506             :     NULL);
    1507          83 :   GNUNET_free_nz ((void *) argv);
    1508          83 :   if (GNUNET_SYSERR == ret)
    1509           0 :     return EXIT_INVALIDARGUMENT;
    1510          83 :   if (GNUNET_NO == ret)
    1511           0 :     return EXIT_SUCCESS;
    1512          83 :   return global_ret;
    1513             : }
    1514             : 
    1515             : 
    1516             : /* end of taler-helper-auditor-aggregation.c */

Generated by: LCOV version 1.14