exchange

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

testing_api_cmd_get_exchange.c (10680B)


      1 /*
      2   This file is part of TALER
      3   (C) 2023 Taler Systems SA
      4 
      5   TALER is free software; you can redistribute it and/or modify
      6   it under the terms of the GNU General Public License as
      7   published by the Free Software Foundation; either version 3, or
      8   (at your option) any later version.
      9 
     10   TALER is distributed in the hope that it will be useful, but
     11   WITHOUT ANY WARRANTY; without even the implied warranty of
     12   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     13   GNU General Public License for more details.
     14 
     15   You should have received a copy of the GNU General Public
     16   License along with TALER; see the file COPYING.  If not, see
     17   <http://www.gnu.org/licenses/>
     18 */
     19 /**
     20  * @file testing/testing_api_cmd_get_exchange.c
     21  * @brief Command to get an exchange handle
     22  * @author Christian Grothoff
     23  */
     24 #include "taler/platform.h"
     25 #include "taler/taler_json_lib.h"
     26 #include <gnunet/gnunet_curl_lib.h>
     27 #include "taler/taler_testing_lib.h"
     28 
     29 
     30 /**
     31  * State for a "get exchange" CMD.
     32  */
     33 struct GetExchangeState
     34 {
     35 
     36   /**
     37    * Master private key of the exchange.
     38    */
     39   struct TALER_MasterPrivateKeyP master_priv;
     40 
     41   /**
     42    * Our interpreter state.
     43    */
     44   struct TALER_TESTING_Interpreter *is;
     45 
     46   /**
     47    * Exchange handle we produced.
     48    */
     49   struct TALER_EXCHANGE_GetKeysHandle *exchange;
     50 
     51   /**
     52    * Keys of the exchange.
     53    */
     54   struct TALER_EXCHANGE_Keys *keys;
     55 
     56   /**
     57    * URL of the exchange.
     58    */
     59   char *exchange_url;
     60 
     61   /**
     62    * Filename of the master private key of the exchange.
     63    */
     64   char *master_priv_file;
     65 
     66   /**
     67    * Label of a command to use to obtain existing
     68    * keys.
     69    */
     70   const char *last_keys_ref;
     71 
     72   /**
     73    * Last denomination date we received when doing this request.
     74    */
     75   struct GNUNET_TIME_Timestamp my_denom_date;
     76 
     77   /**
     78    * Are we waiting for /keys before continuing?
     79    */
     80   bool wait_for_keys;
     81 };
     82 
     83 
     84 /**
     85  * Function called with information about who is auditing
     86  * a particular exchange and what keys the exchange is using.
     87  *
     88  * @param cls closure
     89  * @param kr response from /keys
     90  * @param[in] keys the keys of the exchange
     91  */
     92 static void
     93 cert_cb (void *cls,
     94          const struct TALER_EXCHANGE_KeysResponse *kr,
     95          struct TALER_EXCHANGE_Keys *keys)
     96 {
     97   struct GetExchangeState *ges = cls;
     98   const struct TALER_EXCHANGE_HttpResponse *hr = &kr->hr;
     99   struct TALER_TESTING_Interpreter *is = ges->is;
    100 
    101   ges->exchange = NULL;
    102   if (NULL != ges->keys)
    103     TALER_EXCHANGE_keys_decref (ges->keys);
    104   ges->keys = keys;
    105   switch (hr->http_status)
    106   {
    107   case MHD_HTTP_OK:
    108     if (ges->wait_for_keys)
    109     {
    110       ges->wait_for_keys = false;
    111       TALER_TESTING_interpreter_next (is);
    112       return;
    113     }
    114     ges->my_denom_date = kr->details.ok.keys->last_denom_issue_date;
    115     return;
    116   default:
    117     GNUNET_break (0);
    118     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    119                 "/keys responded with HTTP status %u\n",
    120                 hr->http_status);
    121     if (ges->wait_for_keys)
    122     {
    123       ges->wait_for_keys = false;
    124       TALER_TESTING_interpreter_fail (is);
    125       return;
    126     }
    127     return;
    128   }
    129 }
    130 
    131 
    132 /**
    133  * Run the "get_exchange" command.
    134  *
    135  * @param cls closure.
    136  * @param cmd the command currently being executed.
    137  * @param is the interpreter state.
    138  */
    139 static void
    140 get_exchange_run (void *cls,
    141                   const struct TALER_TESTING_Command *cmd,
    142                   struct TALER_TESTING_Interpreter *is)
    143 {
    144   struct GetExchangeState *ges = cls;
    145   struct TALER_EXCHANGE_Keys *xkeys = NULL;
    146 
    147   (void) cmd;
    148   if (NULL == ges->exchange_url)
    149   {
    150     GNUNET_break (0);
    151     TALER_TESTING_interpreter_fail (is);
    152     return;
    153   }
    154   if (NULL != ges->last_keys_ref)
    155   {
    156     const struct TALER_TESTING_Command *state_cmd;
    157     struct TALER_EXCHANGE_Keys *old_keys;
    158     const char *exchange_url;
    159     json_t *s_keys;
    160 
    161     state_cmd
    162       = TALER_TESTING_interpreter_lookup_command (is,
    163                                                   ges->last_keys_ref);
    164     if (NULL == state_cmd)
    165     {
    166       /* Command providing serialized keys not found.  */
    167       GNUNET_break (0);
    168       TALER_TESTING_interpreter_fail (is);
    169       return;
    170     }
    171     if (GNUNET_OK !=
    172         TALER_TESTING_get_trait_keys (state_cmd,
    173                                       &old_keys))
    174     {
    175       GNUNET_break (0);
    176       TALER_TESTING_interpreter_fail (is);
    177       return;
    178     }
    179     if (NULL == old_keys)
    180     {
    181       GNUNET_break (0);
    182       TALER_TESTING_interpreter_fail (is);
    183       return;
    184     }
    185     if (GNUNET_OK !=
    186         TALER_TESTING_get_trait_exchange_url (state_cmd,
    187                                               &exchange_url))
    188     {
    189       GNUNET_break (0);
    190       TALER_TESTING_interpreter_fail (is);
    191       return;
    192     }
    193     if (0 != strcmp (exchange_url,
    194                      ges->exchange_url))
    195     {
    196       GNUNET_break (0);
    197       TALER_TESTING_interpreter_fail (is);
    198       return;
    199     }
    200     s_keys = TALER_EXCHANGE_keys_to_json (old_keys);
    201     if (NULL == s_keys)
    202     {
    203       GNUNET_break (0);
    204       TALER_TESTING_interpreter_fail (is);
    205       return;
    206     }
    207     xkeys = TALER_EXCHANGE_keys_from_json (s_keys);
    208     if (NULL == xkeys)
    209     {
    210       GNUNET_break (0);
    211       json_dumpf (s_keys,
    212                   stderr,
    213                   JSON_INDENT (2));
    214       json_decref (s_keys);
    215       TALER_TESTING_interpreter_fail (is);
    216       return;
    217     }
    218     json_decref (s_keys);
    219   }
    220   if (NULL != ges->master_priv_file)
    221   {
    222     if (GNUNET_SYSERR ==
    223         GNUNET_CRYPTO_eddsa_key_from_file (ges->master_priv_file,
    224                                            GNUNET_YES,
    225                                            &ges->master_priv.eddsa_priv))
    226     {
    227       GNUNET_break (0);
    228       TALER_EXCHANGE_keys_decref (xkeys);
    229       TALER_TESTING_interpreter_fail (is);
    230       return;
    231     }
    232   }
    233   ges->is = is;
    234   ges->exchange
    235     = TALER_EXCHANGE_get_keys (TALER_TESTING_interpreter_get_context (is),
    236                                ges->exchange_url,
    237                                xkeys,
    238                                &cert_cb,
    239                                ges);
    240   TALER_EXCHANGE_keys_decref (xkeys);
    241   if (NULL == ges->exchange)
    242   {
    243     GNUNET_break (0);
    244     TALER_TESTING_interpreter_fail (is);
    245     return;
    246   }
    247   if (! ges->wait_for_keys)
    248     TALER_TESTING_interpreter_next (is);
    249 }
    250 
    251 
    252 /**
    253  * Cleanup the state.
    254  *
    255  * @param cls closure.
    256  * @param cmd the command which is being cleaned up.
    257  */
    258 static void
    259 get_exchange_cleanup (void *cls,
    260                       const struct TALER_TESTING_Command *cmd)
    261 {
    262   struct GetExchangeState *ges = cls;
    263 
    264   if (NULL != ges->exchange)
    265   {
    266     TALER_EXCHANGE_get_keys_cancel (ges->exchange);
    267     ges->exchange = NULL;
    268   }
    269   TALER_EXCHANGE_keys_decref (ges->keys);
    270   ges->keys = NULL;
    271   GNUNET_free (ges->master_priv_file);
    272   GNUNET_free (ges->exchange_url);
    273   GNUNET_free (ges);
    274 }
    275 
    276 
    277 /**
    278  * Offer internal data to a "get_exchange" CMD state to other commands.
    279  *
    280  * @param cls closure
    281  * @param[out] ret result (could be anything)
    282  * @param trait name of the trait
    283  * @param index index number of the object to offer.
    284  * @return #GNUNET_OK on success
    285  */
    286 static enum GNUNET_GenericReturnValue
    287 get_exchange_traits (void *cls,
    288                      const void **ret,
    289                      const char *trait,
    290                      unsigned int index)
    291 {
    292   struct GetExchangeState *ges = cls;
    293   unsigned int off = (NULL == ges->master_priv_file) ? 1 : 0;
    294 
    295   if (NULL != ges->keys)
    296   {
    297     struct TALER_TESTING_Trait traits[] = {
    298       TALER_TESTING_make_trait_master_priv (&ges->master_priv),
    299       TALER_TESTING_make_trait_master_pub (&ges->keys->master_pub),
    300       TALER_TESTING_make_trait_keys (ges->keys),
    301       TALER_TESTING_make_trait_exchange_url (ges->exchange_url),
    302       TALER_TESTING_make_trait_timestamp (0,
    303                                           &ges->my_denom_date),
    304       TALER_TESTING_trait_end ()
    305     };
    306 
    307     return TALER_TESTING_get_trait (&traits[off],
    308                                     ret,
    309                                     trait,
    310                                     index);
    311   }
    312   else
    313   {
    314     struct TALER_TESTING_Trait traits[] = {
    315       TALER_TESTING_make_trait_master_priv (&ges->master_priv),
    316       TALER_TESTING_make_trait_exchange_url (ges->exchange_url),
    317       TALER_TESTING_make_trait_timestamp (0,
    318                                           &ges->my_denom_date),
    319       TALER_TESTING_trait_end ()
    320     };
    321 
    322     return TALER_TESTING_get_trait (&traits[off],
    323                                     ret,
    324                                     trait,
    325                                     index);
    326   }
    327 }
    328 
    329 
    330 /**
    331  * Get the base URL of the exchange from @a cfg.
    332  *
    333  * @param cfg configuration to evaluate
    334  * @return base URL of the exchange according to @a cfg
    335  */
    336 static char *
    337 get_exchange_base_url (
    338   const struct GNUNET_CONFIGURATION_Handle *cfg)
    339 {
    340   char *exchange_url;
    341 
    342   if (GNUNET_OK !=
    343       GNUNET_CONFIGURATION_get_value_string (cfg,
    344                                              "exchange",
    345                                              "BASE_URL",
    346                                              &exchange_url))
    347   {
    348     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    349                                "exchange",
    350                                "BASE_URL");
    351     return NULL;
    352   }
    353   return exchange_url;
    354 }
    355 
    356 
    357 /**
    358  * Get the file name of the master private key file of the exchange from @a
    359  * cfg.
    360  *
    361  * @param cfg configuration to evaluate
    362  * @return base URL of the exchange according to @a cfg
    363  */
    364 static char *
    365 get_exchange_master_priv_file (
    366   const struct GNUNET_CONFIGURATION_Handle *cfg)
    367 {
    368   char *fn;
    369 
    370   if (GNUNET_OK !=
    371       GNUNET_CONFIGURATION_get_value_filename (cfg,
    372                                                "exchange-offline",
    373                                                "MASTER_PRIV_FILE",
    374                                                &fn))
    375   {
    376     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    377                                "exchange-offline",
    378                                "MASTER_PRIV_FILE");
    379     return NULL;
    380   }
    381   return fn;
    382 }
    383 
    384 
    385 struct TALER_TESTING_Command
    386 TALER_TESTING_cmd_get_exchange (
    387   const char *label,
    388   const struct GNUNET_CONFIGURATION_Handle *cfg,
    389   const char *last_keys_ref,
    390   bool wait_for_keys,
    391   bool load_private_key)
    392 {
    393   struct GetExchangeState *ges;
    394 
    395   ges = GNUNET_new (struct GetExchangeState);
    396   ges->exchange_url = get_exchange_base_url (cfg);
    397   ges->last_keys_ref = last_keys_ref;
    398   if (load_private_key)
    399     ges->master_priv_file = get_exchange_master_priv_file (cfg);
    400   ges->wait_for_keys = wait_for_keys;
    401   {
    402     struct TALER_TESTING_Command cmd = {
    403       .cls = ges,
    404       .label = label,
    405       .run = &get_exchange_run,
    406       .cleanup = &get_exchange_cleanup,
    407       .traits = &get_exchange_traits,
    408       .name = "exchange"
    409     };
    410 
    411     return cmd;
    412   }
    413 }