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 (24969B)


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