LCOV - code coverage report
Current view: top level - auditor - taler-helper-auditor-aggregation.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 42.8 % 449 192
Test Date: 2026-11-03 22:15:25 Functions: 66.7 % 12 8

            Line data    Source code
       1              : /*
       2              :   This file is part of TALER
       3              :   Copyright (C) 2016-2024 Taler Systems SA
       4              : 
       5              :   TALER is free software; you can redistribute it and/or modify it under the
       6              :   terms of the GNU Affero Public License as published by the Free Software
       7              :   Foundation; either version 3, or (at your option) any later version.
       8              : 
       9              :   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
      10              :   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
      11              :   A PARTICULAR PURPOSE.  See the GNU Affero Public License for more details.
      12              : 
      13              :   You should have received a copy of the GNU Affero Public License along with
      14              :   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
      15              : */
      16              : /**
      17              :  * @file auditor/taler-helper-auditor-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 "auditordb_lib.h"
      24              : /* WIRE_TRANSFER_OUT's callback typedef also lives in exchangedb_lib.h, so its
      25              :    closure override must be established before that header is included. */
      26              : struct AggregationContext;
      27              : #define TALER_EXCHANGEDB_WIRE_TRANSFER_OUT_RESULT_CLOSURE struct \
      28              :         AggregationContext
      29              : #include "exchangedb_lib.h"
      30              : #include "taler/taler_bank_service.h"
      31              : #include "taler/taler_signatures.h"
      32              : #include "taler/taler_dbevents.h"
      33              : #include "report-lib.h"
      34              : #include "auditor-database/event_listen.h"
      35              : #include "auditor-database/get_auditor_progress.h"
      36              : #include "auditor-database/get_balance.h"
      37              : #include "auditor-database/insert_amount_arithmetic_inconsistency.h"
      38              : #include "auditor-database/insert_auditor_progress.h"
      39              : #include "auditor-database/insert_bad_sig_losses.h"
      40              : #include "auditor-database/insert_balance.h"
      41              : #include "auditor-database/insert_coin_inconsistency.h"
      42              : #include "auditor-database/insert_fee_time_inconsistency.h"
      43              : #include "auditor-database/insert_row_inconsistency.h"
      44              : #include "auditor-database/insert_wire_out_inconsistency.h"
      45              : #include "exchange-database/get_coin_transactions.h"
      46              : #include "exchange-database/get_known_coin.h"
      47              : #include "exchange-database/get_wire_fee.h"
      48              : struct WireCheckContext;
      49              : #define TALER_EXCHANGEDB_AGGREGATION_DATA_RESULT_CLOSURE struct WireCheckContext
      50              : #include "exchange-database/iterate_wire_transfers.h"
      51              : #include "exchange-database/iterate_wire_outs_above_serial_id.h"
      52              : 
      53              : /**
      54              :  * Return value from main().
      55              :  */
      56              : static int global_ret;
      57              : 
      58              : /**
      59              :  * Run in test mode. Exit when idle instead of
      60              :  * going to sleep and waiting for more work.
      61              :  */
      62              : static int test_mode;
      63              : 
      64              : /**
      65              :  * Checkpointing our progress for aggregations.
      66              :  */
      67              : static TALER_ARL_DEF_PP (aggregation_last_wire_out_serial_id);
      68              : 
      69              : /**
      70              :  * Total aggregation fees (wire fees) earned.
      71              :  */
      72              : static TALER_ARL_DEF_AB (aggregation_total_wire_fee_revenue);
      73              : 
      74              : /**
      75              :  * Total delta between calculated and stored wire out transfers,
      76              :  * for positive deltas.
      77              :  */
      78              : static TALER_ARL_DEF_AB (aggregation_total_wire_out_delta_plus);
      79              : 
      80              : /**
      81              :  * Total delta between calculated and stored wire out transfers
      82              :  * for negative deltas.
      83              :  */
      84              : static TALER_ARL_DEF_AB (aggregation_total_wire_out_delta_minus);
      85              : 
      86              : /**
      87              :  * Profits the exchange made by bad amount calculations on coins.
      88              :  */
      89              : static TALER_ARL_DEF_AB (aggregation_total_coin_delta_plus);
      90              : 
      91              : /**
      92              :  * Losses the exchange made by bad amount calculations on coins.
      93              :  */
      94              : static TALER_ARL_DEF_AB (aggregation_total_coin_delta_minus);
      95              : 
      96              : /**
      97              :  * Profits the exchange made by bad amount calculations.
      98              :  */
      99              : static TALER_ARL_DEF_AB (aggregation_total_arithmetic_delta_plus);
     100              : 
     101              : /**
     102              :  * Losses the exchange made by bad amount calculations.
     103              :  */
     104              : static TALER_ARL_DEF_AB (aggregation_total_arithmetic_delta_minus);
     105              : 
     106              : /**
     107              :  * Total amount lost by operations for which signatures were invalid.
     108              :  */
     109              : static TALER_ARL_DEF_AB (aggregation_total_bad_sig_loss);
     110              : 
     111              : /**
     112              :  * Should we run checks that only work for exchange-internal audits?
     113              :  */
     114              : static int internal_checks;
     115              : 
     116              : static struct GNUNET_DB_EventHandler *eh;
     117              : 
     118              : /**
     119              :  * The auditors's configuration.
     120              :  */
     121              : static const struct GNUNET_CONFIGURATION_Handle *cfg;
     122              : 
     123              : /**
     124              :  * Report a (serious) inconsistency in the exchange's database with
     125              :  * respect to calculations involving amounts.
     126              :  *
     127              :  * @param operation what operation had the inconsistency
     128              :  * @param rowid affected row, 0 if row is missing
     129              :  * @param exchange amount calculated by exchange
     130              :  * @param auditor amount calculated by auditor
     131              :  * @param profitable 1 if @a exchange being larger than @a auditor is
     132              :  *           profitable for the exchange for this operation,
     133              :  *           -1 if @a exchange being smaller than @a auditor is
     134              :  *           profitable for the exchange, and 0 if it is unclear
     135              :  * @return transaction status
     136              :  */
     137              : static enum GNUNET_DB_QueryStatus
     138            0 : report_amount_arithmetic_inconsistency (
     139              :   const char *operation,
     140              :   uint64_t rowid,
     141              :   const struct TALER_Amount *exchange,
     142              :   const struct TALER_Amount *auditor,
     143              :   int profitable)
     144              : {
     145              :   struct TALER_Amount delta;
     146              :   struct TALER_Amount *target;
     147              : 
     148            0 :   if (0 < TALER_amount_cmp (exchange,
     149              :                             auditor))
     150              :   {
     151              :     /* exchange > auditor */
     152            0 :     TALER_ARL_amount_subtract (&delta,
     153              :                                exchange,
     154              :                                auditor);
     155              :   }
     156              :   else
     157              :   {
     158              :     /* exchange <= auditor */
     159            0 :     profitable = -profitable;
     160            0 :     TALER_ARL_amount_subtract (&delta,
     161              :                                auditor,
     162              :                                exchange);
     163              :   }
     164              : 
     165              :   {
     166            0 :     struct TALER_AUDITORDB_AmountArithmeticInconsistency aai = {
     167              :       .problem_row_id = rowid,
     168              :       .profitable = profitable,
     169              :       .operation = (char *) operation,
     170              :       .exchange_amount = *exchange,
     171              :       .auditor_amount = *auditor
     172              :     };
     173              :     enum GNUNET_DB_QueryStatus qs;
     174              : 
     175            0 :     qs = TALER_AUDITORDB_insert_amount_arithmetic_inconsistency (
     176              :       TALER_ARL_adb,
     177              :       &aai);
     178              : 
     179            0 :     if (qs < 0)
     180              :     {
     181            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     182            0 :       return qs;
     183              :     }
     184              :   }
     185            0 :   if (0 != profitable)
     186              :   {
     187            0 :     target = (1 == profitable)
     188              :       ? &TALER_ARL_USE_AB (aggregation_total_arithmetic_delta_plus)
     189            0 :       : &TALER_ARL_USE_AB (aggregation_total_arithmetic_delta_minus);
     190            0 :     TALER_ARL_amount_add (target,
     191              :                           target,
     192              :                           &delta);
     193              :   }
     194            0 :   return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
     195              : }
     196              : 
     197              : 
     198              : /**
     199              :  * Report a (serious) inconsistency in the exchange's database with
     200              :  * respect to calculations involving amounts of a coin.
     201              :  *
     202              :  * @param operation what operation had the inconsistency
     203              :  * @param coin_pub affected coin
     204              :  * @param exchange amount calculated by exchange
     205              :  * @param auditor amount calculated by auditor
     206              :  * @param profitable 1 if @a exchange being larger than @a auditor is
     207              :  *           profitable for the exchange for this operation,
     208              :  *           -1 if @a exchange being smaller than @a auditor is
     209              :  *           profitable for the exchange, and 0 if it is unclear
     210              :  * @return transaction status
     211              :  */
     212              : static enum GNUNET_DB_QueryStatus
     213            0 : report_coin_arithmetic_inconsistency (
     214              :   const char *operation,
     215              :   const struct TALER_CoinSpendPublicKeyP *coin_pub,
     216              :   const struct TALER_Amount *exchange,
     217              :   const struct TALER_Amount *auditor,
     218              :   int profitable)
     219              : {
     220              :   struct TALER_Amount delta;
     221              :   struct TALER_Amount *target;
     222              : 
     223            0 :   if (0 < TALER_amount_cmp (exchange,
     224              :                             auditor))
     225              :   {
     226              :     /* exchange > auditor */
     227            0 :     TALER_ARL_amount_subtract (&delta,
     228              :                                exchange,
     229              :                                auditor);
     230              :   }
     231              :   else
     232              :   {
     233              :     /* exchange <= auditor */
     234            0 :     profitable = -profitable;
     235            0 :     TALER_ARL_amount_subtract (&delta,
     236              :                                auditor,
     237              :                                exchange);
     238              :   }
     239              : 
     240              :   {
     241              :     enum GNUNET_DB_QueryStatus qs;
     242            0 :     struct TALER_AUDITORDB_CoinInconsistency ci = {
     243              :       .operation = (char *) operation,
     244              :       .auditor_amount = *auditor,
     245              :       .exchange_amount = *exchange,
     246              :       .profitable = profitable,
     247              :       .coin_pub = coin_pub->eddsa_pub
     248              :     };
     249              : 
     250            0 :     qs = TALER_AUDITORDB_insert_coin_inconsistency (
     251              :       TALER_ARL_adb,
     252              :       &ci);
     253              : 
     254            0 :     if (qs < 0)
     255              :     {
     256            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     257            0 :       return qs;
     258              :     }
     259              :   }
     260            0 :   if (0 != profitable)
     261              :   {
     262            0 :     target = (1 == profitable)
     263              :       ? &TALER_ARL_USE_AB (aggregation_total_coin_delta_plus)
     264            0 :       : &TALER_ARL_USE_AB (aggregation_total_coin_delta_minus);
     265            0 :     TALER_ARL_amount_add (target,
     266              :                           target,
     267              :                           &delta);
     268              :   }
     269            0 :   return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
     270              : }
     271              : 
     272              : 
     273              : /**
     274              :  * Report a (serious) inconsistency in the exchange's database.
     275              :  *
     276              :  * @param table affected table
     277              :  * @param rowid affected row, 0 if row is missing
     278              :  * @param diagnostic message explaining the problem
     279              :  * @return transaction status
     280              :  */
     281              : static enum GNUNET_DB_QueryStatus
     282            0 : report_row_inconsistency (const char *table,
     283              :                           uint64_t rowid,
     284              :                           const char *diagnostic)
     285              : {
     286              :   enum GNUNET_DB_QueryStatus qs;
     287            0 :   struct TALER_AUDITORDB_RowInconsistency ri = {
     288              :     .diagnostic = (char *) diagnostic,
     289              :     .row_table = (char *) table,
     290              :     .problem_row_id = rowid
     291              :   };
     292              : 
     293            0 :   qs = TALER_AUDITORDB_insert_row_inconsistency (
     294              :     TALER_ARL_adb,
     295              :     &ri);
     296              : 
     297            0 :   if (qs < 0)
     298              :   {
     299            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     300            0 :     return qs;
     301              :   }
     302            0 :   return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
     303              : }
     304              : 
     305              : 
     306              : /* *********************** Analyze aggregations ******************** */
     307              : /* This logic checks that the aggregator did the right thing
     308              :    paying each merchant what they were due (and on time). */
     309              : 
     310              : 
     311              : /**
     312              :  * Information about wire fees charged by the exchange.
     313              :  */
     314              : struct WireFeeInfo
     315              : {
     316              : 
     317              :   /**
     318              :    * Kept in a DLL.
     319              :    */
     320              :   struct WireFeeInfo *next;
     321              : 
     322              :   /**
     323              :    * Kept in a DLL.
     324              :    */
     325              :   struct WireFeeInfo *prev;
     326              : 
     327              :   /**
     328              :    * When does the fee go into effect (inclusive).
     329              :    */
     330              :   struct GNUNET_TIME_Timestamp start_date;
     331              : 
     332              :   /**
     333              :    * When does the fee stop being in effect (exclusive).
     334              :    */
     335              :   struct GNUNET_TIME_Timestamp end_date;
     336              : 
     337              :   /**
     338              :    * How high are the wire fees.
     339              :    */
     340              :   struct TALER_WireFeeSet fees;
     341              : 
     342              : };
     343              : 
     344              : 
     345              : /**
     346              :  * Closure for callbacks during #analyze_merchants().
     347              :  */
     348              : struct AggregationContext
     349              : {
     350              : 
     351              :   /**
     352              :    * DLL of wire fees charged by the exchange.
     353              :    */
     354              :   struct WireFeeInfo *fee_head;
     355              : 
     356              :   /**
     357              :    * DLL of wire fees charged by the exchange.
     358              :    */
     359              :   struct WireFeeInfo *fee_tail;
     360              : 
     361              :   /**
     362              :    * Final result status.
     363              :    */
     364              :   enum GNUNET_DB_QueryStatus qs;
     365              : };
     366              : 
     367              : 
     368              : /**
     369              :  * Closure for #wire_transfer_information_cb.
     370              :  */
     371              : struct WireCheckContext
     372              : {
     373              : 
     374              :   /**
     375              :    * Corresponding merchant context.
     376              :    */
     377              :   struct AggregationContext *ac;
     378              : 
     379              :   /**
     380              :    * Total deposits claimed by all transactions that were aggregated
     381              :    * under the given @e wtid.
     382              :    */
     383              :   struct TALER_Amount total_deposits;
     384              : 
     385              :   /**
     386              :    * Target account details of the receiver.
     387              :    */
     388              :   struct TALER_FullPayto payto_uri;
     389              : 
     390              :   /**
     391              :    * Execution time of the wire transfer.
     392              :    */
     393              :   struct GNUNET_TIME_Timestamp date;
     394              : 
     395              :   /**
     396              :    * Database transaction status.
     397              :    */
     398              :   enum GNUNET_DB_QueryStatus qs;
     399              : 
     400              : };
     401              : 
     402              : 
     403              : /**
     404              :  * Check coin's transaction history for plausibility.  Does NOT check
     405              :  * the signatures (those are checked independently), but does calculate
     406              :  * the amounts for the aggregation table and checks that the total
     407              :  * claimed coin value is within the value of the coin's denomination.
     408              :  *
     409              :  * @param coin_pub public key of the coin (for reporting)
     410              :  * @param h_contract_terms hash of the proposal for which we calculate the amount
     411              :  * @param merchant_pub public key of the merchant (who is allowed to issue refunds)
     412              :  * @param issue denomination information about the coin
     413              :  * @param tl_head head of transaction history to verify
     414              :  * @param[out] merchant_gain amount the coin contributes to the wire transfer to the merchant
     415              :  * @param[out] deposit_gain amount the coin contributes excluding refunds
     416              :  * @return database transaction status
     417              :  */
     418              : static enum GNUNET_DB_QueryStatus
     419           28 : check_transaction_history_for_deposit (
     420              :   const struct TALER_CoinSpendPublicKeyP *coin_pub,
     421              :   const struct TALER_PrivateContractHashP *h_contract_terms,
     422              :   const struct TALER_MerchantPublicKeyP *merchant_pub,
     423              :   const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue,
     424              :   const struct TALER_EXCHANGEDB_TransactionList *tl_head,
     425              :   struct TALER_Amount *merchant_gain,
     426              :   struct TALER_Amount *deposit_gain)
     427              : {
     428              :   struct TALER_Amount expenditures;
     429              :   struct TALER_Amount refunds;
     430              :   struct TALER_Amount spent;
     431           28 :   const struct TALER_Amount *deposited = NULL;
     432              :   struct TALER_Amount merchant_loss;
     433              :   const struct TALER_Amount *deposit_fee;
     434              :   enum GNUNET_DB_QueryStatus qs;
     435              : 
     436           28 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     437              :               "Checking transaction history of coin %s\n",
     438              :               TALER_B2S (coin_pub));
     439           28 :   GNUNET_assert (GNUNET_OK ==
     440              :                  TALER_amount_set_zero (TALER_ARL_currency,
     441              :                                         &expenditures));
     442           28 :   GNUNET_assert (GNUNET_OK ==
     443              :                  TALER_amount_set_zero (TALER_ARL_currency,
     444              :                                         &refunds));
     445           28 :   GNUNET_assert (GNUNET_OK ==
     446              :                  TALER_amount_set_zero (TALER_ARL_currency,
     447              :                                         merchant_gain));
     448           28 :   GNUNET_assert (GNUNET_OK ==
     449              :                  TALER_amount_set_zero (TALER_ARL_currency,
     450              :                                         &merchant_loss));
     451              :   /* Go over transaction history to compute totals; note that we do not bother
     452              :      to reconstruct the order of the events, so instead of subtracting we
     453              :      compute positive (deposit, melt) and negative (refund) values separately
     454              :      here, and then subtract the negative from the positive at the end (after
     455              :      the loops). */
     456           28 :   deposit_fee = NULL;
     457           28 :   for (const struct TALER_EXCHANGEDB_TransactionList *tl = tl_head;
     458           62 :        NULL != tl;
     459           34 :        tl = tl->next)
     460              :   {
     461              :     const struct TALER_Amount *fee_claimed;
     462              : 
     463           34 :     switch (tl->type)
     464              :     {
     465           30 :     case TALER_EXCHANGEDB_TT_DEPOSIT:
     466              :       {
     467              :         const struct TALER_Amount *amount_with_fee;
     468              : 
     469           30 :         amount_with_fee = &tl->details.deposit->amount_with_fee;     /* according to exchange*/
     470           30 :         fee_claimed = &tl->details.deposit->deposit_fee;     /* Fee according to exchange DB */
     471           30 :         TALER_ARL_amount_add (&expenditures,
     472              :                               &expenditures,
     473              :                               amount_with_fee);
     474              :         /* Check if this deposit is within the remit of the aggregation
     475              :            we are investigating, if so, include it in the totals. */
     476           30 :         if ((0 == GNUNET_memcmp (merchant_pub,
     477           28 :                                  &tl->details.deposit->merchant_pub)) &&
     478           28 :             (0 == GNUNET_memcmp (h_contract_terms,
     479              :                                  &tl->details.deposit->h_contract_terms)))
     480              :         {
     481              :           struct TALER_Amount amount_without_fee;
     482              : 
     483           28 :           if (NULL != deposited)
     484              :           {
     485            0 :             struct TALER_AUDITORDB_RowInconsistency ri = {
     486            0 :               .problem_row_id = tl->serial_id,
     487              :               .diagnostic = (char *)
     488              :                             "multiple deposits of the same coin into the same contract detected",
     489              :               .row_table = (char *) "deposits"
     490              :             };
     491              : 
     492            0 :             qs = TALER_AUDITORDB_insert_row_inconsistency (
     493              :               TALER_ARL_adb,
     494              :               &ri);
     495              : 
     496            0 :             if (qs < 0)
     497              :             {
     498            0 :               GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     499            0 :               return qs;
     500              :             }
     501              :           }
     502           28 :           deposited = amount_with_fee;
     503           28 :           if (TALER_ARL_SR_INVALID_NEGATIVE ==
     504           28 :               TALER_ARL_amount_subtract_neg (&amount_without_fee,
     505              :                                              amount_with_fee,
     506              :                                              fee_claimed))
     507              :           {
     508              :             /* The exchange claims a deposit fee that exceeds the deposited
     509              :                amount; report it (the fee check below will also fire) and
     510              :                continue with a zero contribution instead of dying here. */
     511            0 :             qs = report_row_inconsistency ("deposits",
     512            0 :                                            tl->serial_id,
     513              :                                            "deposit fee exceeds deposited amount");
     514            0 :             if (0 > qs)
     515            0 :               return qs;
     516            0 :             GNUNET_assert (GNUNET_OK ==
     517              :                            TALER_amount_set_zero (TALER_ARL_currency,
     518              :                                                   &amount_without_fee));
     519              :           }
     520           28 :           TALER_ARL_amount_add (merchant_gain,
     521              :                                 merchant_gain,
     522              :                                 &amount_without_fee);
     523           28 :           GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     524              :                       "Detected applicable deposit of %s\n",
     525              :                       TALER_amount2s (&amount_without_fee));
     526           28 :           deposit_fee = fee_claimed;     /* We had a deposit, remember the fee, we may need it */
     527              :         }
     528              :         /* Check that the fees given in the transaction list and in dki match */
     529           30 :         if (0 !=
     530           30 :             TALER_amount_cmp (&issue->fees.deposit,
     531              :                               fee_claimed))
     532              :         {
     533              :           /* Disagreement in fee structure between auditor and exchange DB! */
     534            0 :           qs = report_amount_arithmetic_inconsistency ("deposit fee",
     535              :                                                        0,
     536              :                                                        fee_claimed,
     537              :                                                        &issue->fees.deposit,
     538              :                                                        1);
     539            0 :           if (0 > qs)
     540            0 :             return qs;
     541              :         }
     542           30 :         break;
     543              :       }
     544            2 :     case TALER_EXCHANGEDB_TT_MELT:
     545              :       {
     546              :         const struct TALER_Amount *amount_with_fee;
     547              : 
     548            2 :         amount_with_fee = &tl->details.melt->amount_with_fee;
     549            2 :         fee_claimed = &tl->details.melt->melt_fee;
     550            2 :         TALER_ARL_amount_add (&expenditures,
     551              :                               &expenditures,
     552              :                               amount_with_fee);
     553              :         /* Check that the fees given in the transaction list and in dki match */
     554            2 :         if (0 !=
     555            2 :             TALER_amount_cmp (&issue->fees.refresh,
     556              :                               fee_claimed))
     557              :         {
     558              :           /* Disagreement in fee structure between exchange and auditor */
     559            0 :           qs = report_amount_arithmetic_inconsistency ("melt fee",
     560              :                                                        0,
     561              :                                                        fee_claimed,
     562              :                                                        &issue->fees.refresh,
     563              :                                                        1);
     564            0 :           if (0 > qs)
     565            0 :             return qs;
     566              :         }
     567            2 :         break;
     568              :       }
     569            2 :     case TALER_EXCHANGEDB_TT_REFUND:
     570              :       {
     571              :         const struct TALER_Amount *amount_with_fee;
     572              : 
     573            2 :         amount_with_fee = &tl->details.refund->refund_amount;
     574            2 :         fee_claimed = &tl->details.refund->refund_fee;
     575            2 :         TALER_ARL_amount_add (&refunds,
     576              :                               &refunds,
     577              :                               amount_with_fee);
     578            2 :         TALER_ARL_amount_add (&expenditures,
     579              :                               &expenditures,
     580              :                               fee_claimed);
     581              :         /* Check if this refund is within the remit of the aggregation
     582              :            we are investigating, if so, include it in the totals. */
     583            2 :         if ((0 == GNUNET_memcmp (merchant_pub,
     584            0 :                                  &tl->details.refund->merchant_pub)) &&
     585            0 :             (0 == GNUNET_memcmp (h_contract_terms,
     586              :                                  &tl->details.refund->h_contract_terms)))
     587              :         {
     588            0 :           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     589              :                       "Detected applicable refund of %s\n",
     590              :                       TALER_amount2s (amount_with_fee));
     591            0 :           TALER_ARL_amount_add (&merchant_loss,
     592              :                                 &merchant_loss,
     593              :                                 amount_with_fee);
     594              :         }
     595              :         /* Check that the fees given in the transaction list and in dki match */
     596            2 :         if (0 !=
     597            2 :             TALER_amount_cmp (&issue->fees.refund,
     598              :                               fee_claimed))
     599              :         {
     600              :           /* Disagreement in fee structure between exchange and auditor! */
     601            0 :           qs = report_amount_arithmetic_inconsistency ("refund fee",
     602              :                                                        0,
     603              :                                                        fee_claimed,
     604              :                                                        &issue->fees.refund,
     605              :                                                        1);
     606            0 :           if (0 > qs)
     607            0 :             return qs;
     608              :         }
     609            2 :         break;
     610              :       }
     611            0 :     case TALER_EXCHANGEDB_TT_RECOUP_REFRESH_RECEIVER:
     612              :       {
     613              :         const struct TALER_Amount *amount_with_fee;
     614              : 
     615            0 :         amount_with_fee = &tl->details.old_coin_recoup->value;
     616              :         /* We count recoups of refreshed coins like refunds for the dirty old
     617              :            coin, as they equivalently _increase_ the remaining value on the
     618              :            _old_ coin */
     619            0 :         TALER_ARL_amount_add (&refunds,
     620              :                               &refunds,
     621              :                               amount_with_fee);
     622            0 :         break;
     623              :       }
     624            0 :     case TALER_EXCHANGEDB_TT_RECOUP_WITHDRAW:
     625              :       {
     626              :         const struct TALER_Amount *amount_with_fee;
     627              : 
     628              :         /* We count recoups of the coin as expenditures, as it
     629              :            equivalently decreases the remaining value of the recouped coin. */
     630            0 :         amount_with_fee = &tl->details.recoup->value;
     631            0 :         TALER_ARL_amount_add (&expenditures,
     632              :                               &expenditures,
     633              :                               amount_with_fee);
     634            0 :         break;
     635              :       }
     636            0 :     case TALER_EXCHANGEDB_TT_RECOUP_REFRESH:
     637              :       {
     638              :         const struct TALER_Amount *amount_with_fee;
     639              : 
     640              :         /* We count recoups of the coin as expenditures, as it
     641              :            equivalently decreases the remaining value of the recouped coin. */
     642            0 :         amount_with_fee = &tl->details.recoup_refresh->value;
     643            0 :         TALER_ARL_amount_add (&expenditures,
     644              :                               &expenditures,
     645              :                               amount_with_fee);
     646            0 :         break;
     647              :       }
     648            0 :     case TALER_EXCHANGEDB_TT_PURSE_DEPOSIT:
     649              :       {
     650              :         const struct TALER_Amount *amount_with_fee;
     651              : 
     652            0 :         amount_with_fee = &tl->details.purse_deposit->amount;
     653            0 :         if (! tl->details.purse_deposit->refunded)
     654            0 :           TALER_ARL_amount_add (&expenditures,
     655              :                                 &expenditures,
     656              :                                 amount_with_fee);
     657            0 :         break;
     658              :       }
     659              : 
     660            0 :     case TALER_EXCHANGEDB_TT_PURSE_REFUND:
     661              :       {
     662              :         const struct TALER_Amount *amount_with_fee;
     663              : 
     664            0 :         amount_with_fee = &tl->details.purse_refund->refund_amount;
     665            0 :         fee_claimed = &tl->details.purse_refund->refund_fee;
     666            0 :         TALER_ARL_amount_add (&refunds,
     667              :                               &refunds,
     668              :                               amount_with_fee);
     669            0 :         TALER_ARL_amount_add (&expenditures,
     670              :                               &expenditures,
     671              :                               fee_claimed);
     672              :         /* Check that the fees given in the transaction list and in dki match */
     673            0 :         if (0 !=
     674            0 :             TALER_amount_cmp (&issue->fees.refund,
     675              :                               fee_claimed))
     676              :         {
     677              :           /* Disagreement in fee structure between exchange and auditor! */
     678            0 :           qs = report_amount_arithmetic_inconsistency ("refund fee",
     679              :                                                        0,
     680              :                                                        fee_claimed,
     681              :                                                        &issue->fees.refund,
     682              :                                                        1);
     683            0 :           if (0 > qs)
     684            0 :             return qs;
     685              :         }
     686            0 :         break;
     687              :       }
     688              : 
     689            0 :     case TALER_EXCHANGEDB_TT_RESERVE_OPEN:
     690              :       {
     691              :         const struct TALER_Amount *amount_with_fee;
     692              : 
     693            0 :         amount_with_fee = &tl->details.reserve_open->coin_contribution;
     694            0 :         TALER_ARL_amount_add (&expenditures,
     695              :                               &expenditures,
     696              :                               amount_with_fee);
     697            0 :         break;
     698              :       }
     699              :     } /* switch (tl->type) */
     700              :   } /* for 'tl' */
     701              : 
     702           28 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     703              :               "Deposits for this aggregation (after fees) are %s\n",
     704              :               TALER_amount2s (merchant_gain));
     705           28 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     706              :               "Aggregation loss due to refunds is %s\n",
     707              :               TALER_amount2s (&merchant_loss));
     708           28 :   *deposit_gain = *merchant_gain;
     709           28 :   if ((NULL != deposited) &&
     710           28 :       (NULL != deposit_fee) &&
     711           28 :       (0 == TALER_amount_cmp (&merchant_loss,
     712              :                               deposited)))
     713              :   {
     714              :     /* We had a /deposit operation AND /refund operations for the same
     715              :        merchant and contract adding up to the total deposited value
     716              :        including deposit fee. Thus, we should not subtract the /deposit fee
     717              :        from the merchant gain (as it was also refunded). */
     718            0 :     TALER_ARL_amount_add (merchant_gain,
     719              :                           merchant_gain,
     720              :                           deposit_fee);
     721              :   }
     722              :   {
     723              :     struct TALER_Amount final_gain;
     724              : 
     725           28 :     if (TALER_ARL_SR_INVALID_NEGATIVE ==
     726           28 :         TALER_ARL_amount_subtract_neg (&final_gain,
     727              :                                        merchant_gain,
     728              :                                        &merchant_loss))
     729              :     {
     730              :       /* refunds above deposits? Bad! */
     731            0 :       qs = report_coin_arithmetic_inconsistency ("refund (merchant)",
     732              :                                                  coin_pub,
     733              :                                                  merchant_gain,
     734              :                                                  &merchant_loss,
     735              :                                                  1);
     736            0 :       if (0 > qs)
     737            0 :         return qs;
     738              :       /* For the overall aggregation, we should not count this
     739              :          as a NEGATIVE contribution as that is not allowed; so
     740              :          let's count it as zero as that's the best we can do. */
     741            0 :       GNUNET_assert (GNUNET_OK ==
     742              :                      TALER_amount_set_zero (TALER_ARL_currency,
     743              :                                             merchant_gain));
     744              :     }
     745              :     else
     746              :     {
     747           28 :       *merchant_gain = final_gain;
     748              :     }
     749              :   }
     750              : 
     751              : 
     752              :   /* Calculate total balance change, i.e. expenditures (recoup, deposit, refresh)
     753              :      minus refunds (refunds, recoup-to-old) */
     754           28 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     755              :               "Subtracting refunds of %s from coin value loss\n",
     756              :               TALER_amount2s (&refunds));
     757           28 :   if (TALER_ARL_SR_INVALID_NEGATIVE ==
     758           28 :       TALER_ARL_amount_subtract_neg (&spent,
     759              :                                      &expenditures,
     760              :                                      &refunds))
     761              :   {
     762              :     /* refunds above expenditures? Bad! */
     763            0 :     qs = report_coin_arithmetic_inconsistency ("refund (balance)",
     764              :                                                coin_pub,
     765              :                                                &expenditures,
     766              :                                                &refunds,
     767              :                                                1);
     768            0 :     if (0 > qs)
     769            0 :       return qs;
     770              :   }
     771              :   else
     772              :   {
     773              :     /* Now check that 'spent' is less or equal than the total coin value */
     774           28 :     if (1 == TALER_amount_cmp (&spent,
     775              :                                &issue->value))
     776              :     {
     777              :       /* spent > value */
     778            0 :       qs = report_coin_arithmetic_inconsistency ("spend",
     779              :                                                  coin_pub,
     780              :                                                  &spent,
     781              :                                                  &issue->value,
     782              :                                                  -1);
     783            0 :       if (0 > qs)
     784            0 :         return qs;
     785              :     }
     786              :   }
     787           28 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     788              :               "Final merchant gain after refunds is %s\n",
     789              :               TALER_amount2s (deposit_gain));
     790           28 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     791              :               "Coin %s contributes %s to contract %s\n",
     792              :               TALER_B2S (coin_pub),
     793              :               TALER_amount2s (merchant_gain),
     794              :               GNUNET_h2s (&h_contract_terms->hash));
     795           28 :   return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
     796              : }
     797              : 
     798              : 
     799              : /**
     800              :  * Function called with the results of the lookup of the
     801              :  * transaction data associated with a wire transfer identifier.
     802              :  *
     803              :  * @param[in,out] wcc a `struct WireCheckContext`
     804              :  * @param rowid which row in the table is the information from (for diagnostics)
     805              :  * @param merchant_pub public key of the merchant (should be same for all callbacks with the same @e cls)
     806              :  * @param account_pay_uri where did we transfer the funds?
     807              :  * @param h_payto hash over @a account_payto_uri as it is in the DB
     808              :  * @param exchange_payto_uri which of the exchange's accounts was debited,
     809              :  *        `full_payto` NULL if not recorded; unused here
     810              :  * @param exec_time execution time of the wire transfer (should be same for all callbacks with the same @e cls)
     811              :  * @param h_contract_terms which proposal was this payment about
     812              :  * @param denom_pub denomination of @a coin_pub
     813              :  * @param coin_pub which public key was this payment about
     814              :  * @param coin_value amount contributed by this coin in total (with fee),
     815              :  *                   but excluding refunds by this coin
     816              :  * @param deposit_fee applicable deposit fee for this coin, actual
     817              :  *        fees charged may differ if coin was refunded
     818              :  */
     819              : static void
     820           28 : wire_transfer_information_cb (
     821              :   struct WireCheckContext *wcc,
     822              :   uint64_t rowid,
     823              :   const struct TALER_MerchantPublicKeyP *merchant_pub,
     824              :   const struct TALER_FullPayto account_pay_uri,
     825              :   const struct TALER_FullPaytoHashP *h_payto,
     826              :   const struct TALER_FullPayto exchange_payto_uri,
     827              :   struct GNUNET_TIME_Timestamp exec_time,
     828              :   const struct TALER_PrivateContractHashP *h_contract_terms,
     829              :   const struct TALER_DenominationPublicKey *denom_pub,
     830              :   const struct TALER_CoinSpendPublicKeyP *coin_pub,
     831              :   const struct TALER_Amount *coin_value,
     832              :   const struct TALER_Amount *deposit_fee)
     833              : {
     834              :   const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue;
     835              :   struct TALER_Amount computed_value;
     836              :   struct TALER_Amount total_deposit_without_refunds;
     837              :   struct TALER_EXCHANGEDB_TransactionList *tl;
     838              :   struct TALER_CoinPublicInfo coin;
     839              :   enum GNUNET_DB_QueryStatus qs;
     840              :   struct TALER_FullPaytoHashP hpt;
     841              :   uint64_t etag_out;
     842              : 
     843           28 :   if (0 > wcc->qs)
     844            0 :     return;
     845           28 :   TALER_full_payto_hash (account_pay_uri,
     846              :                          &hpt);
     847           28 :   if (0 !=
     848           28 :       GNUNET_memcmp (&hpt,
     849              :                      h_payto))
     850              :   {
     851            0 :     qs = report_row_inconsistency ("wire_targets",
     852              :                                    rowid,
     853              :                                    "h-payto does not match payto URI");
     854            0 :     if (0 > qs)
     855              :     {
     856            0 :       wcc->qs = qs;
     857            0 :       return;
     858              :     }
     859              :   }
     860              :   /* Obtain coin's transaction history */
     861              :   /* FIXME-Optimization: could use 'start' mechanism to only fetch
     862              :      transactions we did not yet process, instead of going over them again and
     863              :      again.*/
     864              : 
     865              :   {
     866              :     struct TALER_Amount balance;
     867              :     struct TALER_DenominationHashP h_denom_pub;
     868              : 
     869           28 :     qs = TALER_EXCHANGEDB_get_coin_transactions (TALER_ARL_edb,
     870              :                                                  false,
     871              :                                                  coin_pub,
     872              :                                                  0,
     873              :                                                  0,
     874              :                                                  &etag_out,
     875              :                                                  &balance,
     876              :                                                  &h_denom_pub,
     877              :                                                  &tl);
     878              :   }
     879           28 :   if (0 > qs)
     880              :   {
     881            0 :     wcc->qs = qs;
     882            0 :     TALER_EXCHANGEDB_free_coin_transaction_list (tl);
     883            0 :     return;
     884              :   }
     885           28 :   if (NULL == tl)
     886              :   {
     887            0 :     qs = report_row_inconsistency ("aggregation",
     888              :                                    rowid,
     889              :                                    "no transaction history for coin claimed in aggregation");
     890            0 :     if (0 > qs)
     891            0 :       wcc->qs = qs;
     892            0 :     return;
     893              :   }
     894           28 :   qs = TALER_EXCHANGEDB_get_known_coin (TALER_ARL_edb,
     895              :                                         coin_pub,
     896              :                                         &coin);
     897           28 :   if (0 > qs)
     898              :   {
     899            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     900            0 :     wcc->qs = qs;
     901            0 :     return;
     902              :   }
     903           28 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
     904              :   {
     905              :     /* this should be a foreign key violation at this point! */
     906            0 :     qs = report_row_inconsistency ("aggregation",
     907              :                                    rowid,
     908              :                                    "could not get coin details for coin claimed in aggregation");
     909            0 :     if (0 > qs)
     910            0 :       wcc->qs = qs;
     911            0 :     TALER_EXCHANGEDB_free_coin_transaction_list (tl);
     912            0 :     return;
     913              :   }
     914           28 :   qs = TALER_ARL_get_denomination_info_by_hash (&coin.denom_pub_hash,
     915              :                                                 &issue);
     916           28 :   if (0 > qs)
     917              :   {
     918            0 :     wcc->qs = qs;
     919            0 :     TALER_denom_sig_free (&coin.denom_sig);
     920            0 :     TALER_EXCHANGEDB_free_coin_transaction_list (tl);
     921            0 :     return;
     922              :   }
     923           28 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
     924              :   {
     925            0 :     TALER_denom_sig_free (&coin.denom_sig);
     926            0 :     TALER_EXCHANGEDB_free_coin_transaction_list (tl);
     927            0 :     qs = report_row_inconsistency ("aggregation",
     928              :                                    rowid,
     929              :                                    "could not find denomination key for coin claimed in aggregation");
     930            0 :     if (0 > qs)
     931            0 :       wcc->qs = qs;
     932            0 :     return;
     933              :   }
     934           28 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     935              :               "Testing coin `%s' for validity\n",
     936              :               TALER_B2S (&coin.coin_pub));
     937           28 :   if (GNUNET_OK !=
     938           28 :       TALER_test_coin_valid (&coin,
     939              :                              denom_pub))
     940              :   {
     941            0 :     struct TALER_AUDITORDB_BadSigLosses bsl = {
     942              :       .problem_row_id = rowid,
     943              :       .operation = (char *) "wire",
     944              :       .loss = *coin_value,
     945              :       .operation_specific_pub = coin.coin_pub.eddsa_pub
     946              :     };
     947              : 
     948            0 :     qs = TALER_AUDITORDB_insert_bad_sig_losses (
     949              :       TALER_ARL_adb,
     950              :       &bsl);
     951            0 :     if (qs < 0)
     952              :     {
     953            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     954            0 :       wcc->qs = qs;
     955            0 :       TALER_denom_sig_free (&coin.denom_sig);
     956            0 :       TALER_EXCHANGEDB_free_coin_transaction_list (tl);
     957            0 :       return;
     958              :     }
     959            0 :     TALER_ARL_amount_add (&TALER_ARL_USE_AB (aggregation_total_bad_sig_loss),
     960              :                           &TALER_ARL_USE_AB (aggregation_total_bad_sig_loss),
     961              :                           coin_value);
     962            0 :     qs = report_row_inconsistency ("deposit",
     963              :                                    rowid,
     964              :                                    "coin denomination signature invalid");
     965            0 :     if (0 > qs)
     966              :     {
     967            0 :       wcc->qs = qs;
     968            0 :       TALER_denom_sig_free (&coin.denom_sig);
     969            0 :       TALER_EXCHANGEDB_free_coin_transaction_list (tl);
     970            0 :       return;
     971              :     }
     972              :   }
     973           28 :   TALER_denom_sig_free (&coin.denom_sig);
     974           28 :   GNUNET_assert (NULL != issue); /* mostly to help static analysis */
     975              :   /* Check transaction history to see if it supports aggregate
     976              :      valuation */
     977           28 :   qs = check_transaction_history_for_deposit (
     978              :     coin_pub,
     979              :     h_contract_terms,
     980              :     merchant_pub,
     981              :     issue,
     982              :     tl,
     983              :     &computed_value,
     984              :     &total_deposit_without_refunds);
     985           28 :   if (0 > qs)
     986              :   {
     987            0 :     TALER_EXCHANGEDB_free_coin_transaction_list (tl);
     988            0 :     wcc->qs = qs;
     989            0 :     return;
     990              :   }
     991           28 :   TALER_EXCHANGEDB_free_coin_transaction_list (tl);
     992           28 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     993              :               "Coin contributes %s to aggregate (deposits after fees and refunds)\n",
     994              :               TALER_amount2s (&computed_value));
     995              :   {
     996              :     struct TALER_Amount coin_value_without_fee;
     997              : 
     998           28 :     if (TALER_ARL_SR_INVALID_NEGATIVE ==
     999           28 :         TALER_ARL_amount_subtract_neg (&coin_value_without_fee,
    1000              :                                        coin_value,
    1001              :                                        deposit_fee))
    1002              :     {
    1003            0 :       qs = report_amount_arithmetic_inconsistency (
    1004              :         "aggregation (fee structure)",
    1005              :         rowid,
    1006              :         coin_value,
    1007              :         deposit_fee,
    1008              :         -1);
    1009            0 :       if (0 > qs)
    1010              :       {
    1011            0 :         wcc->qs = qs;
    1012            0 :         return;
    1013              :       }
    1014              :     }
    1015           28 :     else if (0 !=
    1016           28 :              TALER_amount_cmp (&total_deposit_without_refunds,
    1017              :                                &coin_value_without_fee))
    1018              :     {
    1019            0 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    1020              :                   "Expected coin contribution of %s to aggregate\n",
    1021              :                   TALER_amount2s (&coin_value_without_fee));
    1022            0 :       qs = report_amount_arithmetic_inconsistency (
    1023              :         "aggregation (contribution)",
    1024              :         rowid,
    1025              :         &coin_value_without_fee,
    1026              :         &total_deposit_without_refunds,
    1027              :         -1);
    1028            0 :       if (0 > qs)
    1029              :       {
    1030            0 :         wcc->qs = qs;
    1031            0 :         return;
    1032              :       }
    1033              :     }
    1034              :   }
    1035              :   /* Check other details of wire transfer match */
    1036           28 :   if (0 != TALER_full_payto_cmp (account_pay_uri,
    1037              :                                  wcc->payto_uri))
    1038              :   {
    1039            0 :     qs = report_row_inconsistency ("aggregation",
    1040              :                                    rowid,
    1041              :                                    "target of outgoing wire transfer do not match hash of wire from deposit");
    1042            0 :     if (0 > qs)
    1043              :     {
    1044            0 :       wcc->qs = qs;
    1045            0 :       return;
    1046              :     }
    1047              :   }
    1048           28 :   if (GNUNET_TIME_timestamp_cmp (exec_time,
    1049              :                                  !=,
    1050              :                                  wcc->date))
    1051              :   {
    1052              :     /* This should be impossible from database constraints */
    1053            0 :     GNUNET_break (0);
    1054            0 :     qs = report_row_inconsistency ("aggregation",
    1055              :                                    rowid,
    1056              :                                    "date given in aggregate does not match wire transfer date");
    1057            0 :     if (0 > qs)
    1058              :     {
    1059            0 :       wcc->qs = qs;
    1060            0 :       return;
    1061              :     }
    1062              :   }
    1063              : 
    1064              :   /* Add coin's contribution to total aggregate value */
    1065              :   {
    1066              :     struct TALER_Amount res;
    1067              : 
    1068           28 :     TALER_ARL_amount_add (&res,
    1069              :                           &wcc->total_deposits,
    1070              :                           &computed_value);
    1071           28 :     wcc->total_deposits = res;
    1072              :   }
    1073              : }
    1074              : 
    1075              : 
    1076              : /**
    1077              :  * Lookup the wire fee that the exchange charges at @a timestamp.
    1078              :  *
    1079              :  * @param ac context for caching the result
    1080              :  * @param method method of the wire plugin
    1081              :  * @param timestamp time for which we need the fee
    1082              :  * @return NULL on error (fee unknown)
    1083              :  */
    1084              : static const struct TALER_Amount *
    1085           28 : get_wire_fee (struct AggregationContext *ac,
    1086              :               const char *method,
    1087              :               struct GNUNET_TIME_Timestamp timestamp)
    1088              : {
    1089              :   struct WireFeeInfo *wfi;
    1090              :   struct WireFeeInfo *pos;
    1091              :   struct TALER_MasterSignatureP master_sig;
    1092              :   enum GNUNET_DB_QueryStatus qs;
    1093              :   uint64_t rowid;
    1094              : 
    1095              :   /* Check if fee is already loaded in cache */
    1096           28 :   for (pos = ac->fee_head; NULL != pos; pos = pos->next)
    1097              :   {
    1098           26 :     if (GNUNET_TIME_timestamp_cmp (pos->start_date,
    1099              :                                    <=,
    1100           26 :                                    timestamp) &&
    1101           26 :         GNUNET_TIME_timestamp_cmp (pos->end_date,
    1102              :                                    >,
    1103              :                                    timestamp))
    1104           26 :       return &pos->fees.wire;
    1105            0 :     if (GNUNET_TIME_timestamp_cmp (pos->start_date,
    1106              :                                    >,
    1107              :                                    timestamp))
    1108            0 :       break;
    1109              :   }
    1110              : 
    1111              :   /* Lookup fee in exchange database */
    1112            2 :   wfi = GNUNET_new (struct WireFeeInfo);
    1113            2 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    1114            2 :       TALER_EXCHANGEDB_get_wire_fee (TALER_ARL_edb,
    1115              :                                      method,
    1116              :                                      timestamp,
    1117              :                                      &rowid,
    1118              :                                      &wfi->start_date,
    1119              :                                      &wfi->end_date,
    1120              :                                      &wfi->fees,
    1121              :                                      &master_sig))
    1122              :   {
    1123            0 :     GNUNET_break (0);
    1124            0 :     GNUNET_free (wfi);
    1125            0 :     return NULL;
    1126              :   }
    1127              : 
    1128              :   /* Check signature. (This is not terribly meaningful as the exchange can
    1129              :      easily make this one up, but it means that we have proof that the master
    1130              :      key was used for inconsistent wire fees if a merchant complains.) */
    1131            2 :   if (GNUNET_OK !=
    1132            2 :       TALER_exchange_offline_wire_fee_verify (
    1133              :         method,
    1134              :         wfi->start_date,
    1135              :         wfi->end_date,
    1136            2 :         &wfi->fees,
    1137              :         &TALER_ARL_master_pub,
    1138              :         &master_sig))
    1139              :   {
    1140            0 :     ac->qs = report_row_inconsistency ("wire-fee",
    1141              :                                        timestamp.abs_time.abs_value_us,
    1142              :                                        "wire fee signature invalid at given time");
    1143              :     /* Note: continue with the fee despite the signature
    1144              :        being invalid here; hopefully it is really only the
    1145              :        signature that is bad ... */
    1146              :   }
    1147              : 
    1148              :   /* Established fee, keep in sorted list */
    1149            2 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1150              :               "Wire fee is %s starting at %s\n",
    1151              :               TALER_amount2s (&wfi->fees.wire),
    1152              :               GNUNET_TIME_timestamp2s (wfi->start_date));
    1153            2 :   if ((NULL == pos) ||
    1154            0 :       (NULL == pos->prev))
    1155            2 :     GNUNET_CONTAINER_DLL_insert (ac->fee_head,
    1156              :                                  ac->fee_tail,
    1157              :                                  wfi);
    1158              :   else
    1159            0 :     GNUNET_CONTAINER_DLL_insert_after (ac->fee_head,
    1160              :                                        ac->fee_tail,
    1161              :                                        pos->prev,
    1162              :                                        wfi);
    1163              :   /* Check non-overlaping fee invariant */
    1164            2 :   if ((NULL != wfi->prev) &&
    1165            0 :       GNUNET_TIME_timestamp_cmp (wfi->prev->end_date,
    1166              :                                  >,
    1167              :                                  wfi->start_date))
    1168              :   {
    1169            0 :     struct TALER_AUDITORDB_FeeTimeInconsistency ftib = {
    1170              :       .problem_row_id = rowid,
    1171              :       .diagnostic = (char *) "start date before previous end date",
    1172              :       .time = wfi->start_date.abs_time,
    1173              :       .type = (char *) method
    1174              :     };
    1175              : 
    1176            0 :     qs = TALER_AUDITORDB_insert_fee_time_inconsistency (
    1177              :       TALER_ARL_adb,
    1178              :       &ftib);
    1179            0 :     if (qs < 0)
    1180              :     {
    1181            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1182            0 :       ac->qs = qs;
    1183            0 :       return NULL;
    1184              :     }
    1185              :   }
    1186            2 :   if ((NULL != wfi->next) &&
    1187            0 :       GNUNET_TIME_timestamp_cmp (wfi->next->start_date,
    1188              :                                  <,
    1189              :                                  wfi->end_date))
    1190              :   {
    1191            0 :     struct TALER_AUDITORDB_FeeTimeInconsistency ftia = {
    1192              :       .problem_row_id = rowid,
    1193              :       .diagnostic = (char *) "end date after next start date",
    1194              :       .time = wfi->end_date.abs_time,
    1195              :       .type = (char *) method
    1196              :     };
    1197              : 
    1198            0 :     qs = TALER_AUDITORDB_insert_fee_time_inconsistency (
    1199              :       TALER_ARL_adb,
    1200              :       &ftia);
    1201              : 
    1202            0 :     if (qs < 0)
    1203              :     {
    1204            0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1205            0 :       ac->qs = qs;
    1206            0 :       return NULL;
    1207              :     }
    1208              :   }
    1209            2 :   return &wfi->fees.wire;
    1210              : }
    1211              : 
    1212              : 
    1213              : /**
    1214              :  * Check that a wire transfer made by the exchange is valid
    1215              :  * (has matching deposits).
    1216              :  *
    1217              :  * @param ac a `struct AggregationContext`
    1218              :  * @param rowid identifier of the respective row in the database
    1219              :  * @param date timestamp of the wire transfer (roughly)
    1220              :  * @param wtid wire transfer subject
    1221              :  * @param payto_uri bank account details of the receiver
    1222              :  * @param amount amount that was wired
    1223              :  * @return #GNUNET_OK to continue, #GNUNET_SYSERR to stop iteration
    1224              :  */
    1225              : static enum GNUNET_GenericReturnValue
    1226           28 : check_wire_out_cb (struct AggregationContext *ac,
    1227              :                    uint64_t rowid,
    1228              :                    struct GNUNET_TIME_Timestamp date,
    1229              :                    const struct TALER_WireTransferIdentifierRawP *wtid,
    1230              :                    const struct TALER_FullPayto payto_uri,
    1231              :                    const struct TALER_Amount *amount)
    1232              : {
    1233              :   struct WireCheckContext wcc;
    1234              :   struct TALER_Amount final_amount;
    1235              :   struct TALER_Amount exchange_gain;
    1236              :   enum GNUNET_DB_QueryStatus qs;
    1237              :   char *method;
    1238              : 
    1239              :   /* should be monotonically increasing */
    1240           28 :   GNUNET_assert (rowid >=
    1241              :                  TALER_ARL_USE_PP (aggregation_last_wire_out_serial_id));
    1242           28 :   TALER_ARL_USE_PP (aggregation_last_wire_out_serial_id) = rowid + 1;
    1243              : 
    1244           28 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1245              :               "Checking wire transfer %s over %s performed on %s\n",
    1246              :               TALER_B2S (wtid),
    1247              :               TALER_amount2s (amount),
    1248              :               GNUNET_TIME_timestamp2s (date));
    1249           28 :   if (NULL == (method = TALER_payto_get_method (payto_uri.full_payto)))
    1250              :   {
    1251            0 :     qs = report_row_inconsistency ("wire_out",
    1252              :                                    rowid,
    1253              :                                    "specified wire address lacks method");
    1254            0 :     if (0 > qs)
    1255            0 :       ac->qs = qs;
    1256            0 :     return GNUNET_OK;
    1257              :   }
    1258              : 
    1259           28 :   wcc.ac = ac;
    1260           28 :   wcc.qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
    1261           28 :   wcc.date = date;
    1262           28 :   GNUNET_assert (GNUNET_OK ==
    1263              :                  TALER_amount_set_zero (amount->currency,
    1264              :                                         &wcc.total_deposits));
    1265           28 :   wcc.payto_uri = payto_uri;
    1266           28 :   qs = TALER_EXCHANGEDB_iterate_wire_transfers (TALER_ARL_edb,
    1267              :                                                 wtid,
    1268              :                                                 &wire_transfer_information_cb,
    1269              :                                                 &wcc);
    1270           28 :   if (0 > qs)
    1271              :   {
    1272            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1273            0 :     ac->qs = qs;
    1274            0 :     GNUNET_free (method);
    1275            0 :     return GNUNET_SYSERR;
    1276              :   }
    1277           28 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != wcc.qs)
    1278              :   {
    1279              :     /* Note: detailed information was already logged
    1280              :        in #wire_transfer_information_cb, so here we
    1281              :        only log for debugging */
    1282            0 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1283              :                 "Inconsistency for wire_out %llu (WTID %s) detected\n",
    1284              :                 (unsigned long long) rowid,
    1285              :                 TALER_B2S (wtid));
    1286              :   }
    1287              : 
    1288              : 
    1289              :   /* Subtract aggregation fee from total (if possible) */
    1290              :   {
    1291              :     const struct TALER_Amount *wire_fee;
    1292              : 
    1293           28 :     wire_fee = get_wire_fee (ac,
    1294              :                              method,
    1295              :                              date);
    1296           28 :     if (0 > ac->qs)
    1297              :     {
    1298            0 :       GNUNET_free (method);
    1299            0 :       return GNUNET_SYSERR;
    1300              :     }
    1301           28 :     if (NULL == wire_fee)
    1302              :     {
    1303            0 :       qs = report_row_inconsistency ("wire-fee",
    1304              :                                      date.abs_time.abs_value_us,
    1305              :                                      "wire fee unavailable for given time");
    1306            0 :       if (qs < 0)
    1307              :       {
    1308            0 :         ac->qs = qs;
    1309            0 :         GNUNET_free (method);
    1310            0 :         return GNUNET_SYSERR;
    1311              :       }
    1312              :       /* If fee is unknown, we just assume the fee is zero */
    1313            0 :       final_amount = wcc.total_deposits;
    1314              :     }
    1315           28 :     else if (TALER_ARL_SR_INVALID_NEGATIVE ==
    1316           28 :              TALER_ARL_amount_subtract_neg (&final_amount,
    1317              :                                             &wcc.total_deposits,
    1318              :                                             wire_fee))
    1319              :     {
    1320            0 :       qs = report_amount_arithmetic_inconsistency (
    1321              :         "wire out (fee structure)",
    1322              :         rowid,
    1323              :         &wcc.total_deposits,
    1324              :         wire_fee,
    1325              :         -1);
    1326              :       /* If fee arithmetic fails, we just assume the fee is zero */
    1327            0 :       if (0 > qs)
    1328              :       {
    1329            0 :         ac->qs = qs;
    1330            0 :         GNUNET_free (method);
    1331            0 :         return GNUNET_SYSERR;
    1332              :       }
    1333            0 :       final_amount = wcc.total_deposits;
    1334              :     }
    1335              :   }
    1336           28 :   GNUNET_free (method);
    1337              : 
    1338              :   /* Round down to amount supported by wire method */
    1339           28 :   GNUNET_break (GNUNET_SYSERR !=
    1340              :                 TALER_amount_round_down (&final_amount,
    1341              :                                          &TALER_ARL_currency_round_unit));
    1342              : 
    1343              :   /* Calculate the exchange's gain as the fees plus rounding differences! */
    1344           28 :   TALER_ARL_amount_subtract (&exchange_gain,
    1345              :                              &wcc.total_deposits,
    1346              :                              &final_amount);
    1347              : 
    1348              :   /* Sum up aggregation fees (we simply include the rounding gains) */
    1349           28 :   TALER_ARL_amount_add (&TALER_ARL_USE_AB (aggregation_total_wire_fee_revenue),
    1350              :                         &TALER_ARL_USE_AB (aggregation_total_wire_fee_revenue),
    1351              :                         &exchange_gain);
    1352              : 
    1353              :   /* Check that calculated amount matches actual amount */
    1354           28 :   if (0 != TALER_amount_cmp (amount,
    1355              :                              &final_amount))
    1356              :   {
    1357              :     struct TALER_Amount delta;
    1358              : 
    1359            0 :     if (0 < TALER_amount_cmp (amount,
    1360              :                               &final_amount))
    1361              :     {
    1362              :       /* amount > final_amount */
    1363            0 :       TALER_ARL_amount_subtract (&delta,
    1364              :                                  amount,
    1365              :                                  &final_amount);
    1366            0 :       TALER_ARL_amount_add (&TALER_ARL_USE_AB (
    1367              :                               aggregation_total_wire_out_delta_plus),
    1368              :                             &TALER_ARL_USE_AB (
    1369              :                               aggregation_total_wire_out_delta_plus),
    1370              :                             &delta);
    1371              :     }
    1372              :     else
    1373              :     {
    1374              :       /* amount < final_amount */
    1375            0 :       TALER_ARL_amount_subtract (&delta,
    1376              :                                  &final_amount,
    1377              :                                  amount);
    1378            0 :       TALER_ARL_amount_add (&TALER_ARL_USE_AB (
    1379              :                               aggregation_total_wire_out_delta_minus),
    1380              :                             &TALER_ARL_USE_AB (
    1381              :                               aggregation_total_wire_out_delta_minus),
    1382              :                             &delta);
    1383              :     }
    1384              : 
    1385              :     {
    1386            0 :       struct TALER_AUDITORDB_WireOutInconsistency woi = {
    1387              :         .destination_account = payto_uri,
    1388              :         .diagnostic = (char *) "aggregated amount does not match expectations",
    1389              :         .wire_out_row_id = rowid,
    1390              :         .expected = final_amount,
    1391              :         .claimed = *amount
    1392              :       };
    1393              : 
    1394            0 :       qs = TALER_AUDITORDB_insert_wire_out_inconsistency (
    1395              :         TALER_ARL_adb,
    1396              :         &woi);
    1397              : 
    1398            0 :       if (qs < 0)
    1399              :       {
    1400            0 :         GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1401            0 :         ac->qs = qs;
    1402            0 :         return GNUNET_SYSERR;
    1403              :       }
    1404              :     }
    1405            0 :     return GNUNET_OK;
    1406              :   }
    1407           28 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1408              :               "Aggregation unit %s is OK\n",
    1409              :               TALER_B2S (wtid));
    1410           28 :   return GNUNET_OK;
    1411              : }
    1412              : 
    1413              : 
    1414              : /**
    1415              :  * Analyze the exchange aggregator's payment processing.
    1416              :  *
    1417              :  * @param cls closure
    1418              :  * @return transaction status code
    1419              :  */
    1420              : static enum GNUNET_DB_QueryStatus
    1421           10 : analyze_aggregations (void *cls)
    1422              : {
    1423              :   struct AggregationContext ac;
    1424              :   struct WireFeeInfo *wfi;
    1425              :   enum GNUNET_DB_QueryStatus qs;
    1426              : 
    1427              :   (void) cls;
    1428           10 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1429              :               "Analyzing aggregations\n");
    1430           10 :   qs = TALER_AUDITORDB_get_auditor_progress (
    1431              :     TALER_ARL_adb,
    1432              :     TALER_ARL_GET_PP (aggregation_last_wire_out_serial_id),
    1433              :     NULL);
    1434           10 :   if (0 > qs)
    1435              :   {
    1436            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1437            0 :     return qs;
    1438              :   }
    1439           10 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    1440              :   {
    1441            0 :     GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
    1442              :                 "First analysis using this auditor, starting audit from scratch\n");
    1443              :   }
    1444              :   else
    1445              :   {
    1446           10 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1447              :                 "Resuming aggregation audit at %llu\n",
    1448              :                 (unsigned long long) TALER_ARL_USE_PP (
    1449              :                   aggregation_last_wire_out_serial_id));
    1450              :   }
    1451              : 
    1452           10 :   memset (&ac,
    1453              :           0,
    1454              :           sizeof (ac));
    1455           10 :   qs = TALER_AUDITORDB_get_balance (
    1456              :     TALER_ARL_adb,
    1457              :     TALER_ARL_GET_AB (aggregation_total_wire_fee_revenue),
    1458              :     TALER_ARL_GET_AB (aggregation_total_arithmetic_delta_plus),
    1459              :     TALER_ARL_GET_AB (aggregation_total_arithmetic_delta_minus),
    1460              :     TALER_ARL_GET_AB (aggregation_total_bad_sig_loss),
    1461              :     TALER_ARL_GET_AB (aggregation_total_wire_out_delta_plus),
    1462              :     TALER_ARL_GET_AB (aggregation_total_wire_out_delta_minus),
    1463              :     TALER_ARL_GET_AB (aggregation_total_coin_delta_plus),
    1464              :     TALER_ARL_GET_AB (aggregation_total_coin_delta_minus),
    1465              :     NULL);
    1466           10 :   if (0 > qs)
    1467              :   {
    1468            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1469            0 :     return qs;
    1470              :   }
    1471              : 
    1472           10 :   ac.qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
    1473           10 :   qs = TALER_EXCHANGEDB_iterate_wire_outs_above_serial_id (
    1474              :     TALER_ARL_edb,
    1475              :     TALER_ARL_USE_PP (aggregation_last_wire_out_serial_id),
    1476              :     &check_wire_out_cb,
    1477              :     &ac);
    1478           10 :   if (0 > qs)
    1479              :   {
    1480            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1481            0 :     ac.qs = qs;
    1482              :   }
    1483           12 :   while (NULL != (wfi = ac.fee_head))
    1484              :   {
    1485            2 :     GNUNET_CONTAINER_DLL_remove (ac.fee_head,
    1486              :                                  ac.fee_tail,
    1487              :                                  wfi);
    1488            2 :     GNUNET_free (wfi);
    1489              :   }
    1490           10 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    1491              :   {
    1492              :     /* there were no wire out entries to be looked at, we are done */
    1493            8 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1494              :                 "No wire out entries found\n");
    1495            8 :     return qs;
    1496              :   }
    1497            2 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != ac.qs)
    1498              :   {
    1499            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == ac.qs);
    1500            0 :     return ac.qs;
    1501              :   }
    1502            2 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1503              :               "Finished aggregation audit at %llu\n",
    1504              :               (unsigned long long) TALER_ARL_USE_PP (
    1505              :                 aggregation_last_wire_out_serial_id));
    1506            2 :   qs = TALER_AUDITORDB_insert_balance (
    1507              :     TALER_ARL_adb,
    1508              :     TALER_ARL_SET_AB (aggregation_total_wire_fee_revenue),
    1509              :     TALER_ARL_SET_AB (aggregation_total_arithmetic_delta_plus),
    1510              :     TALER_ARL_SET_AB (aggregation_total_arithmetic_delta_minus),
    1511              :     TALER_ARL_SET_AB (aggregation_total_bad_sig_loss),
    1512              :     TALER_ARL_SET_AB (aggregation_total_wire_out_delta_plus),
    1513              :     TALER_ARL_SET_AB (aggregation_total_wire_out_delta_minus),
    1514              :     TALER_ARL_SET_AB (aggregation_total_coin_delta_plus),
    1515              :     TALER_ARL_SET_AB (aggregation_total_coin_delta_minus),
    1516              :     NULL);
    1517            2 :   if (0 > qs)
    1518              :   {
    1519            0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1520              :                 "Failed to update auditor DB, not recording progress\n");
    1521            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1522            0 :     return qs;
    1523              :   }
    1524              : 
    1525            2 :   qs = TALER_AUDITORDB_insert_auditor_progress (
    1526              :     TALER_ARL_adb,
    1527              :     TALER_ARL_SET_PP (aggregation_last_wire_out_serial_id),
    1528              :     NULL);
    1529            2 :   if (0 > qs)
    1530              :   {
    1531            0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1532              :                 "Failed to update auditor DB, not recording progress\n");
    1533            0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1534            0 :     return qs;
    1535              :   }
    1536            2 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1537              :               "Concluded aggregation audit step at %llu\n",
    1538              :               (unsigned long long) TALER_ARL_USE_PP (
    1539              :                 aggregation_last_wire_out_serial_id));
    1540              : 
    1541            2 :   return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
    1542              : }
    1543              : 
    1544              : 
    1545              : /**
    1546              :  * Function called on events received from Postgres.
    1547              :  *
    1548              :  * @param cls closure, NULL
    1549              :  * @param extra additional event data provided
    1550              :  * @param extra_size number of bytes in @a extra
    1551              :  */
    1552              : static void
    1553            0 : db_notify (void *cls,
    1554              :            const void *extra,
    1555              :            size_t extra_size)
    1556              : {
    1557              :   (void) cls;
    1558              :   (void) extra;
    1559              :   (void) extra_size;
    1560            0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1561              :               "Received notification to wake aggregation helper\n");
    1562            0 :   if (GNUNET_OK !=
    1563            0 :       TALER_ARL_setup_sessions_and_run (&analyze_aggregations,
    1564              :                                         NULL))
    1565              :   {
    1566            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1567              :                 "Audit failed\n");
    1568            0 :     GNUNET_SCHEDULER_shutdown ();
    1569            0 :     global_ret = EXIT_FAILURE;
    1570            0 :     return;
    1571              :   }
    1572              : }
    1573              : 
    1574              : 
    1575              : /**
    1576              :  * Function called on shutdown.
    1577              :  */
    1578              : static void
    1579           10 : do_shutdown (void *cls)
    1580              : {
    1581              :   (void) cls;
    1582           10 :   if (NULL != eh)
    1583              :   {
    1584            4 :     TALER_AUDITORDB_event_listen_cancel (eh);
    1585            4 :     eh = NULL;
    1586              :   }
    1587           10 :   TALER_ARL_done ();
    1588           10 : }
    1589              : 
    1590              : 
    1591              : /**
    1592              :  * Main function that will be run.
    1593              :  *
    1594              :  * @param cls closure
    1595              :  * @param args remaining command-line arguments
    1596              :  * @param cfgfile name of the configuration file used (for saving, can be NULL!)
    1597              :  * @param c configuration
    1598              :  */
    1599              : static void
    1600           10 : run (void *cls,
    1601              :      char *const *args,
    1602              :      const char *cfgfile,
    1603              :      const struct GNUNET_CONFIGURATION_Handle *c)
    1604              : {
    1605              :   (void) cls;
    1606              :   (void) args;
    1607              :   (void) cfgfile;
    1608              : 
    1609           10 :   cfg = c;
    1610           10 :   GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
    1611              :                                  NULL);
    1612           10 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1613              :               "Launching aggregation auditor\n");
    1614           10 :   if (EXIT_SUCCESS !=
    1615           10 :       (global_ret = TALER_ARL_init (c)))
    1616              :   {
    1617            0 :     return;
    1618              :   }
    1619              : 
    1620           10 :   if (test_mode != 1)
    1621              :   {
    1622            4 :     struct GNUNET_DB_EventHeaderP es = {
    1623            4 :       .size = htons (sizeof (es)),
    1624            4 :       .type = htons (TALER_DBEVENT_EXCHANGE_AUDITOR_WAKE_HELPER_AGGREGATION)
    1625              :     };
    1626              : 
    1627            4 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1628              :                 "Running helper indefinitely\n");
    1629            4 :     eh = TALER_AUDITORDB_event_listen (TALER_ARL_adb,
    1630              :                                        &es,
    1631            4 :                                        GNUNET_TIME_UNIT_FOREVER_REL,
    1632              :                                        &db_notify,
    1633              :                                        NULL);
    1634              :   }
    1635           10 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1636              :               "Starting audit\n");
    1637           10 :   if (GNUNET_OK !=
    1638           10 :       TALER_ARL_setup_sessions_and_run (&analyze_aggregations,
    1639              :                                         NULL))
    1640              :   {
    1641            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1642              :                 "Audit failed\n");
    1643            0 :     GNUNET_SCHEDULER_shutdown ();
    1644            0 :     global_ret = EXIT_FAILURE;
    1645            0 :     return;
    1646              :   }
    1647              : }
    1648              : 
    1649              : 
    1650              : /**
    1651              :  * The main function to audit the exchange's aggregation processing.
    1652              :  *
    1653              :  * @param argc number of arguments from the command line
    1654              :  * @param argv command line arguments
    1655              :  * @return 0 ok, 1 on error
    1656              :  */
    1657              : int
    1658           10 : main (int argc,
    1659              :       char *const *argv)
    1660              : {
    1661           10 :   const struct GNUNET_GETOPT_CommandLineOption options[] = {
    1662           10 :     GNUNET_GETOPT_option_flag ('i',
    1663              :                                "internal",
    1664              :                                "perform checks only applicable for exchange-internal audits",
    1665              :                                &internal_checks),
    1666           10 :     GNUNET_GETOPT_option_flag ('t',
    1667              :                                "test",
    1668              :                                "run in test mode and exit when idle",
    1669              :                                &test_mode),
    1670           10 :     GNUNET_GETOPT_option_timetravel ('T',
    1671              :                                      "timetravel"),
    1672              :     GNUNET_GETOPT_OPTION_END
    1673              :   };
    1674              :   enum GNUNET_GenericReturnValue ret;
    1675              : 
    1676           10 :   ret = GNUNET_PROGRAM_run (
    1677              :     TALER_AUDITOR_project_data (),
    1678              :     argc,
    1679              :     argv,
    1680              :     "taler-helper-auditor-aggregation",
    1681              :     gettext_noop ("Audit Taler exchange aggregation activity"),
    1682              :     options,
    1683              :     &run,
    1684              :     NULL);
    1685           10 :   if (GNUNET_SYSERR == ret)
    1686            0 :     return EXIT_NOTCONFIGURED;
    1687           10 :   if (GNUNET_NO == ret)
    1688            0 :     return EXIT_SUCCESS;
    1689           10 :   return global_ret;
    1690              : }
    1691              : 
    1692              : 
    1693              : /* end of taler-helper-auditor-aggregation.c */
        

Generated by: LCOV version 2.0-1