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_reserve_purse.c (11032B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2022, 2024 Taler Systems SA
      4 
      5   TALER is free software; you can redistribute it and/or modify it
      6   under the terms of the GNU General Public License as published by
      7   the Free Software Foundation; either version 3, or (at your
      8   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 GNU
     13   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_reserve_purse.c
     21  * @brief command for testing /reserves/$PID/purse
     22  * @author Christian Grothoff
     23  */
     24 #include "taler/taler_json_lib.h"
     25 #include <gnunet/gnunet_curl_lib.h>
     26 struct ReservePurseState;
     27 #define TALER_EXCHANGE_POST_RESERVES_PURSE_RESULT_CLOSURE struct \
     28         ReservePurseState
     29 #include "taler/exchange/post-reserves-RESERVE_PUB-purse.h"
     30 #include "taler/taler_testing_lib.h"
     31 #include "taler/taler_signatures.h"
     32 
     33 
     34 /**
     35  * State for a "purse create with merge" CMD.
     36  */
     37 struct ReservePurseState
     38 {
     39 
     40   /**
     41    * Merge time (local time when the command was
     42    * executed).
     43    */
     44   struct GNUNET_TIME_Timestamp merge_timestamp;
     45 
     46   /**
     47    * Account (reserve) private key.
     48    */
     49   union TALER_AccountPrivateKeyP account_priv;
     50 
     51   /**
     52    * Account (reserve) public key.
     53    */
     54   union TALER_AccountPublicKeyP account_pub;
     55 
     56   /**
     57    * Reserve signature generated for the request
     58    * (client-side).
     59    */
     60   struct TALER_ReserveSignatureP reserve_sig;
     61 
     62   /**
     63    * Private key of the purse.
     64    */
     65   struct TALER_PurseContractPrivateKeyP purse_priv;
     66 
     67   /**
     68    * Public key of the purse.
     69    */
     70   struct TALER_PurseContractPublicKeyP purse_pub;
     71 
     72   /**
     73    * Private key with the merge capability.
     74    */
     75   struct TALER_PurseMergePrivateKeyP merge_priv;
     76 
     77   /**
     78    * Public key of the merge capability.
     79    */
     80   struct TALER_PurseMergePublicKeyP merge_pub;
     81 
     82   /**
     83    * Private key to decrypt the contract.
     84    */
     85   struct TALER_ContractDiffiePrivateP contract_priv;
     86 
     87   /**
     88    * Handle while operation is running.
     89    */
     90   struct TALER_EXCHANGE_PostReservesPurseHandle *dh;
     91 
     92   /**
     93    * When will the purse expire?
     94    */
     95   struct GNUNET_TIME_Relative expiration_rel;
     96 
     97   /**
     98    * When will the purse expire?
     99    */
    100   struct GNUNET_TIME_Timestamp purse_expiration;
    101 
    102   /**
    103    * Hash of the payto://-URI for the reserve we are
    104    * merging into.
    105    */
    106   struct TALER_NormalizedPaytoHashP h_payto;
    107 
    108   /**
    109    * Set to the KYC requirement row *if* the exchange replied with
    110    * a request for KYC.
    111    */
    112   uint64_t requirement_row;
    113 
    114   /**
    115    * Contract terms for the purse.
    116    */
    117   json_t *contract_terms;
    118 
    119   /**
    120    * Reference to the reserve, or NULL (!).
    121    */
    122   const char *reserve_ref;
    123 
    124   /**
    125    * Interpreter state.
    126    */
    127   struct TALER_TESTING_Interpreter *is;
    128 
    129   /**
    130    * Expected HTTP response code.
    131    */
    132   unsigned int expected_response_code;
    133 
    134   /**
    135    * True to pay the purse fee.
    136    */
    137   bool pay_purse_fee;
    138 };
    139 
    140 
    141 /**
    142  * Callback to analyze the /reserves/$PID/purse response, just used to check if
    143  * the response code is acceptable.
    144  *
    145  * @param ds closure.
    146  * @param dr purse response details
    147  */
    148 static void
    149 purse_cb (struct ReservePurseState *ds,
    150           const struct TALER_EXCHANGE_PostReservesPurseResponse *dr)
    151 {
    152   ds->dh = NULL;
    153   ds->reserve_sig = *dr->reserve_sig;
    154   if (ds->expected_response_code != dr->hr.http_status)
    155   {
    156     TALER_TESTING_unexpected_status (ds->is,
    157                                      dr->hr.http_status,
    158                                      ds->expected_response_code);
    159     return;
    160   }
    161   switch (dr->hr.http_status)
    162   {
    163   case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
    164     /* KYC required */
    165     ds->requirement_row =
    166       dr->details.unavailable_for_legal_reasons.requirement_row;
    167     GNUNET_break (0 ==
    168                   GNUNET_memcmp (
    169                     &ds->h_payto,
    170                     &dr->details.unavailable_for_legal_reasons.h_payto));
    171     break;
    172   }
    173   TALER_TESTING_interpreter_next (ds->is);
    174 }
    175 
    176 
    177 /**
    178  * Run the command.
    179  *
    180  * @param cls closure.
    181  * @param cmd the command to execute.
    182  * @param is the interpreter state.
    183  */
    184 static void
    185 purse_run (void *cls,
    186            const struct TALER_TESTING_Command *cmd,
    187            struct TALER_TESTING_Interpreter *is)
    188 {
    189   struct ReservePurseState *ds = cls;
    190   const struct TALER_ReservePrivateKeyP *reserve_priv;
    191   const struct TALER_TESTING_Command *ref;
    192 
    193   (void) cmd;
    194   ds->is = is;
    195   ref = TALER_TESTING_interpreter_lookup_command (ds->is,
    196                                                   ds->reserve_ref);
    197   GNUNET_assert (NULL != ref);
    198   if (GNUNET_OK !=
    199       TALER_TESTING_get_trait_reserve_priv (ref,
    200                                             &reserve_priv))
    201   {
    202     GNUNET_break (0);
    203     TALER_TESTING_interpreter_fail (ds->is);
    204     return;
    205   }
    206   ds->account_priv.reserve_priv = *reserve_priv;
    207   GNUNET_CRYPTO_eddsa_key_create (
    208     &ds->purse_priv.eddsa_priv);
    209   GNUNET_CRYPTO_eddsa_key_get_public (
    210     &ds->purse_priv.eddsa_priv,
    211     &ds->purse_pub.eddsa_pub);
    212   GNUNET_CRYPTO_eddsa_key_get_public (
    213     &ds->account_priv.reserve_priv.eddsa_priv,
    214     &ds->account_pub.reserve_pub.eddsa_pub);
    215   GNUNET_CRYPTO_eddsa_key_create (
    216     &ds->merge_priv.eddsa_priv);
    217   GNUNET_CRYPTO_eddsa_key_get_public (
    218     &ds->merge_priv.eddsa_priv,
    219     &ds->merge_pub.eddsa_pub);
    220   GNUNET_CRYPTO_ecdhe_key_create (
    221     &ds->contract_priv.ecdhe_priv);
    222   ds->purse_expiration
    223     = GNUNET_TIME_absolute_to_timestamp (
    224         GNUNET_TIME_relative_to_absolute (
    225           ds->expiration_rel));
    226 
    227   {
    228     struct TALER_NormalizedPayto payto_uri;
    229     const char *exchange_url;
    230     const struct TALER_TESTING_Command *exchange_cmd;
    231 
    232     exchange_cmd = TALER_TESTING_interpreter_get_command (is,
    233                                                           "exchange");
    234     if (NULL == exchange_cmd)
    235     {
    236       GNUNET_break (0);
    237       TALER_TESTING_interpreter_fail (is);
    238       return;
    239     }
    240     GNUNET_assert (
    241       GNUNET_OK ==
    242       TALER_TESTING_get_trait_exchange_url (
    243         exchange_cmd,
    244         &exchange_url));
    245     payto_uri
    246       = TALER_reserve_make_payto (
    247           exchange_url,
    248           &ds->account_pub.reserve_pub);
    249     TALER_normalized_payto_hash (payto_uri,
    250                                  &ds->h_payto);
    251     GNUNET_free (payto_uri.normalized_payto);
    252   }
    253 
    254   GNUNET_assert (0 ==
    255                  json_object_set_new (
    256                    ds->contract_terms,
    257                    "pay_deadline",
    258                    GNUNET_JSON_from_timestamp (ds->purse_expiration)));
    259   ds->merge_timestamp = GNUNET_TIME_timestamp_get ();
    260   ds->dh = TALER_EXCHANGE_post_reserves_purse_create (
    261     TALER_TESTING_interpreter_get_context (is),
    262     TALER_TESTING_get_exchange_url (is),
    263     TALER_TESTING_get_keys (is),
    264     &ds->account_priv.reserve_priv,
    265     &ds->purse_priv,
    266     &ds->merge_priv,
    267     &ds->contract_priv,
    268     ds->contract_terms,
    269     ds->pay_purse_fee,
    270     ds->merge_timestamp);
    271   if (NULL == ds->dh)
    272   {
    273     GNUNET_break (0);
    274     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    275                 "Could not purse reserve\n");
    276     TALER_TESTING_interpreter_fail (is);
    277     return;
    278   }
    279   GNUNET_assert (GNUNET_OK ==
    280                  TALER_EXCHANGE_post_reserves_purse_set_options (
    281                    ds->dh,
    282                    TALER_EXCHANGE_post_reserves_purse_option_upload_contract ())
    283                  );
    284   GNUNET_assert (TALER_EC_NONE ==
    285                  TALER_EXCHANGE_post_reserves_purse_start (ds->dh,
    286                                                            &purse_cb,
    287                                                            ds));
    288 }
    289 
    290 
    291 /**
    292  * Free the state of a "purse" CMD, and possibly cancel a
    293  * pending operation thereof.
    294  *
    295  * @param cls closure, must be a `struct ReservePurseState`.
    296  * @param cmd the command which is being cleaned up.
    297  */
    298 static void
    299 purse_cleanup (void *cls,
    300                const struct TALER_TESTING_Command *cmd)
    301 {
    302   struct ReservePurseState *ds = cls;
    303 
    304   if (NULL != ds->dh)
    305   {
    306     TALER_TESTING_command_incomplete (ds->is,
    307                                       cmd->label);
    308     TALER_EXCHANGE_post_reserves_purse_cancel (ds->dh);
    309     ds->dh = NULL;
    310   }
    311   json_decref (ds->contract_terms);
    312   GNUNET_free (ds);
    313 }
    314 
    315 
    316 /**
    317  * Offer internal data from a "purse" CMD, to other commands.
    318  *
    319  * @param cls closure.
    320  * @param[out] ret result.
    321  * @param trait name of the trait.
    322  * @param index index number of the object to offer.
    323  * @return #GNUNET_OK on success.
    324  */
    325 static enum GNUNET_GenericReturnValue
    326 purse_traits (void *cls,
    327               const void **ret,
    328               const char *trait,
    329               unsigned int index)
    330 {
    331   struct ReservePurseState *ds = cls;
    332   struct TALER_TESTING_Trait traits[] = {
    333     TALER_TESTING_make_trait_timestamp (
    334       0,
    335       &ds->merge_timestamp),
    336     TALER_TESTING_make_trait_contract_terms (
    337       ds->contract_terms),
    338     TALER_TESTING_make_trait_purse_priv (
    339       &ds->purse_priv),
    340     TALER_TESTING_make_trait_purse_pub (
    341       &ds->purse_pub),
    342     TALER_TESTING_make_trait_merge_priv (
    343       &ds->merge_priv),
    344     TALER_TESTING_make_trait_merge_pub (
    345       &ds->merge_pub),
    346     TALER_TESTING_make_trait_contract_priv (
    347       &ds->contract_priv),
    348     TALER_TESTING_make_trait_account_priv (
    349       &ds->account_priv),
    350     TALER_TESTING_make_trait_account_pub (
    351       &ds->account_pub),
    352     TALER_TESTING_make_trait_reserve_priv (
    353       &ds->account_priv.reserve_priv),
    354     TALER_TESTING_make_trait_reserve_pub (
    355       &ds->account_pub.reserve_pub),
    356     TALER_TESTING_make_trait_reserve_sig (
    357       &ds->reserve_sig),
    358     TALER_TESTING_make_trait_legi_requirement_row (
    359       &ds->requirement_row),
    360     TALER_TESTING_make_trait_h_normalized_payto (
    361       &ds->h_payto),
    362     TALER_TESTING_trait_end ()
    363   };
    364 
    365   return TALER_TESTING_get_trait (traits,
    366                                   ret,
    367                                   trait,
    368                                   index);
    369 }
    370 
    371 
    372 struct TALER_TESTING_Command
    373 TALER_TESTING_cmd_purse_create_with_reserve (
    374   const char *label,
    375   unsigned int expected_http_status,
    376   const char *contract_terms,
    377   bool upload_contract,
    378   bool pay_purse_fee,
    379   struct GNUNET_TIME_Relative expiration,
    380   const char *reserve_ref)
    381 {
    382   struct ReservePurseState *ds;
    383   json_error_t err;
    384 
    385   ds = GNUNET_new (struct ReservePurseState);
    386   ds->expiration_rel = expiration;
    387   ds->contract_terms = json_loads (contract_terms,
    388                                    0 /* flags */,
    389                                    &err);
    390   GNUNET_assert (NULL != ds->contract_terms);
    391   ds->pay_purse_fee = pay_purse_fee;
    392   ds->reserve_ref = reserve_ref;
    393   ds->expected_response_code = expected_http_status;
    394 
    395   {
    396     struct TALER_TESTING_Command cmd = {
    397       .cls = ds,
    398       .label = label,
    399       .run = &purse_run,
    400       .cleanup = &purse_cleanup,
    401       .traits = &purse_traits
    402     };
    403 
    404     return cmd;
    405   }
    406 }
    407 
    408 
    409 /* end of testing_api_cmd_reserve_purse.c */