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_recoup_refresh.c (12294B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2014-2022 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_recoup_refresh.c
     21  * @brief Implement the /recoup-refresh test command.
     22  * @author Marcello Stanisci
     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 "pay back" CMD.
     32  */
     33 struct RecoupRefreshState
     34 {
     35   /**
     36    * Expected HTTP status code.
     37    */
     38   unsigned int expected_response_code;
     39 
     40   /**
     41    * Command that offers a reserve private key,
     42    * plus a coin to be paid back.
     43    */
     44   const char *coin_reference;
     45 
     46   /**
     47    * Entry in the old coin's history generated by this operation.
     48    */
     49   struct TALER_EXCHANGE_CoinHistoryEntry che_old;
     50 
     51   /**
     52    * Entry in the recouped coin's history generated by this operation.
     53    */
     54   struct TALER_EXCHANGE_CoinHistoryEntry che_new;
     55 
     56   /**
     57    * Public key of the refunded coin.
     58    */
     59   struct TALER_CoinSpendPublicKeyP coin_pub_old;
     60 
     61   /**
     62    * Public key of the refunded coin.
     63    */
     64   struct TALER_CoinSpendPublicKeyP coin_pub_new;
     65 
     66   /**
     67    * Amount to be recouped.
     68    */
     69   struct TALER_Amount amount;
     70 
     71   /**
     72    * The interpreter state.
     73    */
     74   struct TALER_TESTING_Interpreter *is;
     75 
     76   /**
     77    * Handle to the ongoing operation.
     78    */
     79   struct TALER_EXCHANGE_RecoupRefreshHandle *ph;
     80 
     81   /**
     82    * NULL if coin was not refreshed, otherwise reference
     83    * to the melt operation underlying @a coin_reference.
     84    */
     85   const char *melt_reference;
     86 
     87 };
     88 
     89 
     90 /**
     91  * Check the result of the recoup_refresh request: checks whether
     92  * the HTTP response code is good, and that the coin that
     93  * was paid back belonged to the right old coin.
     94  *
     95  * @param cls closure
     96  * @param rrr response details
     97  */
     98 static void
     99 recoup_refresh_cb (void *cls,
    100                    const struct TALER_EXCHANGE_RecoupRefreshResponse *rrr)
    101 {
    102   struct RecoupRefreshState *rrs = cls;
    103   const struct TALER_EXCHANGE_HttpResponse *hr = &rrr->hr;
    104   struct TALER_TESTING_Interpreter *is = rrs->is;
    105   char *cref;
    106   unsigned int idx;
    107 
    108   rrs->ph = NULL;
    109   if (rrs->expected_response_code != hr->http_status)
    110   {
    111     TALER_TESTING_unexpected_status (is,
    112                                      hr->http_status,
    113                                      rrs->expected_response_code);
    114     return;
    115   }
    116 
    117   if (GNUNET_OK !=
    118       TALER_TESTING_parse_coin_reference (
    119         rrs->coin_reference,
    120         &cref,
    121         &idx))
    122   {
    123     TALER_TESTING_interpreter_fail (is);
    124     return;
    125   }
    126   (void) idx; /* do NOT use! We ignore 'idx', must be 0 for melt! */
    127 
    128   GNUNET_free (cref);
    129   switch (hr->http_status)
    130   {
    131   case MHD_HTTP_OK:
    132     /* check old_coin_pub */
    133     {
    134       const struct TALER_TESTING_Command *melt_cmd;
    135       const struct TALER_CoinSpendPrivateKeyP *dirty_priv;
    136       struct TALER_CoinSpendPublicKeyP oc;
    137 
    138       melt_cmd = TALER_TESTING_interpreter_lookup_command (is,
    139                                                            rrs->melt_reference);
    140       if (NULL == melt_cmd)
    141       {
    142         GNUNET_break (0);
    143         TALER_TESTING_interpreter_fail (is);
    144         return;
    145       }
    146       if (GNUNET_OK !=
    147           TALER_TESTING_get_trait_coin_priv (melt_cmd,
    148                                              0,
    149                                              &dirty_priv))
    150       {
    151         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    152                     "Coin %u not found in command %s\n",
    153                     0,
    154                     rrs->melt_reference);
    155         GNUNET_break (0);
    156         TALER_TESTING_interpreter_fail (is);
    157         return;
    158       }
    159       GNUNET_CRYPTO_eddsa_key_get_public (&dirty_priv->eddsa_priv,
    160                                           &oc.eddsa_pub);
    161       if (0 != GNUNET_memcmp (&oc,
    162                               &rrr->details.ok.old_coin_pub))
    163       {
    164         GNUNET_break (0);
    165         TALER_TESTING_interpreter_fail (is);
    166         return;
    167       }
    168     }
    169     break;
    170   case MHD_HTTP_NOT_FOUND:
    171     break;
    172   case MHD_HTTP_CONFLICT:
    173     break;
    174   default:
    175     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    176                 "Unmanaged HTTP status code %u/%d.\n",
    177                 hr->http_status,
    178                 (int) hr->ec);
    179     break;
    180   }
    181   TALER_TESTING_interpreter_next (is);
    182 }
    183 
    184 
    185 /**
    186  * Run the command.
    187  *
    188  * @param cls closure.
    189  * @param cmd the command to execute.
    190  * @param is the interpreter state.
    191  */
    192 static void
    193 recoup_refresh_run (void *cls,
    194                     const struct TALER_TESTING_Command *cmd,
    195                     struct TALER_TESTING_Interpreter *is)
    196 {
    197   struct RecoupRefreshState *rrs = cls;
    198   const struct TALER_TESTING_Command *coin_cmd;
    199   const struct TALER_TESTING_Command *melt_cmd;
    200   const struct TALER_CoinSpendPrivateKeyP *coin_priv;
    201   const struct TALER_CoinSpendPrivateKeyP *coin_priv_old;
    202   const struct TALER_EXCHANGE_DenomPublicKey *denom_pub;
    203   const struct TALER_DenominationSignature *coin_sig;
    204   const struct TALER_RefreshMasterSecretP *rplanchet;
    205   const struct TALER_PlanchetMasterSecretP *planchet;
    206   const struct TALER_ExchangeBlindingValues *ewv;
    207   char *cref;
    208   unsigned int idx;
    209   struct TALER_DenominationHashP h_denom_pub;
    210 
    211   rrs->is = is;
    212   if (GNUNET_OK !=
    213       TALER_TESTING_parse_coin_reference (
    214         rrs->coin_reference,
    215         &cref,
    216         &idx))
    217   {
    218     TALER_TESTING_interpreter_fail (is);
    219     return;
    220   }
    221 
    222   coin_cmd = TALER_TESTING_interpreter_lookup_command (is,
    223                                                        cref);
    224   GNUNET_free (cref);
    225   if (NULL == coin_cmd)
    226   {
    227     GNUNET_break (0);
    228     TALER_TESTING_interpreter_fail (is);
    229     return;
    230   }
    231   melt_cmd = TALER_TESTING_interpreter_lookup_command (is,
    232                                                        rrs->melt_reference);
    233   if (NULL == melt_cmd)
    234   {
    235     GNUNET_break (0);
    236     TALER_TESTING_interpreter_fail (is);
    237     return;
    238   }
    239   if (GNUNET_OK !=
    240       TALER_TESTING_get_trait_coin_priv (coin_cmd,
    241                                          idx,
    242                                          &coin_priv))
    243   {
    244     GNUNET_break (0);
    245     TALER_TESTING_interpreter_fail (is);
    246     return;
    247   }
    248   if (GNUNET_OK !=
    249       TALER_TESTING_get_trait_coin_priv (melt_cmd,
    250                                          0,
    251                                          &coin_priv_old))
    252   {
    253     GNUNET_break (0);
    254     TALER_TESTING_interpreter_fail (is);
    255     return;
    256   }
    257   GNUNET_CRYPTO_eddsa_key_get_public (
    258     &coin_priv->eddsa_priv,
    259     &rrs->coin_pub_new.eddsa_pub);
    260   GNUNET_CRYPTO_eddsa_key_get_public (
    261     &coin_priv_old->eddsa_priv,
    262     &rrs->coin_pub_old.eddsa_pub);
    263 
    264   if (GNUNET_OK !=
    265       TALER_TESTING_get_trait_exchange_blinding_values (melt_cmd,
    266                                                         idx,
    267                                                         &ewv))
    268   {
    269     GNUNET_break (0);
    270     TALER_TESTING_interpreter_fail (is);
    271     return;
    272   }
    273   if (GNUNET_OK !=
    274       TALER_TESTING_get_trait_planchet_secrets (coin_cmd,
    275                                                 idx,
    276                                                 &planchet))
    277   {
    278     GNUNET_break (0);
    279     TALER_TESTING_interpreter_fail (is);
    280     return;
    281   }
    282   if (GNUNET_OK !=
    283       TALER_TESTING_get_trait_refresh_secret (melt_cmd,
    284                                               &rplanchet))
    285   {
    286     GNUNET_break (0);
    287     TALER_TESTING_interpreter_fail (is);
    288     return;
    289   }
    290   if (GNUNET_OK !=
    291       TALER_TESTING_get_trait_denom_pub (coin_cmd,
    292                                          idx,
    293                                          &denom_pub))
    294   {
    295     GNUNET_break (0);
    296     TALER_TESTING_interpreter_fail (is);
    297     return;
    298   }
    299   if (GNUNET_OK !=
    300       TALER_TESTING_get_trait_denom_sig (coin_cmd,
    301                                          idx,
    302                                          &coin_sig))
    303   {
    304     GNUNET_break (0);
    305     TALER_TESTING_interpreter_fail (is);
    306     return;
    307   }
    308   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    309               "Trying to recoup_refresh denomination '%s'\n",
    310               TALER_B2S (&denom_pub->h_key));
    311   rrs->che_old.type
    312     = TALER_EXCHANGE_CTT_OLD_COIN_RECOUP;
    313   rrs->che_old.amount
    314     = rrs->amount;
    315   rrs->che_old.details.old_coin_recoup.new_coin_pub
    316     = rrs->coin_pub_new;
    317   rrs->che_new.type
    318     = TALER_EXCHANGE_CTT_RECOUP_REFRESH;
    319   rrs->che_new.amount
    320     = rrs->amount;
    321   rrs->che_new.details.recoup_refresh.old_coin_pub
    322     = rrs->coin_pub_old;
    323   TALER_planchet_blinding_secret_create (
    324     planchet,
    325     ewv,
    326     &rrs->che_new.details.recoup_refresh.coin_bks);
    327   TALER_denom_pub_hash (&denom_pub->key,
    328                         &h_denom_pub);
    329   TALER_wallet_recoup_refresh_sign (
    330     &h_denom_pub,
    331     &rrs->che_new.details.recoup_refresh.coin_bks,
    332     coin_priv,
    333     &rrs->che_new.details.recoup_refresh.coin_sig);
    334   rrs->ph = TALER_EXCHANGE_recoup_refresh (
    335     TALER_TESTING_interpreter_get_context (is),
    336     TALER_TESTING_get_exchange_url (is),
    337     TALER_TESTING_get_keys (is),
    338     denom_pub,
    339     coin_sig,
    340     ewv,
    341     rplanchet,
    342     planchet,
    343     idx,
    344     &recoup_refresh_cb,
    345     rrs);
    346   GNUNET_assert (NULL != rrs->ph);
    347 }
    348 
    349 
    350 /**
    351  * Cleanup the "recoup_refresh" CMD state, and possibly cancel
    352  * a pending operation thereof.
    353  *
    354  * @param cls closure.
    355  * @param cmd the command which is being cleaned up.
    356  */
    357 static void
    358 recoup_refresh_cleanup (void *cls,
    359                         const struct TALER_TESTING_Command *cmd)
    360 {
    361   struct RecoupRefreshState *rrs = cls;
    362   if (NULL != rrs->ph)
    363   {
    364     TALER_EXCHANGE_recoup_refresh_cancel (rrs->ph);
    365     rrs->ph = NULL;
    366   }
    367   GNUNET_free (rrs);
    368 }
    369 
    370 
    371 /**
    372  * Offer internal data from a "recoup-refresh" CMD state to other
    373  * commands.
    374  *
    375  * @param cls closure
    376  * @param[out] ret result (could be anything)
    377  * @param trait name of the trait
    378  * @param index index number of the object to offer.
    379  * @return #GNUNET_OK on success
    380  */
    381 static enum GNUNET_GenericReturnValue
    382 recoup_refresh_traits (void *cls,
    383                        const void **ret,
    384                        const char *trait,
    385                        unsigned int index)
    386 {
    387   struct RecoupRefreshState *rrs = cls;
    388   struct TALER_TESTING_Trait traits[] = {
    389     TALER_TESTING_make_trait_coin_history (0,
    390                                            &rrs->che_old),
    391     TALER_TESTING_make_trait_coin_pub (0,
    392                                        &rrs->coin_pub_old),
    393     TALER_TESTING_make_trait_coin_history (1,
    394                                            &rrs->che_new),
    395     TALER_TESTING_make_trait_coin_pub (1,
    396                                        &rrs->coin_pub_new),
    397     TALER_TESTING_trait_end ()
    398   };
    399 
    400   return TALER_TESTING_get_trait (traits,
    401                                   ret,
    402                                   trait,
    403                                   index);
    404 }
    405 
    406 
    407 struct TALER_TESTING_Command
    408 TALER_TESTING_cmd_recoup_refresh (const char *label,
    409                                   unsigned int expected_response_code,
    410                                   const char *coin_reference,
    411                                   const char *melt_reference,
    412                                   const char *amount)
    413 {
    414   struct RecoupRefreshState *rrs;
    415 
    416   rrs = GNUNET_new (struct RecoupRefreshState);
    417   rrs->expected_response_code = expected_response_code;
    418   rrs->coin_reference = coin_reference;
    419   rrs->melt_reference = melt_reference;
    420   if (GNUNET_OK !=
    421       TALER_string_to_amount (amount,
    422                               &rrs->amount))
    423   {
    424     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    425                 "Failed to parse amount `%s' at %s\n",
    426                 amount,
    427                 label);
    428     GNUNET_assert (0);
    429   }
    430   {
    431     struct TALER_TESTING_Command cmd = {
    432       .cls = rrs,
    433       .label = label,
    434       .run = &recoup_refresh_run,
    435       .cleanup = &recoup_refresh_cleanup,
    436       .traits = &recoup_refresh_traits
    437     };
    438 
    439     return cmd;
    440   }
    441 }