LCOV - code coverage report
Current view: top level - auditor - taler-helper-auditor-aggregation.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 10.6 % 472 50
Test Date: 2025-12-28 14:06:02 Functions: 33.3 % 12 4

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

Generated by: LCOV version 2.0-1