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


      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/platform.h"
     25 #include "taler/taler_json_lib.h"
     26 #include <gnunet/gnunet_curl_lib.h>
     27 #include "taler/taler_testing_lib.h"
     28 #include "taler/taler_signatures.h"
     29 #include "taler/backoff.h"
     30 
     31 
     32 /**
     33  * State for a "purse create with merge" CMD.
     34  */
     35 struct ReservePurseState
     36 {
     37 
     38   /**
     39    * Merge time (local time when the command was
     40    * executed).
     41    */
     42   struct GNUNET_TIME_Timestamp merge_timestamp;
     43 
     44   /**
     45    * Account (reserve) private key.
     46    */
     47   union TALER_AccountPrivateKeyP account_priv;
     48 
     49   /**
     50    * Account (reserve) public key.
     51    */
     52   union TALER_AccountPublicKeyP account_pub;
     53 
     54   /**
     55    * Reserve signature generated for the request
     56    * (client-side).
     57    */
     58   struct TALER_ReserveSignatureP reserve_sig;
     59 
     60   /**
     61    * Private key of the purse.
     62    */
     63   struct TALER_PurseContractPrivateKeyP purse_priv;
     64 
     65   /**
     66    * Public key of the purse.
     67    */
     68   struct TALER_PurseContractPublicKeyP purse_pub;
     69 
     70   /**
     71    * Private key with the merge capability.
     72    */
     73   struct TALER_PurseMergePrivateKeyP merge_priv;
     74 
     75   /**
     76    * Public key of the merge capability.
     77    */
     78   struct TALER_PurseMergePublicKeyP merge_pub;
     79 
     80   /**
     81    * Private key to decrypt the contract.
     82    */
     83   struct TALER_ContractDiffiePrivateP contract_priv;
     84 
     85   /**
     86    * Handle while operation is running.
     87    */
     88   struct TALER_EXCHANGE_PurseCreateMergeHandle *dh;
     89 
     90   /**
     91    * When will the purse expire?
     92    */
     93   struct GNUNET_TIME_Relative expiration_rel;
     94 
     95   /**
     96    * When will the purse expire?
     97    */
     98   struct GNUNET_TIME_Timestamp purse_expiration;
     99 
    100   /**
    101    * Hash of the payto://-URI for the reserve we are
    102    * merging into.
    103    */
    104   struct TALER_NormalizedPaytoHashP h_payto;
    105 
    106   /**
    107    * Set to the KYC requirement row *if* the exchange replied with
    108    * a request for KYC.
    109    */
    110   uint64_t requirement_row;
    111 
    112   /**
    113    * Contract terms for the purse.
    114    */
    115   json_t *contract_terms;
    116 
    117   /**
    118    * Reference to the reserve, or NULL (!).
    119    */
    120   const char *reserve_ref;
    121 
    122   /**
    123    * Interpreter state.
    124    */
    125   struct TALER_TESTING_Interpreter *is;
    126 
    127   /**
    128    * Expected HTTP response code.
    129    */
    130   unsigned int expected_response_code;
    131 
    132   /**
    133    * True to pay the purse fee.
    134    */
    135   bool pay_purse_fee;
    136 };
    137 
    138 
    139 /**
    140  * Callback to analyze the /reserves/$PID/purse response, just used to check if
    141  * the response code is acceptable.
    142  *
    143  * @param cls closure.
    144  * @param dr purse response details
    145  */
    146 static void
    147 purse_cb (void *cls,
    148           const struct TALER_EXCHANGE_PurseCreateMergeResponse *dr)
    149 {
    150   struct ReservePurseState *ds = cls;
    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_purse_create_with_merge (
    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     true /* upload contract */,
    270     ds->pay_purse_fee,
    271     ds->merge_timestamp,
    272     &purse_cb,
    273     ds);
    274   if (NULL == ds->dh)
    275   {
    276     GNUNET_break (0);
    277     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    278                 "Could not purse reserve\n");
    279     TALER_TESTING_interpreter_fail (is);
    280     return;
    281   }
    282 }
    283 
    284 
    285 /**
    286  * Free the state of a "purse" CMD, and possibly cancel a
    287  * pending operation thereof.
    288  *
    289  * @param cls closure, must be a `struct ReservePurseState`.
    290  * @param cmd the command which is being cleaned up.
    291  */
    292 static void
    293 purse_cleanup (void *cls,
    294                const struct TALER_TESTING_Command *cmd)
    295 {
    296   struct ReservePurseState *ds = cls;
    297 
    298   if (NULL != ds->dh)
    299   {
    300     TALER_TESTING_command_incomplete (ds->is,
    301                                       cmd->label);
    302     TALER_EXCHANGE_purse_create_with_merge_cancel (ds->dh);
    303     ds->dh = NULL;
    304   }
    305   json_decref (ds->contract_terms);
    306   GNUNET_free (ds);
    307 }
    308 
    309 
    310 /**
    311  * Offer internal data from a "purse" CMD, to other commands.
    312  *
    313  * @param cls closure.
    314  * @param[out] ret result.
    315  * @param trait name of the trait.
    316  * @param index index number of the object to offer.
    317  * @return #GNUNET_OK on success.
    318  */
    319 static enum GNUNET_GenericReturnValue
    320 purse_traits (void *cls,
    321               const void **ret,
    322               const char *trait,
    323               unsigned int index)
    324 {
    325   struct ReservePurseState *ds = cls;
    326   struct TALER_TESTING_Trait traits[] = {
    327     TALER_TESTING_make_trait_timestamp (
    328       0,
    329       &ds->merge_timestamp),
    330     TALER_TESTING_make_trait_contract_terms (
    331       ds->contract_terms),
    332     TALER_TESTING_make_trait_purse_priv (
    333       &ds->purse_priv),
    334     TALER_TESTING_make_trait_purse_pub (
    335       &ds->purse_pub),
    336     TALER_TESTING_make_trait_merge_priv (
    337       &ds->merge_priv),
    338     TALER_TESTING_make_trait_merge_pub (
    339       &ds->merge_pub),
    340     TALER_TESTING_make_trait_contract_priv (
    341       &ds->contract_priv),
    342     TALER_TESTING_make_trait_account_priv (
    343       &ds->account_priv),
    344     TALER_TESTING_make_trait_account_pub (
    345       &ds->account_pub),
    346     TALER_TESTING_make_trait_reserve_priv (
    347       &ds->account_priv.reserve_priv),
    348     TALER_TESTING_make_trait_reserve_pub (
    349       &ds->account_pub.reserve_pub),
    350     TALER_TESTING_make_trait_reserve_sig (
    351       &ds->reserve_sig),
    352     TALER_TESTING_make_trait_legi_requirement_row (
    353       &ds->requirement_row),
    354     TALER_TESTING_make_trait_h_normalized_payto (
    355       &ds->h_payto),
    356     TALER_TESTING_trait_end ()
    357   };
    358 
    359   return TALER_TESTING_get_trait (traits,
    360                                   ret,
    361                                   trait,
    362                                   index);
    363 }
    364 
    365 
    366 struct TALER_TESTING_Command
    367 TALER_TESTING_cmd_purse_create_with_reserve (
    368   const char *label,
    369   unsigned int expected_http_status,
    370   const char *contract_terms,
    371   bool upload_contract,
    372   bool pay_purse_fee,
    373   struct GNUNET_TIME_Relative expiration,
    374   const char *reserve_ref)
    375 {
    376   struct ReservePurseState *ds;
    377   json_error_t err;
    378 
    379   ds = GNUNET_new (struct ReservePurseState);
    380   ds->expiration_rel = expiration;
    381   ds->contract_terms = json_loads (contract_terms,
    382                                    0 /* flags */,
    383                                    &err);
    384   GNUNET_assert (NULL != ds->contract_terms);
    385   ds->pay_purse_fee = pay_purse_fee;
    386   ds->reserve_ref = reserve_ref;
    387   ds->expected_response_code = expected_http_status;
    388 
    389   {
    390     struct TALER_TESTING_Command cmd = {
    391       .cls = ds,
    392       .label = label,
    393       .run = &purse_run,
    394       .cleanup = &purse_cleanup,
    395       .traits = &purse_traits
    396     };
    397 
    398     return cmd;
    399   }
    400 }
    401 
    402 
    403 /* end of testing_api_cmd_reserve_purse.c */