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_purse_create_deposit.c (13098B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2022 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_purse_create_deposit.c
     21  * @brief command for testing /purses/$PID/create
     22  * @author Christian Grothoff
     23  */
     24 #include "taler/taler_json_lib.h"
     25 #include <gnunet/gnunet_curl_lib.h>
     26 #include "taler/taler_testing_lib.h"
     27 #include "taler/taler_signatures.h"
     28 
     29 /**
     30  * Information we keep per deposited coin.
     31  */
     32 struct Coin
     33 {
     34   /**
     35    * Reference to the respective command.
     36    */
     37   char *command_ref;
     38 
     39   /**
     40    * index of the specific coin in the traits of @e command_ref.
     41    */
     42   unsigned int coin_index;
     43 
     44   /**
     45    * Public key of the deposited coin.
     46    */
     47   struct TALER_CoinSpendPublicKeyP coin_pub;
     48 
     49   /**
     50    * Amount to deposit (with fee).
     51    */
     52   struct TALER_Amount deposit_with_fee;
     53 
     54   /**
     55    * Entry in the coin's history generated by this operation.
     56    */
     57   struct TALER_EXCHANGE_CoinHistoryEntry che;
     58 
     59 };
     60 
     61 
     62 /**
     63  * State for a "purse create deposit" CMD.
     64  */
     65 struct PurseCreateDepositState
     66 {
     67 
     68   /**
     69    * Total purse target amount without fees.
     70    */
     71   struct TALER_Amount target_amount;
     72 
     73   /**
     74    * Reference to any command that is able to provide a coin.
     75    */
     76   struct Coin *coin_references;
     77 
     78   /**
     79    * JSON string describing what a proposal is about.
     80    */
     81   json_t *contract_terms;
     82 
     83   /**
     84    * Purse expiration time.
     85    */
     86   struct GNUNET_TIME_Timestamp purse_expiration;
     87 
     88   /**
     89    * Relative purse expiration time.
     90    */
     91   struct GNUNET_TIME_Relative rel_expiration;
     92 
     93   /**
     94    * Set (by the interpreter) to a fresh private key.  This
     95    * key will be used to create the purse.
     96    */
     97   struct TALER_PurseContractPrivateKeyP purse_priv;
     98 
     99   /**
    100    * Set (by the interpreter) to a fresh private key.  This
    101    * key will be used to merge the purse.
    102    */
    103   struct TALER_PurseMergePrivateKeyP merge_priv;
    104 
    105   /**
    106    * Set (by the interpreter) to a fresh private key.  This
    107    * key will be used to decrypt the contract.
    108    */
    109   struct TALER_ContractDiffiePrivateP contract_priv;
    110 
    111   /**
    112    * Signing key used by the exchange to sign the
    113    * deposit confirmation.
    114    */
    115   struct TALER_ExchangePublicKeyP exchange_pub;
    116 
    117   /**
    118    * Signature from the exchange on the
    119    * deposit confirmation.
    120    */
    121   struct TALER_ExchangeSignatureP exchange_sig;
    122 
    123   /**
    124    * Set (by the interpreter) to a public key corresponding
    125    * to @e purse_priv.
    126    */
    127   struct TALER_PurseContractPublicKeyP purse_pub;
    128 
    129   /**
    130    * PurseCreateDeposit handle while operation is running.
    131    */
    132   struct TALER_EXCHANGE_PostPursesCreateHandle *dh;
    133 
    134   /**
    135    * Interpreter state.
    136    */
    137   struct TALER_TESTING_Interpreter *is;
    138 
    139   /**
    140    * Expected HTTP response code.
    141    */
    142   unsigned int expected_response_code;
    143 
    144   /**
    145    * Length of the @e coin_references array.
    146    */
    147   unsigned int num_coin_references;
    148 
    149   /**
    150    * Should we upload the contract?
    151    */
    152   bool upload_contract;
    153 
    154 };
    155 
    156 
    157 /**
    158  * Callback to analyze the /purses/$PID/create response, just used to check if
    159  * the response code is acceptable.
    160  *
    161  * @param cls closure.
    162  * @param dr deposit response details
    163  */
    164 static void
    165 deposit_cb (void *cls,
    166             const struct TALER_EXCHANGE_PostPursesCreateResponse *dr)
    167 {
    168   struct PurseCreateDepositState *ds = cls;
    169 
    170   ds->dh = NULL;
    171   if (ds->expected_response_code != dr->hr.http_status)
    172   {
    173     TALER_TESTING_unexpected_status (ds->is,
    174                                      dr->hr.http_status,
    175                                      ds->expected_response_code);
    176     return;
    177   }
    178   if (MHD_HTTP_OK == dr->hr.http_status)
    179   {
    180     ds->exchange_pub = dr->details.ok.exchange_pub;
    181     ds->exchange_sig = dr->details.ok.exchange_sig;
    182   }
    183   TALER_TESTING_interpreter_next (ds->is);
    184 }
    185 
    186 
    187 /**
    188  * Run the command.
    189  *
    190  * @param cls closure.
    191  * @param cmd the command to execute.
    192  * @param is the interpreter state.
    193  */
    194 static void
    195 deposit_run (void *cls,
    196              const struct TALER_TESTING_Command *cmd,
    197              struct TALER_TESTING_Interpreter *is)
    198 {
    199   struct PurseCreateDepositState *ds = cls;
    200   struct TALER_EXCHANGE_PurseDeposit deposits[ds->num_coin_references];
    201 
    202   (void) cmd;
    203   ds->is = is;
    204   GNUNET_CRYPTO_eddsa_key_create (&ds->purse_priv.eddsa_priv);
    205   GNUNET_CRYPTO_eddsa_key_create (&ds->merge_priv.eddsa_priv);
    206   GNUNET_CRYPTO_ecdhe_key_create (&ds->contract_priv.ecdhe_priv);
    207   GNUNET_CRYPTO_eddsa_key_get_public (&ds->purse_priv.eddsa_priv,
    208                                       &ds->purse_pub.eddsa_pub);
    209 
    210   for (unsigned int i = 0; i<ds->num_coin_references; i++)
    211   {
    212     struct Coin *cr = &ds->coin_references[i];
    213     struct TALER_EXCHANGE_PurseDeposit *pd = &deposits[i];
    214     const struct TALER_TESTING_Command *coin_cmd;
    215     const struct TALER_CoinSpendPrivateKeyP *coin_priv;
    216     const struct TALER_AgeCommitmentProof *age_commitment_proof = NULL;
    217     const struct TALER_EXCHANGE_DenomPublicKey *denom_pub;
    218     const struct TALER_DenominationSignature *denom_pub_sig;
    219 
    220     coin_cmd = TALER_TESTING_interpreter_lookup_command (is,
    221                                                          cr->command_ref);
    222     if (NULL == coin_cmd)
    223     {
    224       GNUNET_break (0);
    225       TALER_TESTING_interpreter_fail (is);
    226       return;
    227     }
    228 
    229     if ( (GNUNET_OK !=
    230           TALER_TESTING_get_trait_coin_priv (coin_cmd,
    231                                              cr->coin_index,
    232                                              &coin_priv)) ||
    233          (GNUNET_OK !=
    234           TALER_TESTING_get_trait_age_commitment_proof (coin_cmd,
    235                                                         cr->coin_index,
    236                                                         &age_commitment_proof))
    237          ||
    238          (GNUNET_OK !=
    239           TALER_TESTING_get_trait_denom_pub (coin_cmd,
    240                                              cr->coin_index,
    241                                              &denom_pub)) ||
    242          (GNUNET_OK !=
    243           TALER_TESTING_get_trait_denom_sig (coin_cmd,
    244                                              cr->coin_index,
    245                                              &denom_pub_sig)) )
    246     {
    247       GNUNET_break (0);
    248       TALER_TESTING_interpreter_fail (is);
    249       return;
    250     }
    251     pd->age_commitment_proof = age_commitment_proof;
    252     pd->denom_sig = *denom_pub_sig;
    253     pd->coin_priv = *coin_priv;
    254     pd->amount = cr->deposit_with_fee;
    255     pd->h_denom_pub = denom_pub->h_key;
    256     GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv,
    257                                         &cr->coin_pub.eddsa_pub);
    258     cr->che.type = TALER_EXCHANGE_CTT_PURSE_DEPOSIT;
    259     cr->che.amount = cr->deposit_with_fee;
    260     GNUNET_CRYPTO_eddsa_key_get_public (
    261       &ds->purse_priv.eddsa_priv,
    262       &cr->che.details.purse_deposit.purse_pub.eddsa_pub);
    263     cr->che.details.purse_deposit.exchange_base_url
    264       = TALER_TESTING_get_exchange_url (is);
    265     TALER_age_commitment_hash (
    266       &age_commitment_proof->commitment,
    267       &cr->che.details.purse_deposit.phac);
    268   }
    269 
    270   ds->purse_expiration =
    271     GNUNET_TIME_absolute_to_timestamp (
    272       GNUNET_TIME_relative_to_absolute (ds->rel_expiration));
    273   GNUNET_assert (0 ==
    274                  json_object_set_new (
    275                    ds->contract_terms,
    276                    "pay_deadline",
    277                    GNUNET_JSON_from_timestamp (ds->purse_expiration)));
    278   ds->dh = TALER_EXCHANGE_post_purses_create_create (
    279     TALER_TESTING_interpreter_get_context (is),
    280     TALER_TESTING_get_exchange_url (is),
    281     TALER_TESTING_get_keys (is),
    282     &ds->purse_priv,
    283     &ds->merge_priv,
    284     &ds->contract_priv,
    285     ds->contract_terms,
    286     ds->num_coin_references,
    287     deposits);
    288   GNUNET_assert (NULL != ds->dh);
    289   if (ds->upload_contract)
    290     TALER_EXCHANGE_post_purses_create_set_options (
    291       ds->dh,
    292       TALER_EXCHANGE_post_purses_create_option_upload_contract ());
    293   GNUNET_assert (TALER_EC_NONE ==
    294                  TALER_EXCHANGE_post_purses_create_start (ds->dh,
    295                                                           &deposit_cb,
    296                                                           ds));
    297 }
    298 
    299 
    300 /**
    301  * Free the state of a "deposit" CMD, and possibly cancel a
    302  * pending operation thereof.
    303  *
    304  * @param cls closure, must be a `struct PurseCreateDepositState`.
    305  * @param cmd the command which is being cleaned up.
    306  */
    307 static void
    308 deposit_cleanup (void *cls,
    309                  const struct TALER_TESTING_Command *cmd)
    310 {
    311   struct PurseCreateDepositState *ds = cls;
    312 
    313   if (NULL != ds->dh)
    314   {
    315     TALER_TESTING_command_incomplete (ds->is,
    316                                       cmd->label);
    317     TALER_EXCHANGE_post_purses_create_cancel (ds->dh);
    318     ds->dh = NULL;
    319   }
    320   for (unsigned int i = 0; i<ds->num_coin_references; i++)
    321     GNUNET_free (ds->coin_references[i].command_ref);
    322   json_decref (ds->contract_terms);
    323   GNUNET_free (ds->coin_references);
    324   GNUNET_free (ds);
    325 }
    326 
    327 
    328 /**
    329  * Offer internal data from a "deposit" CMD, to other commands.
    330  *
    331  * @param cls closure.
    332  * @param[out] ret result.
    333  * @param trait name of the trait.
    334  * @param index index number of the object to offer.
    335  * @return #GNUNET_OK on success.
    336  */
    337 static enum GNUNET_GenericReturnValue
    338 deposit_traits (void *cls,
    339                 const void **ret,
    340                 const char *trait,
    341                 unsigned int index)
    342 {
    343   struct PurseCreateDepositState *ds = cls;
    344   if (index >= ds->num_coin_references)
    345     return GNUNET_NO;
    346 
    347   {
    348     const struct Coin *co = &ds->coin_references[index];
    349     struct TALER_TESTING_Trait traits[] = {
    350       TALER_TESTING_make_trait_merge_priv (&ds->merge_priv),
    351       TALER_TESTING_make_trait_contract_priv (&ds->contract_priv),
    352       TALER_TESTING_make_trait_coin_history (index,
    353                                              &co->che),
    354       TALER_TESTING_make_trait_coin_pub (index,
    355                                          &co->coin_pub),
    356       TALER_TESTING_make_trait_purse_priv (&ds->purse_priv),
    357       TALER_TESTING_make_trait_purse_pub (&ds->purse_pub),
    358       TALER_TESTING_make_trait_contract_terms (ds->contract_terms),
    359       TALER_TESTING_make_trait_deposit_amount (0,
    360                                                &ds->target_amount),
    361       TALER_TESTING_make_trait_timestamp (index,
    362                                           &ds->purse_expiration),
    363       TALER_TESTING_trait_end ()
    364     };
    365 
    366     return TALER_TESTING_get_trait (traits,
    367                                     ret,
    368                                     trait,
    369                                     index);
    370   }
    371 }
    372 
    373 
    374 struct TALER_TESTING_Command
    375 TALER_TESTING_cmd_purse_create_with_deposit (
    376   const char *label,
    377   unsigned int expected_http_status,
    378   const char *contract_terms,
    379   bool upload_contract,
    380   struct GNUNET_TIME_Relative purse_expiration,
    381   ...)
    382 {
    383   struct PurseCreateDepositState *ds;
    384 
    385   ds = GNUNET_new (struct PurseCreateDepositState);
    386   ds->rel_expiration = purse_expiration;
    387   ds->upload_contract = upload_contract;
    388   ds->expected_response_code = expected_http_status;
    389   ds->contract_terms = json_loads (contract_terms,
    390                                    JSON_REJECT_DUPLICATES,
    391                                    NULL);
    392   if (NULL == ds->contract_terms)
    393   {
    394     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    395                 "Failed to parse contract terms `%s' for CMD `%s'\n",
    396                 contract_terms,
    397                 label);
    398     GNUNET_assert (0);
    399   }
    400   {
    401     va_list ap;
    402     unsigned int i;
    403     const char *ref;
    404     const char *val;
    405 
    406     va_start (ap, purse_expiration);
    407     while (NULL != (va_arg (ap, const char *)))
    408       ds->num_coin_references++;
    409     va_end (ap);
    410     GNUNET_assert (0 == (ds->num_coin_references % 2));
    411     ds->num_coin_references /= 2;
    412     ds->coin_references = GNUNET_new_array (ds->num_coin_references,
    413                                             struct Coin);
    414     i = 0;
    415     va_start (ap, purse_expiration);
    416     while (NULL != (ref = va_arg (ap, const char *)))
    417     {
    418       struct Coin *c = &ds->coin_references[i++];
    419 
    420       GNUNET_assert (NULL != (val = va_arg (ap, const char *)));
    421       GNUNET_assert (GNUNET_OK ==
    422                      TALER_TESTING_parse_coin_reference (
    423                        ref,
    424                        &c->command_ref,
    425                        &c->coin_index));
    426       GNUNET_assert (GNUNET_OK ==
    427                      TALER_string_to_amount (val,
    428                                              &c->deposit_with_fee));
    429     }
    430     va_end (ap);
    431   }
    432   {
    433     struct TALER_TESTING_Command cmd = {
    434       .cls = ds,
    435       .label = label,
    436       .run = &deposit_run,
    437       .cleanup = &deposit_cleanup,
    438       .traits = &deposit_traits
    439     };
    440 
    441     return cmd;
    442   }
    443 }
    444 
    445 
    446 /* end of testing_api_cmd_purse_create_deposit.c */