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


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