LCOV - code coverage report
Current view: top level - testing - testing_api_cmd_purse_deposit.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 81.5 % 146 119
Test Date: 2025-12-26 23:00:34 Functions: 100.0 % 5 5

            Line data    Source code
       1              : /*
       2              :   This file is part of TALER
       3              :   Copyright (C) 2022, 2023 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_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              :    * Entry in the coin's history generated by this operation.
      43              :    */
      44              :   struct TALER_EXCHANGE_CoinHistoryEntry che;
      45              : 
      46              :   /**
      47              :    * Public key of the deposited coin.
      48              :    */
      49              :   struct TALER_CoinSpendPublicKeyP coin_pub;
      50              : 
      51              :   /**
      52              :    * index of the specific coin in the traits of @e command_ref.
      53              :    */
      54              :   unsigned int coin_index;
      55              : 
      56              :   /**
      57              :    * Amount to deposit (with fee).
      58              :    */
      59              :   struct TALER_Amount deposit_with_fee;
      60              : 
      61              : };
      62              : 
      63              : 
      64              : /**
      65              :  * State for a "purse deposit" CMD.
      66              :  */
      67              : struct PurseDepositState
      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              :    * The purse's public key.
      82              :    */
      83              :   struct TALER_PurseContractPublicKeyP purse_pub;
      84              : 
      85              :   /**
      86              :    * The reserve we are being deposited into.
      87              :    * Set as a trait once we know the reserve.
      88              :    */
      89              :   struct TALER_ReservePublicKeyP reserve_pub;
      90              : 
      91              :   /**
      92              :    * PurseDeposit handle while operation is running.
      93              :    */
      94              :   struct TALER_EXCHANGE_PurseDepositHandle *dh;
      95              : 
      96              :   /**
      97              :    * Interpreter state.
      98              :    */
      99              :   struct TALER_TESTING_Interpreter *is;
     100              : 
     101              :   /**
     102              :    * Reference to the command that established the purse.
     103              :    */
     104              :   const char *purse_ref;
     105              : 
     106              :   /**
     107              :    * Reserve history entry that corresponds to this operation.
     108              :    * Will be of type #TALER_EXCHANGE_RTT_MERGE.
     109              :    * Only valid if @e purse_complete is true.
     110              :    */
     111              :   struct TALER_EXCHANGE_ReserveHistoryEntry reserve_history;
     112              :   /**
     113              :    * Expected HTTP response code.
     114              :    */
     115              :   unsigned int expected_response_code;
     116              : 
     117              :   /**
     118              :    * Length of the @e coin_references array.
     119              :    */
     120              :   unsigned int num_coin_references;
     121              : 
     122              :   /**
     123              :    * Minimum age to apply to all deposits.
     124              :    */
     125              :   uint8_t min_age;
     126              : 
     127              :   /**
     128              :    * Set to true if this deposit filled the purse.
     129              :    */
     130              :   bool purse_complete;
     131              : };
     132              : 
     133              : 
     134              : /**
     135              :  * Callback to analyze the /purses/$PID/deposit response, just used to check if
     136              :  * the response code is acceptable.
     137              :  *
     138              :  * @param cls closure.
     139              :  * @param dr deposit response details
     140              :  */
     141              : static void
     142            7 : deposit_cb (void *cls,
     143              :             const struct TALER_EXCHANGE_PurseDepositResponse *dr)
     144              : {
     145            7 :   struct PurseDepositState *ds = cls;
     146              : 
     147            7 :   ds->dh = NULL;
     148            7 :   if (ds->expected_response_code != dr->hr.http_status)
     149              :   {
     150            0 :     TALER_TESTING_unexpected_status (ds->is,
     151              :                                      dr->hr.http_status,
     152              :                                      ds->expected_response_code);
     153            0 :     return;
     154              :   }
     155            7 :   if (MHD_HTTP_OK == dr->hr.http_status)
     156              :   {
     157            5 :     if (-1 !=
     158            5 :         TALER_amount_cmp (&dr->details.ok.total_deposited,
     159              :                           &dr->details.ok.purse_value_after_fees))
     160              :     {
     161              :       const struct TALER_TESTING_Command *purse_cmd;
     162              :       const struct TALER_ReserveSignatureP *reserve_sig;
     163              :       const struct TALER_ReservePublicKeyP *reserve_pub;
     164              :       const struct GNUNET_TIME_Timestamp *merge_timestamp;
     165              :       const struct TALER_PurseMergePublicKeyP *merge_pub;
     166              : 
     167            5 :       purse_cmd = TALER_TESTING_interpreter_lookup_command (ds->is,
     168              :                                                             ds->purse_ref);
     169            5 :       GNUNET_assert (NULL != purse_cmd);
     170            5 :       if (GNUNET_OK !=
     171            5 :           TALER_TESTING_get_trait_reserve_sig (purse_cmd,
     172              :                                                &reserve_sig))
     173              :       {
     174            0 :         GNUNET_break (0);
     175            0 :         TALER_TESTING_interpreter_fail (ds->is);
     176            0 :         return;
     177              :       }
     178            5 :       if (GNUNET_OK !=
     179            5 :           TALER_TESTING_get_trait_reserve_pub (purse_cmd,
     180              :                                                &reserve_pub))
     181              :       {
     182            0 :         GNUNET_break (0);
     183            0 :         TALER_TESTING_interpreter_fail (ds->is);
     184            0 :         return;
     185              :       }
     186            5 :       if (GNUNET_OK !=
     187            5 :           TALER_TESTING_get_trait_merge_pub (purse_cmd,
     188              :                                              &merge_pub))
     189              :       {
     190            0 :         GNUNET_break (0);
     191            0 :         TALER_TESTING_interpreter_fail (ds->is);
     192            0 :         return;
     193              :       }
     194            5 :       ds->reserve_pub = *reserve_pub;
     195            5 :       if (GNUNET_OK !=
     196            5 :           TALER_TESTING_get_trait_timestamp (purse_cmd,
     197              :                                              0,
     198              :                                              &merge_timestamp))
     199              :       {
     200            0 :         GNUNET_break (0);
     201            0 :         TALER_TESTING_interpreter_fail (ds->is);
     202            0 :         return;
     203              :       }
     204              : 
     205              :       /* Deposits complete, create trait! */
     206            5 :       ds->reserve_history.type = TALER_EXCHANGE_RTT_MERGE;
     207              :       {
     208              :         struct TALER_EXCHANGE_Keys *keys;
     209              :         const struct TALER_EXCHANGE_GlobalFee *gf;
     210              : 
     211            5 :         keys = TALER_TESTING_get_keys (ds->is);
     212            5 :         GNUNET_assert (NULL != keys);
     213            5 :         gf = TALER_EXCHANGE_get_global_fee (keys,
     214              :                                             *merge_timestamp);
     215            5 :         GNUNET_assert (NULL != gf);
     216              : 
     217              :         /* Note: change when flags below changes! */
     218              :         ds->reserve_history.amount
     219            5 :           = dr->details.ok.purse_value_after_fees;
     220              :         if (true)
     221              :         {
     222            5 :           ds->reserve_history.details.merge_details.purse_fee = gf->fees.purse;
     223              :         }
     224              :         else
     225              :         {
     226              :           TALER_amount_set_zero (
     227              :             ds->reserve_history.amount.currency,
     228              :             &ds->reserve_history.details.merge_details.purse_fee);
     229              :         }
     230              :       }
     231              :       ds->reserve_history.details.merge_details.h_contract_terms
     232            5 :         = dr->details.ok.h_contract_terms;
     233              :       ds->reserve_history.details.merge_details.merge_pub
     234            5 :         = *merge_pub;
     235              :       ds->reserve_history.details.merge_details.purse_pub
     236            5 :         = ds->purse_pub;
     237              :       ds->reserve_history.details.merge_details.reserve_sig
     238            5 :         = *reserve_sig;
     239              :       ds->reserve_history.details.merge_details.merge_timestamp
     240            5 :         = *merge_timestamp;
     241              :       ds->reserve_history.details.merge_details.purse_expiration
     242            5 :         = dr->details.ok.purse_expiration;
     243              :       ds->reserve_history.details.merge_details.min_age
     244            5 :         = ds->min_age;
     245              :       ds->reserve_history.details.merge_details.flags
     246            5 :         = TALER_WAMF_MODE_CREATE_WITH_PURSE_FEE;
     247            5 :       ds->purse_complete = true;
     248              :     }
     249              :   }
     250            7 :   TALER_TESTING_interpreter_next (ds->is);
     251              : }
     252              : 
     253              : 
     254              : /**
     255              :  * Run the command.
     256              :  *
     257              :  * @param cls closure.
     258              :  * @param cmd the command to execute.
     259              :  * @param is the interpreter state.
     260              :  */
     261              : static void
     262            7 : deposit_run (void *cls,
     263              :              const struct TALER_TESTING_Command *cmd,
     264              :              struct TALER_TESTING_Interpreter *is)
     265            7 : {
     266            7 :   struct PurseDepositState *ds = cls;
     267            7 :   struct TALER_EXCHANGE_PurseDeposit deposits[ds->num_coin_references];
     268              :   const struct TALER_PurseContractPublicKeyP *purse_pub;
     269              :   const struct TALER_TESTING_Command *purse_cmd;
     270              : 
     271              :   (void) cmd;
     272            7 :   ds->is = is;
     273            7 :   purse_cmd = TALER_TESTING_interpreter_lookup_command (is,
     274              :                                                         ds->purse_ref);
     275            7 :   GNUNET_assert (NULL != purse_cmd);
     276            7 :   if (GNUNET_OK !=
     277            7 :       TALER_TESTING_get_trait_purse_pub (purse_cmd,
     278              :                                          &purse_pub))
     279              :   {
     280            0 :     GNUNET_break (0);
     281            0 :     TALER_TESTING_interpreter_fail (is);
     282            0 :     return;
     283              :   }
     284            7 :   ds->purse_pub = *purse_pub;
     285           14 :   for (unsigned int i = 0; i<ds->num_coin_references; i++)
     286              :   {
     287            7 :     struct Coin *cr = &ds->coin_references[i];
     288            7 :     struct TALER_EXCHANGE_PurseDeposit *pd = &deposits[i];
     289              :     const struct TALER_TESTING_Command *coin_cmd;
     290              :     const struct TALER_CoinSpendPrivateKeyP *coin_priv;
     291            7 :     const struct TALER_AgeCommitmentProof *age_commitment_proof = NULL;
     292              :     const struct TALER_EXCHANGE_DenomPublicKey *denom_pub;
     293              :     const struct TALER_DenominationSignature *denom_pub_sig;
     294              : 
     295            7 :     coin_cmd = TALER_TESTING_interpreter_lookup_command (is,
     296            7 :                                                          cr->command_ref);
     297            7 :     GNUNET_assert (NULL != coin_cmd);
     298            7 :     if ( (GNUNET_OK !=
     299            7 :           TALER_TESTING_get_trait_coin_priv (coin_cmd,
     300              :                                              cr->coin_index,
     301            7 :                                              &coin_priv)) ||
     302              :          (GNUNET_OK !=
     303            7 :           TALER_TESTING_get_trait_age_commitment_proof (coin_cmd,
     304              :                                                         cr->coin_index,
     305              :                                                         &age_commitment_proof))
     306            7 :          ||
     307              :          (GNUNET_OK !=
     308            7 :           TALER_TESTING_get_trait_denom_pub (coin_cmd,
     309              :                                              cr->coin_index,
     310            7 :                                              &denom_pub)) ||
     311              :          (GNUNET_OK !=
     312            7 :           TALER_TESTING_get_trait_denom_sig (coin_cmd,
     313              :                                              cr->coin_index,
     314              :                                              &denom_pub_sig)) )
     315              :     {
     316            0 :       GNUNET_break (0);
     317            0 :       TALER_TESTING_interpreter_fail (is);
     318            0 :       return;
     319              :     }
     320            7 :     GNUNET_CRYPTO_eddsa_key_get_public (&coin_priv->eddsa_priv,
     321              :                                         &cr->coin_pub.eddsa_pub);
     322            7 :     cr->che.type = TALER_EXCHANGE_CTT_PURSE_DEPOSIT;
     323            7 :     cr->che.amount = cr->deposit_with_fee;
     324            7 :     cr->che.details.purse_deposit.purse_pub = *purse_pub;
     325              :     cr->che.details.purse_deposit.exchange_base_url
     326            7 :       = TALER_TESTING_get_exchange_url (is);
     327            7 :     TALER_age_commitment_hash (
     328            7 :       &age_commitment_proof->commitment,
     329              :       &cr->che.details.purse_deposit.phac);
     330            7 :     pd->age_commitment_proof = age_commitment_proof;
     331            7 :     pd->denom_sig = *denom_pub_sig;
     332            7 :     pd->coin_priv = *coin_priv;
     333            7 :     pd->amount = cr->deposit_with_fee;
     334            7 :     pd->h_denom_pub = denom_pub->h_key;
     335              :   }
     336              : 
     337            7 :   ds->dh = TALER_EXCHANGE_purse_deposit (
     338              :     TALER_TESTING_interpreter_get_context (is),
     339              :     TALER_TESTING_get_exchange_url (is),
     340              :     TALER_TESTING_get_keys (is),
     341              :     NULL, /* FIXME #7271: WADs support: purse exchange URL */
     342            7 :     &ds->purse_pub,
     343            7 :     ds->min_age,
     344              :     ds->num_coin_references,
     345              :     deposits,
     346              :     &deposit_cb,
     347              :     ds);
     348            7 :   if (NULL == ds->dh)
     349              :   {
     350            0 :     GNUNET_break (0);
     351            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     352              :                 "Could not deposit into purse\n");
     353            0 :     TALER_TESTING_interpreter_fail (is);
     354            0 :     return;
     355              :   }
     356              : }
     357              : 
     358              : 
     359              : /**
     360              :  * Free the state of a "deposit" CMD, and possibly cancel a
     361              :  * pending operation thereof.
     362              :  *
     363              :  * @param cls closure, must be a `struct PurseDepositState`.
     364              :  * @param cmd the command which is being cleaned up.
     365              :  */
     366              : static void
     367            7 : deposit_cleanup (void *cls,
     368              :                  const struct TALER_TESTING_Command *cmd)
     369              : {
     370            7 :   struct PurseDepositState *ds = cls;
     371              : 
     372            7 :   if (NULL != ds->dh)
     373              :   {
     374            0 :     TALER_TESTING_command_incomplete (ds->is,
     375              :                                       cmd->label);
     376            0 :     TALER_EXCHANGE_purse_deposit_cancel (ds->dh);
     377            0 :     ds->dh = NULL;
     378              :   }
     379           14 :   for (unsigned int i = 0; i<ds->num_coin_references; i++)
     380            7 :     GNUNET_free (ds->coin_references[i].command_ref);
     381            7 :   GNUNET_free (ds->coin_references);
     382            7 :   GNUNET_free (ds);
     383            7 : }
     384              : 
     385              : 
     386              : /**
     387              :  * Offer internal data from a "deposit" CMD, to other commands.
     388              :  *
     389              :  * @param cls closure.
     390              :  * @param[out] ret result.
     391              :  * @param trait name of the trait.
     392              :  * @param index index number of the object to offer.
     393              :  * @return #GNUNET_OK on success.
     394              :  */
     395              : static enum GNUNET_GenericReturnValue
     396            9 : deposit_traits (void *cls,
     397              :                 const void **ret,
     398              :                 const char *trait,
     399              :                 unsigned int index)
     400              : {
     401            9 :   struct PurseDepositState *ds = cls;
     402              : 
     403            9 :   if (index >= ds->num_coin_references)
     404            1 :     return GNUNET_NO;
     405              :   {
     406            8 :     const struct Coin *co = &ds->coin_references[index];
     407              :     struct TALER_TESTING_Trait traits[] = {
     408              :       /* history entry MUST be first due to response code logic below! */
     409            8 :       TALER_TESTING_make_trait_reserve_history (0,
     410            8 :                                                 &ds->reserve_history),
     411            8 :       TALER_TESTING_make_trait_coin_history (index,
     412              :                                              &co->che),
     413            8 :       TALER_TESTING_make_trait_coin_pub (index,
     414              :                                          &co->coin_pub),
     415            8 :       TALER_TESTING_make_trait_reserve_pub (&ds->reserve_pub),
     416            8 :       TALER_TESTING_make_trait_purse_pub (&ds->purse_pub),
     417            8 :       TALER_TESTING_trait_end ()
     418              :     };
     419              : 
     420            8 :     return TALER_TESTING_get_trait (ds->purse_complete
     421              :                                     ? &traits[0]   /* we have reserve history */
     422              :                                     : &traits[1],  /* skip reserve history */
     423              :                                     ret,
     424              :                                     trait,
     425              :                                     index);
     426              :   }
     427              : }
     428              : 
     429              : 
     430              : struct TALER_TESTING_Command
     431            7 : TALER_TESTING_cmd_purse_deposit_coins (
     432              :   const char *label,
     433              :   unsigned int expected_http_status,
     434              :   uint8_t min_age,
     435              :   const char *purse_ref,
     436              :   ...)
     437              : {
     438              :   struct PurseDepositState *ds;
     439              : 
     440            7 :   ds = GNUNET_new (struct PurseDepositState);
     441            7 :   ds->expected_response_code = expected_http_status;
     442            7 :   ds->min_age = min_age;
     443            7 :   ds->purse_ref = purse_ref;
     444              :   {
     445              :     va_list ap;
     446              :     unsigned int i;
     447              :     const char *ref;
     448              :     const char *val;
     449              : 
     450            7 :     va_start (ap, purse_ref);
     451           21 :     while (NULL != (va_arg (ap, const char *)))
     452           14 :       ds->num_coin_references++;
     453            7 :     va_end (ap);
     454            7 :     GNUNET_assert (0 == (ds->num_coin_references % 2));
     455            7 :     ds->num_coin_references /= 2;
     456            7 :     ds->coin_references = GNUNET_new_array (ds->num_coin_references,
     457              :                                             struct Coin);
     458            7 :     i = 0;
     459            7 :     va_start (ap, purse_ref);
     460           14 :     while (NULL != (ref = va_arg (ap, const char *)))
     461              :     {
     462            7 :       struct Coin *c = &ds->coin_references[i++];
     463              : 
     464            7 :       GNUNET_assert (NULL != (val = va_arg (ap,
     465              :                                             const char *)));
     466            7 :       GNUNET_assert (GNUNET_OK ==
     467              :                      TALER_TESTING_parse_coin_reference (
     468              :                        ref,
     469              :                        &c->command_ref,
     470              :                        &c->coin_index));
     471            7 :       GNUNET_assert (GNUNET_OK ==
     472              :                      TALER_string_to_amount (val,
     473              :                                              &c->deposit_with_fee));
     474              :     }
     475            7 :     va_end (ap);
     476              :   }
     477              :   {
     478            7 :     struct TALER_TESTING_Command cmd = {
     479              :       .cls = ds,
     480              :       .label = label,
     481              :       .run = &deposit_run,
     482              :       .cleanup = &deposit_cleanup,
     483              :       .traits = &deposit_traits
     484              :     };
     485              : 
     486            7 :     return cmd;
     487              :   }
     488              : }
     489              : 
     490              : 
     491              : /* end of testing_api_cmd_purse_deposit.c */
        

Generated by: LCOV version 2.0-1