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


      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   if (NULL == denominations)
    182   {
    183     denominations = GNUNET_CONTAINER_multihashmap_create (256,
    184                                                           GNUNET_NO);
    185     if (NULL  == denominations_by_serial)
    186       denominations_by_serial = GNUNET_CONTAINER_multiuuidmap_create (256,
    187                                                                       GNUNET_NO)
    188       ;
    189 
    190     qs = TALER_EXCHANGEDB_iterate_denomination_info (TALER_ARL_edb,
    191                                                      &add_denomination,
    192                                                      NULL);
    193     if (0 > qs)
    194     {
    195       GNUNET_break (0);
    196       *issuep = NULL;
    197       return qs;
    198     }
    199   }
    200   {
    201     const struct TALER_EXCHANGEDB_DenominationKeyInformation *i;
    202 
    203     i = GNUNET_CONTAINER_multihashmap_get (denominations,
    204                                            &dh->hash);
    205     if (NULL != i)
    206     {
    207       /* cache hit */
    208       *issuep = i;
    209       return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
    210     }
    211   }
    212   /* maybe database changed since we last iterated, give it one more shot */
    213   {
    214     struct TALER_EXCHANGEDB_DenominationKeyInformation issue;
    215     uint64_t denom_serial;
    216 
    217     qs = TALER_EXCHANGEDB_get_denomination_info (TALER_ARL_edb,
    218                                                  dh,
    219                                                  &denom_serial,
    220                                                  &issue);
    221     if (qs <= 0)
    222     {
    223       GNUNET_break (qs >= 0);
    224       if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    225         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    226                     "Denomination %s not found\n",
    227                     TALER_B2S (dh));
    228       return qs;
    229     }
    230 
    231     add_denomination (NULL,
    232                       denom_serial,
    233                       NULL,
    234                       &issue);
    235   }
    236   {
    237     const struct TALER_EXCHANGEDB_DenominationKeyInformation *i;
    238 
    239     i = GNUNET_CONTAINER_multihashmap_get (denominations,
    240                                            &dh->hash);
    241     if (NULL != i)
    242     {
    243       /* cache hit */
    244       *issuep = i;
    245       return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
    246     }
    247   }
    248   /* We found more keys, but not the denomination we are looking for :-( */
    249   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    250               "Denomination %s not found\n",
    251               TALER_B2S (dh));
    252   return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
    253 }
    254 
    255 
    256 enum GNUNET_DB_QueryStatus
    257 TALER_ARL_get_denomination_info_by_serial (
    258   uint64_t denom_serial,
    259   const struct TALER_EXCHANGEDB_DenominationKeyInformation **issuep)
    260 {
    261   enum GNUNET_DB_QueryStatus qs;
    262   struct GNUNET_Uuid uuid;
    263 
    264   serial_to_uuid (denom_serial,
    265                   &uuid);
    266   if (NULL == denominations_by_serial)
    267   {
    268     denominations_by_serial = GNUNET_CONTAINER_multiuuidmap_create (256,
    269                                                                     GNUNET_NO);
    270     if (NULL == denominations)
    271       denominations = GNUNET_CONTAINER_multihashmap_create (256,
    272                                                             GNUNET_NO);
    273 
    274     qs = TALER_EXCHANGEDB_iterate_denomination_info (TALER_ARL_edb,
    275                                                      &add_denomination,
    276                                                      NULL);
    277     if (0 > qs)
    278     {
    279       GNUNET_break (0);
    280       *issuep = NULL;
    281       return qs;
    282     }
    283   }
    284   {
    285     const struct TALER_EXCHANGEDB_DenominationKeyInformation *i;
    286 
    287     i = GNUNET_CONTAINER_multiuuidmap_get (denominations_by_serial,
    288                                            &uuid);
    289     if (NULL != i)
    290     {
    291       /* cache hit */
    292       *issuep = i;
    293       return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
    294     }
    295   }
    296   /* maybe database changed since we last iterated, give it one more shot */
    297   {
    298     struct TALER_EXCHANGEDB_DenominationKeyInformation issue;
    299 
    300     qs = TALER_EXCHANGEDB_get_denomination_by_serial (TALER_ARL_edb,
    301                                                       denom_serial,
    302                                                       &issue);
    303     if (qs <= 0)
    304     {
    305       GNUNET_break (qs >= 0);
    306       if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    307         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    308                     "Denomination with serial %lu not found\n",
    309                     denom_serial);
    310       return qs;
    311     }
    312 
    313     add_denomination (NULL,
    314                       denom_serial,
    315                       NULL,
    316                       &issue);
    317   }
    318 
    319   {
    320     const struct TALER_EXCHANGEDB_DenominationKeyInformation *i;
    321 
    322     i = GNUNET_CONTAINER_multiuuidmap_get (denominations_by_serial,
    323                                            &uuid);
    324     if (NULL != i)
    325     {
    326       /* cache hit */
    327       *issuep = i;
    328       return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
    329     }
    330   }
    331   /* We found more keys, but not the denomination we are looking for :-( */
    332   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    333               "Denomination with serial %lu not found\n",
    334               denom_serial);
    335   return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
    336 }
    337 
    338 
    339 enum GNUNET_DB_QueryStatus
    340 TALER_ARL_get_denomination_info (
    341   const struct TALER_DenominationPublicKey *denom_pub,
    342   const struct TALER_EXCHANGEDB_DenominationKeyInformation **issue,
    343   struct TALER_DenominationHashP *dh)
    344 {
    345   struct TALER_DenominationHashP hc;
    346 
    347   if (NULL == dh)
    348     dh = &hc;
    349   TALER_denom_pub_hash (denom_pub,
    350                         dh);
    351   return TALER_ARL_get_denomination_info_by_hash (dh,
    352                                                   issue);
    353 }
    354 
    355 
    356 /**
    357  * Perform the given @a analysis within a transaction scope.
    358  * Commit on success.
    359  *
    360  * @param analysis analysis to run
    361  * @param analysis_cls closure for @a analysis
    362  * @return #GNUNET_OK if @a analysis successfully committed,
    363  *         #GNUNET_NO if we had an error on commit (retry may help)
    364  *         #GNUNET_SYSERR on hard errors
    365  */
    366 static enum GNUNET_GenericReturnValue
    367 transact (TALER_ARL_Analysis analysis,
    368           void *analysis_cls)
    369 {
    370   int ret;
    371   enum GNUNET_DB_QueryStatus qs;
    372 
    373   ret = TALER_AUDITORDB_start (TALER_ARL_adb);
    374   if (GNUNET_OK != ret)
    375   {
    376     GNUNET_break (0);
    377     return GNUNET_SYSERR;
    378   }
    379   if (GNUNET_OK !=
    380       TALER_EXCHANGEDB_preflight (TALER_ARL_edb))
    381   {
    382     GNUNET_break (0);
    383     return GNUNET_SYSERR;
    384   }
    385   ret = TALER_EXCHANGEDB_start (TALER_ARL_edb,
    386                                 "auditor");
    387   if (GNUNET_OK != ret)
    388   {
    389     GNUNET_break (0);
    390     TALER_EXCHANGEDB_rollback (TALER_ARL_edb);
    391     return GNUNET_SYSERR;
    392   }
    393   qs = analysis (analysis_cls);
    394   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
    395   {
    396     qs = TALER_EXCHANGEDB_commit (TALER_ARL_edb);
    397     if (0 > qs)
    398     {
    399       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    400       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    401                   "Exchange DB commit failed, rolling back transaction\n");
    402       TALER_AUDITORDB_rollback (TALER_ARL_adb);
    403     }
    404     else
    405     {
    406       qs = TALER_AUDITORDB_commit (TALER_ARL_adb);
    407       if (0 > qs)
    408       {
    409         GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    410         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    411                     "Auditor DB commit failed!\n");
    412       }
    413     }
    414   }
    415   else
    416   {
    417     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    418                 "Processing failed; rolling back transaction\n");
    419     TALER_AUDITORDB_rollback (TALER_ARL_adb);
    420     TALER_EXCHANGEDB_rollback (TALER_ARL_edb);
    421   }
    422   switch (qs)
    423   {
    424   case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    425     return GNUNET_OK;
    426   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    427     return GNUNET_OK;
    428   case GNUNET_DB_STATUS_SOFT_ERROR:
    429     return GNUNET_NO;
    430   case GNUNET_DB_STATUS_HARD_ERROR:
    431     return GNUNET_SYSERR;
    432   }
    433   return GNUNET_OK;
    434 }
    435 
    436 
    437 enum GNUNET_GenericReturnValue
    438 TALER_ARL_setup_sessions_and_run (TALER_ARL_Analysis ana,
    439                                   void *ana_cls)
    440 {
    441   enum GNUNET_DB_QueryStatus qs;
    442 
    443   if (GNUNET_SYSERR ==
    444       TALER_EXCHANGEDB_preflight (TALER_ARL_edb))
    445   {
    446     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    447                 "Failed to initialize exchange connection.\n");
    448     return GNUNET_SYSERR;
    449   }
    450   if (GNUNET_SYSERR ==
    451       TALER_AUDITORDB_preflight (TALER_ARL_adb))
    452   {
    453     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    454                 "Failed to initialize auditor session.\n");
    455     return GNUNET_SYSERR;
    456   }
    457 
    458   for (unsigned int retries=0; retries<3; retries++)
    459   {
    460     qs = transact (ana,
    461                    ana_cls);
    462     if (GNUNET_DB_STATUS_SOFT_ERROR != qs)
    463       break;
    464   }
    465   if (qs < 0)
    466     return GNUNET_SYSERR;
    467   return GNUNET_OK;
    468 }
    469 
    470 
    471 void
    472 TALER_ARL_amount_add_ (struct TALER_Amount *sum,
    473                        const struct TALER_Amount *a1,
    474                        const struct TALER_Amount *a2,
    475                        const char *filename,
    476                        const char *functionname,
    477                        unsigned int line)
    478 {
    479   enum TALER_AmountArithmeticResult aar;
    480   const char *msg;
    481   char *a2s;
    482 
    483   aar = TALER_amount_add (sum,
    484                           a1,
    485                           a2);
    486   if (aar >= 0)
    487     return;
    488   switch (aar)
    489   {
    490   case TALER_AAR_INVALID_RESULT_OVERFLOW:
    491     msg =
    492       "arithmetic overflow in amount addition (likely the database is corrupt, see manual)";
    493     break;
    494   case TALER_AAR_INVALID_NORMALIZATION_FAILED:
    495     msg =
    496       "normalization failed in amount addition (likely the database is corrupt, see manual)";
    497     break;
    498   case TALER_AAR_INVALID_CURRENCIES_INCOMPATIBLE:
    499     msg =
    500       "incompatible currencies in amount addition (likely bad configuration and auditor code missing a sanity check, see manual)";
    501     break;
    502   default:
    503     GNUNET_assert (0); /* should be impossible */
    504   }
    505   a2s = TALER_amount_to_string (a2);
    506   fprintf (stderr,
    507            "Aborting audit due to fatal error in function %s at %s:%d trying to add %s to %s: %s\n",
    508            functionname,
    509            filename,
    510            line,
    511            TALER_amount2s (a1),
    512            a2s,
    513            msg);
    514   GNUNET_free (a2s);
    515   exit (42);
    516 }
    517 
    518 
    519 void
    520 TALER_ARL_amount_subtract_ (struct TALER_Amount *diff,
    521                             const struct TALER_Amount *a1,
    522                             const struct TALER_Amount *a2,
    523                             const char *filename,
    524                             const char *functionname,
    525                             unsigned int line)
    526 {
    527   enum TALER_AmountArithmeticResult aar;
    528   const char *msg;
    529   char *a2s;
    530 
    531   aar = TALER_amount_subtract (diff,
    532                                a1,
    533                                a2);
    534   if (aar >= 0)
    535     return;
    536   switch (aar)
    537   {
    538   case TALER_AAR_INVALID_NEGATIVE_RESULT:
    539     msg =
    540       "negative result in amount subtraction (likely the database is corrupt, see manual)";
    541     break;
    542   case TALER_AAR_INVALID_NORMALIZATION_FAILED:
    543     msg =
    544       "normalization failed in amount subtraction (likely the database is corrupt, see manual)";
    545     break;
    546   case TALER_AAR_INVALID_CURRENCIES_INCOMPATIBLE:
    547     msg =
    548       "currencies incompatible in amount subtraction (likely bad configuration and auditor code missing a sanity check, see manual)";
    549     break;
    550   default:
    551     GNUNET_assert (0); /* should be impossible */
    552   }
    553   a2s = TALER_amount_to_string (a2);
    554   fprintf (stderr,
    555            "Aborting audit due to fatal error in function %s at %s:%d trying to subtract %s from %s: %s\n",
    556            functionname,
    557            filename,
    558            line,
    559            a2s,
    560            TALER_amount2s (a1),
    561            msg);
    562   GNUNET_free (a2s);
    563   exit (42);
    564 }
    565 
    566 
    567 enum TALER_ARL_SubtractionResult
    568 TALER_ARL_amount_subtract_neg_ (struct TALER_Amount *diff,
    569                                 const struct TALER_Amount *a1,
    570                                 const struct TALER_Amount *a2,
    571                                 const char *filename,
    572                                 const char *functionname,
    573                                 unsigned int line)
    574 {
    575   enum TALER_AmountArithmeticResult aar;
    576   const char *msg;
    577   char *a2s;
    578 
    579   aar = TALER_amount_subtract (diff,
    580                                a1,
    581                                a2);
    582   switch (aar)
    583   {
    584   case TALER_AAR_RESULT_POSITIVE:
    585     return TALER_ARL_SR_POSITIVE;
    586   case TALER_AAR_RESULT_ZERO:
    587     return TALER_ARL_SR_ZERO;
    588   case TALER_AAR_INVALID_NEGATIVE_RESULT:
    589     return TALER_ARL_SR_INVALID_NEGATIVE;
    590   case TALER_AAR_INVALID_NORMALIZATION_FAILED:
    591     msg =
    592       "normalization failed in amount subtraction (likely the database is corrupt, see manual)";
    593     break;
    594   case TALER_AAR_INVALID_CURRENCIES_INCOMPATIBLE:
    595     msg =
    596       "currencies incompatible in amount subtraction (likely bad configuration and auditor code missing a sanity check, see manual)";
    597     break;
    598   default:
    599     GNUNET_assert (0); /* should be impossible */
    600   }
    601   a2s = TALER_amount_to_string (a2);
    602   fprintf (stderr,
    603            "Aborting audit due to fatal error in function %s at %s:%d trying to subtract %s from %s: %s\n",
    604            functionname,
    605            filename,
    606            line,
    607            a2s,
    608            TALER_amount2s (a1),
    609            msg);
    610   GNUNET_free (a2s);
    611   exit (42);
    612 }
    613 
    614 
    615 enum GNUNET_GenericReturnValue
    616 TALER_ARL_init (const struct GNUNET_CONFIGURATION_Handle *c)
    617 {
    618   TALER_ARL_cfg = c;
    619   start_time = GNUNET_TIME_absolute_get ();
    620 
    621   if (GNUNET_OK !=
    622       GNUNET_CONFIGURATION_get_value_string (TALER_ARL_cfg,
    623                                              "auditor",
    624                                              "BASE_URL",
    625                                              &TALER_ARL_auditor_url))
    626   {
    627     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    628                                "auditor",
    629                                "BASE_URL");
    630     return GNUNET_SYSERR;
    631   }
    632   if (GNUNET_OK !=
    633       GNUNET_CONFIGURATION_get_value_string (TALER_ARL_cfg,
    634                                              "exchange",
    635                                              "BASE_URL",
    636                                              &TALER_ARL_exchange_url))
    637   {
    638     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    639                                "exchange",
    640                                "BASE_URL");
    641     return GNUNET_SYSERR;
    642   }
    643 
    644   if (GNUNET_is_zero (&TALER_ARL_master_pub))
    645   {
    646     /* -m option not given, try configuration */
    647     char *master_public_key_str;
    648 
    649     if (GNUNET_OK !=
    650         GNUNET_CONFIGURATION_get_value_string (TALER_ARL_cfg,
    651                                                "exchange",
    652                                                "MASTER_PUBLIC_KEY",
    653                                                &master_public_key_str))
    654     {
    655       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    656                   "Pass option -m or set MASTER_PUBLIC_KEY in the configuration!\n");
    657       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    658                                  "exchange",
    659                                  "MASTER_PUBLIC_KEY");
    660       return GNUNET_SYSERR;
    661     }
    662     if (GNUNET_OK !=
    663         GNUNET_CRYPTO_eddsa_public_key_from_string (
    664           master_public_key_str,
    665           strlen (master_public_key_str),
    666           &TALER_ARL_master_pub.eddsa_pub))
    667     {
    668       GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
    669                                  "exchange",
    670                                  "MASTER_PUBLIC_KEY",
    671                                  "invalid key");
    672       GNUNET_free (master_public_key_str);
    673       return GNUNET_SYSERR;
    674     }
    675     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    676                 "Running auditor against exchange master public key `%s'\n",
    677                 master_public_key_str);
    678     GNUNET_free (master_public_key_str);
    679   } /* end of -m not given */
    680 
    681   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    682               "Taler auditor running for exchange master public key %s\n",
    683               TALER_B2S (&TALER_ARL_master_pub));
    684 
    685   if (GNUNET_is_zero (&TALER_ARL_auditor_pub))
    686   {
    687     char *auditor_public_key_str;
    688 
    689     if (GNUNET_OK ==
    690         GNUNET_CONFIGURATION_get_value_string (c,
    691                                                "auditor",
    692                                                "PUBLIC_KEY",
    693                                                &auditor_public_key_str))
    694     {
    695       if (GNUNET_OK !=
    696           GNUNET_CRYPTO_eddsa_public_key_from_string (
    697             auditor_public_key_str,
    698             strlen (auditor_public_key_str),
    699             &TALER_ARL_auditor_pub.eddsa_pub))
    700       {
    701         GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
    702                                    "auditor",
    703                                    "PUBLIC_KEY",
    704                                    "invalid key");
    705         GNUNET_free (auditor_public_key_str);
    706         return GNUNET_SYSERR;
    707       }
    708       GNUNET_free (auditor_public_key_str);
    709     }
    710   }
    711 
    712   if (GNUNET_is_zero (&TALER_ARL_auditor_pub))
    713   {
    714     /* public key not configured */
    715     /* try loading private key and deriving public key */
    716     char *fn;
    717 
    718     if (GNUNET_OK ==
    719         GNUNET_CONFIGURATION_get_value_filename (c,
    720                                                  "auditor",
    721                                                  "AUDITOR_PRIV_FILE",
    722                                                  &fn))
    723     {
    724       struct TALER_AuditorPrivateKeyP auditor_priv;
    725 
    726       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    727                   "Loading offline private key from `%s' to get auditor public key\n",
    728                   fn);
    729       if (GNUNET_OK ==
    730           GNUNET_CRYPTO_eddsa_key_from_file (fn,
    731                                              GNUNET_NO, /* do NOT create it! */
    732                                              &auditor_priv.eddsa_priv))
    733       {
    734         GNUNET_CRYPTO_eddsa_key_get_public (&auditor_priv.eddsa_priv,
    735                                             &TALER_ARL_auditor_pub.eddsa_pub);
    736       }
    737       GNUNET_free (fn);
    738     }
    739   }
    740 
    741   if (GNUNET_is_zero (&TALER_ARL_auditor_pub))
    742   {
    743     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    744                                "auditor",
    745                                "PUBLIC_KEY/AUDITOR_PRIV_FILE");
    746     return GNUNET_SYSERR;
    747   }
    748 
    749   if (GNUNET_OK !=
    750       TALER_config_get_currency (TALER_ARL_cfg,
    751                                  "exchange",
    752                                  &TALER_ARL_currency))
    753   {
    754     return GNUNET_SYSERR;
    755   }
    756   {
    757     if ( (GNUNET_OK !=
    758           TALER_config_get_amount (TALER_ARL_cfg,
    759                                    "exchange",
    760                                    "CURRENCY_ROUND_UNIT",
    761                                    &TALER_ARL_currency_round_unit)) ||
    762          ( (0 != TALER_ARL_currency_round_unit.fraction) &&
    763            (0 != TALER_ARL_currency_round_unit.value) ) )
    764     {
    765       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    766                   "Need non-zero value in section `exchange' under `CURRENCY_ROUND_UNIT'\n");
    767       return GNUNET_SYSERR;
    768     }
    769   }
    770   if (NULL ==
    771       (TALER_ARL_edb = TALER_EXCHANGEDB_connect (TALER_ARL_cfg,
    772                                                  false)))
    773   {
    774     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    775                 "Failed to initialize exchange database plugin.\n");
    776     TALER_ARL_done ();
    777     return GNUNET_SYSERR;
    778   }
    779   if (NULL ==
    780       (TALER_ARL_adb = TALER_AUDITORDB_connect (TALER_ARL_cfg,
    781                                                 false)))
    782   {
    783     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    784                 "Failed to initialize auditor database plugin.\n");
    785     TALER_ARL_done ();
    786     return GNUNET_SYSERR;
    787   }
    788   if (GNUNET_SYSERR ==
    789       TALER_AUDITORDB_preflight (TALER_ARL_adb))
    790   {
    791     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    792                 "Failed to start session with auditor database.\n");
    793     TALER_ARL_done ();
    794     return GNUNET_SYSERR;
    795   }
    796   return GNUNET_OK;
    797 }
    798 
    799 
    800 void
    801 TALER_ARL_done ()
    802 {
    803   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    804               "Audit complete\n");
    805   if (NULL != TALER_ARL_adb)
    806   {
    807     TALER_AUDITORDB_disconnect (TALER_ARL_adb);
    808     TALER_ARL_adb = NULL;
    809   }
    810   if (NULL != TALER_ARL_edb)
    811   {
    812     TALER_EXCHANGEDB_disconnect (TALER_ARL_edb);
    813     TALER_ARL_edb = NULL;
    814   }
    815   GNUNET_free (TALER_ARL_exchange_url);
    816   GNUNET_free (TALER_ARL_auditor_url);
    817 }
    818 
    819 
    820 /* end of report-lib.c */