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


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