exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

report-lib.c (25635B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2016-2020 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/report-lib.c
     18  * @brief helper library to facilitate generation of audit reports
     19  * @author Christian Grothoff
     20  */
     21 #include "report-lib.h"
     22 #include "auditor-database/preflight.h"
     23 #include "auditor-database/start.h"
     24 #include "exchange-database/commit.h"
     25 #include "exchange-database/get_denomination_by_serial.h"
     26 #include "exchange-database/get_denomination_info.h"
     27 #include "exchange-database/iterate_denomination_info.h"
     28 #include "exchange-database/preflight.h"
     29 #include "exchange-database/rollback.h"
     30 #include "exchange-database/start.h"
     31 
     32 /**
     33  * Handle to access the exchange's database.
     34  */
     35 struct TALER_EXCHANGEDB_PostgresContext *TALER_ARL_edb;
     36 
     37 /**
     38  * Which currency are we doing the audit for?
     39  */
     40 char *TALER_ARL_currency;
     41 
     42 /**
     43  * How many fractional digits does the currency use?
     44  */
     45 struct TALER_Amount TALER_ARL_currency_round_unit;
     46 
     47 /**
     48  * Our configuration.
     49  */
     50 const struct GNUNET_CONFIGURATION_Handle *TALER_ARL_cfg;
     51 
     52 /**
     53  * Handle to access the auditor's database.
     54  */
     55 struct TALER_AUDITORDB_PostgresContext *TALER_ARL_adb;
     56 
     57 /**
     58  * Master public key of the exchange to audit.
     59  */
     60 struct TALER_MasterPublicKeyP TALER_ARL_master_pub;
     61 
     62 /**
     63  * Public key of the auditor.
     64  */
     65 struct TALER_AuditorPublicKeyP TALER_ARL_auditor_pub;
     66 
     67 /**
     68  * REST API endpoint of the auditor.
     69  */
     70 char *TALER_ARL_auditor_url;
     71 
     72 /**
     73  * REST API endpoint of the exchange.
     74  */
     75 char *TALER_ARL_exchange_url;
     76 
     77 /**
     78  * At what time did the auditor process start?
     79  */
     80 struct GNUNET_TIME_Absolute start_time;
     81 
     82 /**
     83  * Results about denominations, cached per-transaction, maps denomination pub hashes
     84  * to `const struct TALER_EXCHANGEDB_DenominationKeyInformation`.
     85  */
     86 static struct GNUNET_CONTAINER_MultiHashMap *denominations;
     87 
     88 /**
     89  * Results about denominations, cached per-transaction, maps row/serial ID's
     90  * to `const struct TALER_EXCHANGEDB_DenominationKeyInformation`.
     91  */
     92 static struct GNUNET_CONTAINER_MultiUuidmap *denominations_by_serial;
     93 
     94 /**
     95  * Helper to convert a serial/row id to a uuid for the lookup
     96  * in a uuid hash table.
     97  *
     98  * @param serial serial id of entry
     99  * @param[out] uuid uuid to write
    100  */
    101 static void
    102 serial_to_uuid (
    103   uint64_t serial,
    104   struct GNUNET_Uuid *uuid)
    105 {
    106   uuid->value[0] = serial;
    107   uuid->value[1] = serial >> 32;
    108   uuid->value[2] = 0;
    109   uuid->value[3] = 0;
    110 }
    111 
    112 
    113 /**
    114  * Function called with the results of iterate_denomination_info(),
    115  * or directly (!).  Used to check and add the respective denomination
    116  * to our hash table.
    117  *
    118  * @param cls closure, NULL
    119  * @param denom_serial table row of the denomaination
    120  * @param denom_pub public key, sometimes NULL (!)
    121  * @param issue issuing information with value, fees and other info about the denomination.
    122  */
    123 static void
    124 add_denomination (
    125   void *cls,
    126   uint64_t denom_serial,
    127   const struct TALER_DenominationPublicKey *denom_pub,
    128   const struct TALER_EXCHANGEDB_DenominationKeyInformation *issue)
    129 {
    130   (void) cls;
    131   (void) denom_pub;
    132   if (NULL !=
    133       GNUNET_CONTAINER_multihashmap_get (denominations,
    134                                          &issue->denom_hash.hash))
    135     return; /* value already known */
    136 #if GNUNET_EXTRA_LOGGING >= 1
    137   {
    138     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    139                 "Tracking denomination `%s' (%s)\n",
    140                 GNUNET_h2s (&issue->denom_hash.hash),
    141                 TALER_amount2s (&issue->value));
    142     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    143                 "Withdraw fee is %s\n",
    144                 TALER_amount2s (&issue->fees.withdraw));
    145     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    146                 "Start time is %s\n",
    147                 GNUNET_TIME_timestamp2s (issue->start));
    148     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    149                 "Expire deposit time is %s\n",
    150                 GNUNET_TIME_timestamp2s (issue->expire_deposit));
    151   }
    152 #endif
    153   {
    154     struct TALER_EXCHANGEDB_DenominationKeyInformation *i;
    155     struct GNUNET_Uuid uuid;
    156 
    157     i = GNUNET_new (struct TALER_EXCHANGEDB_DenominationKeyInformation);
    158     *i = *issue;
    159     GNUNET_assert (GNUNET_OK ==
    160                    GNUNET_CONTAINER_multihashmap_put (denominations,
    161                                                       &issue->denom_hash.hash,
    162                                                       i,
    163                                                       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
    164     serial_to_uuid (denom_serial, &uuid);
    165     GNUNET_assert (GNUNET_OK ==
    166                    GNUNET_CONTAINER_multiuuidmap_put (denominations_by_serial,
    167                                                       &uuid,
    168                                                       i,
    169                                                       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
    170   }
    171 }
    172 
    173 
    174 enum GNUNET_DB_QueryStatus
    175 TALER_ARL_get_denomination_info_by_hash (
    176   const struct TALER_DenominationHashP *dh,
    177   const struct TALER_EXCHANGEDB_DenominationKeyInformation **issuep)
    178 {
    179   enum GNUNET_DB_QueryStatus qs;
    180 
    181   *issuep = NULL;
    182   if (NULL == denominations)
    183   {
    184     denominations = GNUNET_CONTAINER_multihashmap_create (256,
    185                                                           GNUNET_NO);
    186     if (NULL  == denominations_by_serial)
    187       denominations_by_serial = GNUNET_CONTAINER_multiuuidmap_create (256,
    188                                                                       GNUNET_NO)
    189       ;
    190 
    191     qs = TALER_EXCHANGEDB_iterate_denomination_info (TALER_ARL_edb,
    192                                                      &add_denomination,
    193                                                      NULL);
    194     if (0 > qs)
    195     {
    196       GNUNET_break (0);
    197       *issuep = NULL;
    198       return qs;
    199     }
    200   }
    201   {
    202     const struct TALER_EXCHANGEDB_DenominationKeyInformation *i;
    203 
    204     i = GNUNET_CONTAINER_multihashmap_get (denominations,
    205                                            &dh->hash);
    206     if (NULL != i)
    207     {
    208       /* cache hit */
    209       *issuep = i;
    210       return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
    211     }
    212   }
    213   /* maybe database changed since we last iterated, give it one more shot */
    214   {
    215     struct TALER_EXCHANGEDB_DenominationKeyInformation issue;
    216     uint64_t denom_serial;
    217 
    218     qs = TALER_EXCHANGEDB_get_denomination_info (TALER_ARL_edb,
    219                                                  dh,
    220                                                  &denom_serial,
    221                                                  &issue);
    222     if (qs <= 0)
    223     {
    224       GNUNET_break (qs >= 0);
    225       if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    226         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    227                     "Denomination %s not found\n",
    228                     TALER_B2S (dh));
    229       return qs;
    230     }
    231 
    232     add_denomination (NULL,
    233                       denom_serial,
    234                       NULL,
    235                       &issue);
    236   }
    237   {
    238     const struct TALER_EXCHANGEDB_DenominationKeyInformation *i;
    239 
    240     i = GNUNET_CONTAINER_multihashmap_get (denominations,
    241                                            &dh->hash);
    242     if (NULL != i)
    243     {
    244       /* cache hit */
    245       *issuep = i;
    246       return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
    247     }
    248   }
    249   /* We found more keys, but not the denomination we are looking for :-( */
    250   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    251               "Denomination %s not found\n",
    252               TALER_B2S (dh));
    253   return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
    254 }
    255 
    256 
    257 enum GNUNET_DB_QueryStatus
    258 TALER_ARL_get_denomination_info_by_serial (
    259   uint64_t denom_serial,
    260   const struct TALER_EXCHANGEDB_DenominationKeyInformation **issuep)
    261 {
    262   enum GNUNET_DB_QueryStatus qs;
    263   struct GNUNET_Uuid uuid;
    264 
    265   *issuep = NULL;
    266   serial_to_uuid (denom_serial,
    267                   &uuid);
    268   if (NULL == denominations_by_serial)
    269   {
    270     denominations_by_serial = GNUNET_CONTAINER_multiuuidmap_create (256,
    271                                                                     GNUNET_NO);
    272     if (NULL == denominations)
    273       denominations = GNUNET_CONTAINER_multihashmap_create (256,
    274                                                             GNUNET_NO);
    275 
    276     qs = TALER_EXCHANGEDB_iterate_denomination_info (TALER_ARL_edb,
    277                                                      &add_denomination,
    278                                                      NULL);
    279     if (0 > qs)
    280     {
    281       GNUNET_break (0);
    282       *issuep = NULL;
    283       return qs;
    284     }
    285   }
    286   {
    287     const struct TALER_EXCHANGEDB_DenominationKeyInformation *i;
    288 
    289     i = GNUNET_CONTAINER_multiuuidmap_get (denominations_by_serial,
    290                                            &uuid);
    291     if (NULL != i)
    292     {
    293       /* cache hit */
    294       *issuep = i;
    295       return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
    296     }
    297   }
    298   /* maybe database changed since we last iterated, give it one more shot */
    299   {
    300     struct TALER_EXCHANGEDB_DenominationKeyInformation issue;
    301 
    302     qs = TALER_EXCHANGEDB_get_denomination_by_serial (TALER_ARL_edb,
    303                                                       denom_serial,
    304                                                       &issue);
    305     if (qs <= 0)
    306     {
    307       GNUNET_break (qs >= 0);
    308       if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    309         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    310                     "Denomination with serial %lu not found\n",
    311                     denom_serial);
    312       return qs;
    313     }
    314 
    315     add_denomination (NULL,
    316                       denom_serial,
    317                       NULL,
    318                       &issue);
    319   }
    320 
    321   {
    322     const struct TALER_EXCHANGEDB_DenominationKeyInformation *i;
    323 
    324     i = GNUNET_CONTAINER_multiuuidmap_get (denominations_by_serial,
    325                                            &uuid);
    326     if (NULL != i)
    327     {
    328       /* cache hit */
    329       *issuep = i;
    330       return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
    331     }
    332   }
    333   /* We found more keys, but not the denomination we are looking for :-( */
    334   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    335               "Denomination with serial %lu not found\n",
    336               denom_serial);
    337   return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
    338 }
    339 
    340 
    341 enum GNUNET_DB_QueryStatus
    342 TALER_ARL_get_denomination_info (
    343   const struct TALER_DenominationPublicKey *denom_pub,
    344   const struct TALER_EXCHANGEDB_DenominationKeyInformation **issue,
    345   struct TALER_DenominationHashP *dh)
    346 {
    347   struct TALER_DenominationHashP hc;
    348 
    349   if (NULL == dh)
    350     dh = &hc;
    351   TALER_denom_pub_hash (denom_pub,
    352                         dh);
    353   return TALER_ARL_get_denomination_info_by_hash (dh,
    354                                                   issue);
    355 }
    356 
    357 
    358 /**
    359  * Perform the given @a analysis within a transaction scope.
    360  * Commit on success.
    361  *
    362  * @param analysis analysis to run
    363  * @param analysis_cls closure for @a analysis
    364  * @return #GNUNET_OK if @a analysis successfully committed,
    365  *         #GNUNET_NO if we had an error on commit (retry may help)
    366  *         #GNUNET_SYSERR on hard errors
    367  */
    368 static enum GNUNET_GenericReturnValue
    369 transact (TALER_ARL_Analysis analysis,
    370           void *analysis_cls)
    371 {
    372   int ret;
    373   enum GNUNET_DB_QueryStatus qs;
    374 
    375   ret = TALER_AUDITORDB_start (TALER_ARL_adb,
    376                                "auditor-analysis");
    377   if (GNUNET_OK != ret)
    378   {
    379     GNUNET_break (0);
    380     return GNUNET_SYSERR;
    381   }
    382   if (GNUNET_OK !=
    383       TALER_EXCHANGEDB_preflight (TALER_ARL_edb))
    384   {
    385     GNUNET_break (0);
    386     return GNUNET_SYSERR;
    387   }
    388   ret = TALER_EXCHANGEDB_start (TALER_ARL_edb,
    389                                 "auditor");
    390   if (GNUNET_OK != ret)
    391   {
    392     GNUNET_break (0);
    393     TALER_EXCHANGEDB_rollback (TALER_ARL_edb);
    394     return GNUNET_SYSERR;
    395   }
    396   qs = analysis (analysis_cls);
    397   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
    398   {
    399     qs = TALER_EXCHANGEDB_commit (TALER_ARL_edb);
    400     if (0 > qs)
    401     {
    402       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    403       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    404                   "Exchange DB commit failed, rolling back transaction\n");
    405       TALER_AUDITORDB_rollback (TALER_ARL_adb);
    406     }
    407     else
    408     {
    409       qs = TALER_AUDITORDB_commit (TALER_ARL_adb);
    410       if (0 > qs)
    411       {
    412         GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    413         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    414                     "Auditor DB commit failed!\n");
    415       }
    416     }
    417   }
    418   else
    419   {
    420     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    421                 "Processing failed; rolling back transaction\n");
    422     TALER_AUDITORDB_rollback (TALER_ARL_adb);
    423     TALER_EXCHANGEDB_rollback (TALER_ARL_edb);
    424   }
    425   switch (qs)
    426   {
    427   case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    428     return GNUNET_OK;
    429   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    430     return GNUNET_OK;
    431   case GNUNET_DB_STATUS_SOFT_ERROR:
    432     return GNUNET_NO;
    433   case GNUNET_DB_STATUS_HARD_ERROR:
    434     return GNUNET_SYSERR;
    435   }
    436   return GNUNET_OK;
    437 }
    438 
    439 
    440 enum GNUNET_GenericReturnValue
    441 TALER_ARL_setup_sessions_and_run (TALER_ARL_Analysis ana,
    442                                   void *ana_cls)
    443 {
    444   enum GNUNET_GenericReturnValue ret;
    445 
    446   if (GNUNET_SYSERR ==
    447       TALER_EXCHANGEDB_preflight (TALER_ARL_edb))
    448   {
    449     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    450                 "Failed to initialize exchange connection.\n");
    451     return GNUNET_SYSERR;
    452   }
    453   if (GNUNET_SYSERR ==
    454       TALER_AUDITORDB_preflight (TALER_ARL_adb))
    455   {
    456     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    457                 "Failed to initialize auditor session.\n");
    458     return GNUNET_SYSERR;
    459   }
    460 
    461   for (unsigned int retries = 0; retries<3; retries++)
    462   {
    463     ret = transact (ana,
    464                     ana_cls);
    465     if (GNUNET_NO != ret)
    466       break;
    467     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    468                 "Serialization issue, trying again\n");
    469   }
    470   if (GNUNET_OK != ret)
    471   {
    472     /* Includes the retries running out: the analysis was rolled back every
    473        time, so there is nothing committed to report success about. */
    474     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    475                 "Analysis failed, not recording progress\n");
    476     return GNUNET_SYSERR;
    477   }
    478   return GNUNET_OK;
    479 }
    480 
    481 
    482 void
    483 TALER_ARL_amount_add_ (struct TALER_Amount *sum,
    484                        const struct TALER_Amount *a1,
    485                        const struct TALER_Amount *a2,
    486                        const char *filename,
    487                        const char *functionname,
    488                        unsigned int line)
    489 {
    490   enum TALER_AmountArithmeticResult aar;
    491   const char *msg;
    492   char *a2s;
    493 
    494   aar = TALER_amount_add (sum,
    495                           a1,
    496                           a2);
    497   if (aar >= 0)
    498     return;
    499   switch (aar)
    500   {
    501   case TALER_AAR_INVALID_RESULT_OVERFLOW:
    502     msg =
    503       "arithmetic overflow in amount addition (likely the database is corrupt, see manual)";
    504     break;
    505   case TALER_AAR_INVALID_NORMALIZATION_FAILED:
    506     msg =
    507       "normalization failed in amount addition (likely the database is corrupt, see manual)";
    508     break;
    509   case TALER_AAR_INVALID_CURRENCIES_INCOMPATIBLE:
    510     msg =
    511       "incompatible currencies in amount addition (likely bad configuration and auditor code missing a sanity check, see manual)";
    512     break;
    513   default:
    514     GNUNET_assert (0); /* should be impossible */
    515   }
    516   a2s = TALER_amount_to_string (a2);
    517   fprintf (stderr,
    518            "Aborting audit due to fatal error in function %s at %s:%d trying to add %s to %s: %s\n",
    519            functionname,
    520            filename,
    521            line,
    522            TALER_amount2s (a1),
    523            a2s,
    524            msg);
    525   GNUNET_free (a2s);
    526   exit (42);
    527 }
    528 
    529 
    530 void
    531 TALER_ARL_amount_subtract_ (struct TALER_Amount *diff,
    532                             const struct TALER_Amount *a1,
    533                             const struct TALER_Amount *a2,
    534                             const char *filename,
    535                             const char *functionname,
    536                             unsigned int line)
    537 {
    538   enum TALER_AmountArithmeticResult aar;
    539   const char *msg;
    540   char *a2s;
    541 
    542   aar = TALER_amount_subtract (diff,
    543                                a1,
    544                                a2);
    545   if (aar >= 0)
    546     return;
    547   switch (aar)
    548   {
    549   case TALER_AAR_INVALID_NEGATIVE_RESULT:
    550     msg =
    551       "negative result in amount subtraction (likely the database is corrupt, see manual)";
    552     break;
    553   case TALER_AAR_INVALID_NORMALIZATION_FAILED:
    554     msg =
    555       "normalization failed in amount subtraction (likely the database is corrupt, see manual)";
    556     break;
    557   case TALER_AAR_INVALID_CURRENCIES_INCOMPATIBLE:
    558     msg =
    559       "currencies incompatible in amount subtraction (likely bad configuration and auditor code missing a sanity check, see manual)";
    560     break;
    561   default:
    562     GNUNET_assert (0); /* should be impossible */
    563   }
    564   a2s = TALER_amount_to_string (a2);
    565   fprintf (stderr,
    566            "Aborting audit due to fatal error in function %s at %s:%d trying to subtract %s from %s: %s\n",
    567            functionname,
    568            filename,
    569            line,
    570            a2s,
    571            TALER_amount2s (a1),
    572            msg);
    573   GNUNET_free (a2s);
    574   exit (42);
    575 }
    576 
    577 
    578 enum TALER_ARL_SubtractionResult
    579 TALER_ARL_amount_subtract_neg_ (struct TALER_Amount *diff,
    580                                 const struct TALER_Amount *a1,
    581                                 const struct TALER_Amount *a2,
    582                                 const char *filename,
    583                                 const char *functionname,
    584                                 unsigned int line)
    585 {
    586   enum TALER_AmountArithmeticResult aar;
    587   const char *msg;
    588   char *a2s;
    589 
    590   aar = TALER_amount_subtract (diff,
    591                                a1,
    592                                a2);
    593   switch (aar)
    594   {
    595   case TALER_AAR_RESULT_POSITIVE:
    596     return TALER_ARL_SR_POSITIVE;
    597   case TALER_AAR_RESULT_ZERO:
    598     return TALER_ARL_SR_ZERO;
    599   case TALER_AAR_INVALID_NEGATIVE_RESULT:
    600     return TALER_ARL_SR_INVALID_NEGATIVE;
    601   case TALER_AAR_INVALID_NORMALIZATION_FAILED:
    602     msg =
    603       "normalization failed in amount subtraction (likely the database is corrupt, see manual)";
    604     break;
    605   case TALER_AAR_INVALID_CURRENCIES_INCOMPATIBLE:
    606     msg =
    607       "currencies incompatible in amount subtraction (likely bad configuration and auditor code missing a sanity check, see manual)";
    608     break;
    609   default:
    610     GNUNET_assert (0); /* should be impossible */
    611   }
    612   a2s = TALER_amount_to_string (a2);
    613   fprintf (stderr,
    614            "Aborting audit due to fatal error in function %s at %s:%d trying to subtract %s from %s: %s\n",
    615            functionname,
    616            filename,
    617            line,
    618            a2s,
    619            TALER_amount2s (a1),
    620            msg);
    621   GNUNET_free (a2s);
    622   exit (42);
    623 }
    624 
    625 
    626 enum GNUNET_GenericReturnValue
    627 TALER_ARL_init (const struct GNUNET_CONFIGURATION_Handle *c)
    628 {
    629   TALER_ARL_cfg = c;
    630   start_time = GNUNET_TIME_absolute_get ();
    631 
    632   if (GNUNET_OK !=
    633       GNUNET_CONFIGURATION_get_value_string (TALER_ARL_cfg,
    634                                              "auditor",
    635                                              "BASE_URL",
    636                                              &TALER_ARL_auditor_url))
    637   {
    638     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    639                                "auditor",
    640                                "BASE_URL");
    641     return GNUNET_SYSERR;
    642   }
    643   if (GNUNET_OK !=
    644       GNUNET_CONFIGURATION_get_value_string (TALER_ARL_cfg,
    645                                              "exchange",
    646                                              "BASE_URL",
    647                                              &TALER_ARL_exchange_url))
    648   {
    649     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    650                                "exchange",
    651                                "BASE_URL");
    652     return GNUNET_SYSERR;
    653   }
    654 
    655   if (GNUNET_is_zero (&TALER_ARL_master_pub))
    656   {
    657     /* -m option not given, try configuration */
    658     char *master_public_key_str;
    659 
    660     if (GNUNET_OK !=
    661         GNUNET_CONFIGURATION_get_value_string (TALER_ARL_cfg,
    662                                                "exchange",
    663                                                "MASTER_PUBLIC_KEY",
    664                                                &master_public_key_str))
    665     {
    666       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    667                   "Pass option -m or set MASTER_PUBLIC_KEY in the configuration!\n");
    668       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    669                                  "exchange",
    670                                  "MASTER_PUBLIC_KEY");
    671       return GNUNET_SYSERR;
    672     }
    673     if (GNUNET_OK !=
    674         GNUNET_CRYPTO_eddsa_public_key_from_string (
    675           master_public_key_str,
    676           strlen (master_public_key_str),
    677           &TALER_ARL_master_pub.eddsa_pub))
    678     {
    679       GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
    680                                  "exchange",
    681                                  "MASTER_PUBLIC_KEY",
    682                                  "invalid key");
    683       GNUNET_free (master_public_key_str);
    684       return GNUNET_SYSERR;
    685     }
    686     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    687                 "Running auditor against exchange master public key `%s'\n",
    688                 master_public_key_str);
    689     GNUNET_free (master_public_key_str);
    690   } /* end of -m not given */
    691 
    692   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    693               "Taler auditor running for exchange master public key %s\n",
    694               TALER_B2S (&TALER_ARL_master_pub));
    695 
    696   if (GNUNET_is_zero (&TALER_ARL_auditor_pub))
    697   {
    698     char *auditor_public_key_str;
    699 
    700     if (GNUNET_OK ==
    701         GNUNET_CONFIGURATION_get_value_string (c,
    702                                                "auditor",
    703                                                "PUBLIC_KEY",
    704                                                &auditor_public_key_str))
    705     {
    706       if (GNUNET_OK !=
    707           GNUNET_CRYPTO_eddsa_public_key_from_string (
    708             auditor_public_key_str,
    709             strlen (auditor_public_key_str),
    710             &TALER_ARL_auditor_pub.eddsa_pub))
    711       {
    712         GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
    713                                    "auditor",
    714                                    "PUBLIC_KEY",
    715                                    "invalid key");
    716         GNUNET_free (auditor_public_key_str);
    717         return GNUNET_SYSERR;
    718       }
    719       GNUNET_free (auditor_public_key_str);
    720     }
    721   }
    722 
    723   if (GNUNET_is_zero (&TALER_ARL_auditor_pub))
    724   {
    725     /* public key not configured */
    726     /* try loading private key and deriving public key */
    727     char *fn;
    728 
    729     if (GNUNET_OK ==
    730         GNUNET_CONFIGURATION_get_value_filename (c,
    731                                                  "auditor",
    732                                                  "AUDITOR_PRIV_FILE",
    733                                                  &fn))
    734     {
    735       struct TALER_AuditorPrivateKeyP auditor_priv;
    736 
    737       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    738                   "Loading offline private key from `%s' to get auditor public key\n",
    739                   fn);
    740       if (GNUNET_OK ==
    741           GNUNET_CRYPTO_eddsa_key_from_file (fn,
    742                                              GNUNET_NO, /* do NOT create it! */
    743                                              &auditor_priv.eddsa_priv))
    744       {
    745         GNUNET_CRYPTO_eddsa_key_get_public (&auditor_priv.eddsa_priv,
    746                                             &TALER_ARL_auditor_pub.eddsa_pub);
    747       }
    748       GNUNET_free (fn);
    749     }
    750   }
    751 
    752   if (GNUNET_is_zero (&TALER_ARL_auditor_pub))
    753   {
    754     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    755                                "auditor",
    756                                "PUBLIC_KEY/AUDITOR_PRIV_FILE");
    757     return GNUNET_SYSERR;
    758   }
    759 
    760   if (GNUNET_OK !=
    761       TALER_config_get_currency (TALER_ARL_cfg,
    762                                  "exchange",
    763                                  &TALER_ARL_currency))
    764   {
    765     return GNUNET_SYSERR;
    766   }
    767   {
    768     if ( (GNUNET_OK !=
    769           TALER_config_get_amount (TALER_ARL_cfg,
    770                                    "exchange",
    771                                    "CURRENCY_ROUND_UNIT",
    772                                    &TALER_ARL_currency_round_unit)) ||
    773          ( (0 != TALER_ARL_currency_round_unit.fraction) &&
    774            (0 != TALER_ARL_currency_round_unit.value) ) )
    775     {
    776       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    777                   "Need non-zero value in section `exchange' under `CURRENCY_ROUND_UNIT'\n");
    778       return GNUNET_SYSERR;
    779     }
    780   }
    781   if (NULL ==
    782       (TALER_ARL_edb = TALER_EXCHANGEDB_connect (TALER_ARL_cfg)))
    783   {
    784     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    785                 "Failed to initialize exchange database plugin.\n");
    786     TALER_ARL_done ();
    787     return GNUNET_SYSERR;
    788   }
    789   if (NULL ==
    790       (TALER_ARL_adb = TALER_AUDITORDB_connect (TALER_ARL_cfg)))
    791   {
    792     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    793                 "Failed to initialize auditor database plugin.\n");
    794     TALER_ARL_done ();
    795     return GNUNET_SYSERR;
    796   }
    797   if (GNUNET_SYSERR ==
    798       TALER_AUDITORDB_preflight (TALER_ARL_adb))
    799   {
    800     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    801                 "Failed to start session with auditor database.\n");
    802     TALER_ARL_done ();
    803     return GNUNET_SYSERR;
    804   }
    805   return GNUNET_OK;
    806 }
    807 
    808 
    809 void
    810 TALER_ARL_done ()
    811 {
    812   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    813               "Audit complete\n");
    814   if (NULL != TALER_ARL_adb)
    815   {
    816     TALER_AUDITORDB_disconnect (TALER_ARL_adb);
    817     TALER_ARL_adb = NULL;
    818   }
    819   if (NULL != TALER_ARL_edb)
    820   {
    821     TALER_EXCHANGEDB_disconnect (TALER_ARL_edb);
    822     TALER_ARL_edb = NULL;
    823   }
    824   GNUNET_free (TALER_ARL_exchange_url);
    825   GNUNET_free (TALER_ARL_auditor_url);
    826 }
    827 
    828 
    829 /* end of report-lib.c */