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


      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_PostReservesPurseHandle *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_PostReservesPurseResponse *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_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 */