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

Generated by: LCOV version 2.0-1