LCOV - code coverage report
Current view: top level - testing - testing_api_cmd_reserve_purse.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 86.6 % 112 97
Test Date: 2026-04-14 15:39:31 Functions: 100.0 % 5 5

            Line data    Source code
       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/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              : /**
      31              :  * State for a "purse create with merge" CMD.
      32              :  */
      33              : struct ReservePurseState
      34              : {
      35              : 
      36              :   /**
      37              :    * Merge time (local time when the command was
      38              :    * executed).
      39              :    */
      40              :   struct GNUNET_TIME_Timestamp merge_timestamp;
      41              : 
      42              :   /**
      43              :    * Account (reserve) private key.
      44              :    */
      45              :   union TALER_AccountPrivateKeyP account_priv;
      46              : 
      47              :   /**
      48              :    * Account (reserve) public key.
      49              :    */
      50              :   union TALER_AccountPublicKeyP account_pub;
      51              : 
      52              :   /**
      53              :    * Reserve signature generated for the request
      54              :    * (client-side).
      55              :    */
      56              :   struct TALER_ReserveSignatureP reserve_sig;
      57              : 
      58              :   /**
      59              :    * Private key of the purse.
      60              :    */
      61              :   struct TALER_PurseContractPrivateKeyP purse_priv;
      62              : 
      63              :   /**
      64              :    * Public key of the purse.
      65              :    */
      66              :   struct TALER_PurseContractPublicKeyP purse_pub;
      67              : 
      68              :   /**
      69              :    * Private key with the merge capability.
      70              :    */
      71              :   struct TALER_PurseMergePrivateKeyP merge_priv;
      72              : 
      73              :   /**
      74              :    * Public key of the merge capability.
      75              :    */
      76              :   struct TALER_PurseMergePublicKeyP merge_pub;
      77              : 
      78              :   /**
      79              :    * Private key to decrypt the contract.
      80              :    */
      81              :   struct TALER_ContractDiffiePrivateP contract_priv;
      82              : 
      83              :   /**
      84              :    * Handle while operation is running.
      85              :    */
      86              :   struct TALER_EXCHANGE_PostReservesPurseHandle *dh;
      87              : 
      88              :   /**
      89              :    * When will the purse expire?
      90              :    */
      91              :   struct GNUNET_TIME_Relative expiration_rel;
      92              : 
      93              :   /**
      94              :    * When will the purse expire?
      95              :    */
      96              :   struct GNUNET_TIME_Timestamp purse_expiration;
      97              : 
      98              :   /**
      99              :    * Hash of the payto://-URI for the reserve we are
     100              :    * merging into.
     101              :    */
     102              :   struct TALER_NormalizedPaytoHashP h_payto;
     103              : 
     104              :   /**
     105              :    * Set to the KYC requirement row *if* the exchange replied with
     106              :    * a request for KYC.
     107              :    */
     108              :   uint64_t requirement_row;
     109              : 
     110              :   /**
     111              :    * Contract terms for the purse.
     112              :    */
     113              :   json_t *contract_terms;
     114              : 
     115              :   /**
     116              :    * Reference to the reserve, or NULL (!).
     117              :    */
     118              :   const char *reserve_ref;
     119              : 
     120              :   /**
     121              :    * Interpreter state.
     122              :    */
     123              :   struct TALER_TESTING_Interpreter *is;
     124              : 
     125              :   /**
     126              :    * Expected HTTP response code.
     127              :    */
     128              :   unsigned int expected_response_code;
     129              : 
     130              :   /**
     131              :    * True to pay the purse fee.
     132              :    */
     133              :   bool pay_purse_fee;
     134              : };
     135              : 
     136              : 
     137              : /**
     138              :  * Callback to analyze the /reserves/$PID/purse response, just used to check if
     139              :  * the response code is acceptable.
     140              :  *
     141              :  * @param cls closure.
     142              :  * @param dr purse response details
     143              :  */
     144              : static void
     145           14 : purse_cb (void *cls,
     146              :           const struct TALER_EXCHANGE_PostReservesPurseResponse *dr)
     147              : {
     148           14 :   struct ReservePurseState *ds = cls;
     149              : 
     150           14 :   ds->dh = NULL;
     151           14 :   ds->reserve_sig = *dr->reserve_sig;
     152           14 :   if (ds->expected_response_code != dr->hr.http_status)
     153              :   {
     154            0 :     TALER_TESTING_unexpected_status (ds->is,
     155              :                                      dr->hr.http_status,
     156              :                                      ds->expected_response_code);
     157            0 :     return;
     158              :   }
     159           14 :   switch (dr->hr.http_status)
     160              :   {
     161            1 :   case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
     162              :     /* KYC required */
     163            1 :     ds->requirement_row =
     164            1 :       dr->details.unavailable_for_legal_reasons.requirement_row;
     165            1 :     GNUNET_break (0 ==
     166              :                   GNUNET_memcmp (
     167              :                     &ds->h_payto,
     168              :                     &dr->details.unavailable_for_legal_reasons.h_payto));
     169            1 :     break;
     170              :   }
     171           14 :   TALER_TESTING_interpreter_next (ds->is);
     172              : }
     173              : 
     174              : 
     175              : /**
     176              :  * Run the command.
     177              :  *
     178              :  * @param cls closure.
     179              :  * @param cmd the command to execute.
     180              :  * @param is the interpreter state.
     181              :  */
     182              : static void
     183           14 : purse_run (void *cls,
     184              :            const struct TALER_TESTING_Command *cmd,
     185              :            struct TALER_TESTING_Interpreter *is)
     186              : {
     187           14 :   struct ReservePurseState *ds = cls;
     188              :   const struct TALER_ReservePrivateKeyP *reserve_priv;
     189              :   const struct TALER_TESTING_Command *ref;
     190              : 
     191              :   (void) cmd;
     192           14 :   ds->is = is;
     193           14 :   ref = TALER_TESTING_interpreter_lookup_command (ds->is,
     194              :                                                   ds->reserve_ref);
     195           14 :   GNUNET_assert (NULL != ref);
     196           14 :   if (GNUNET_OK !=
     197           14 :       TALER_TESTING_get_trait_reserve_priv (ref,
     198              :                                             &reserve_priv))
     199              :   {
     200            0 :     GNUNET_break (0);
     201            0 :     TALER_TESTING_interpreter_fail (ds->is);
     202            0 :     return;
     203              :   }
     204           14 :   ds->account_priv.reserve_priv = *reserve_priv;
     205           14 :   GNUNET_CRYPTO_eddsa_key_create (
     206              :     &ds->purse_priv.eddsa_priv);
     207           14 :   GNUNET_CRYPTO_eddsa_key_get_public (
     208           14 :     &ds->purse_priv.eddsa_priv,
     209              :     &ds->purse_pub.eddsa_pub);
     210           14 :   GNUNET_CRYPTO_eddsa_key_get_public (
     211           14 :     &ds->account_priv.reserve_priv.eddsa_priv,
     212              :     &ds->account_pub.reserve_pub.eddsa_pub);
     213           14 :   GNUNET_CRYPTO_eddsa_key_create (
     214              :     &ds->merge_priv.eddsa_priv);
     215           14 :   GNUNET_CRYPTO_eddsa_key_get_public (
     216           14 :     &ds->merge_priv.eddsa_priv,
     217              :     &ds->merge_pub.eddsa_pub);
     218           14 :   GNUNET_CRYPTO_ecdhe_key_create (
     219              :     &ds->contract_priv.ecdhe_priv);
     220              :   ds->purse_expiration
     221           14 :     = GNUNET_TIME_absolute_to_timestamp (
     222              :         GNUNET_TIME_relative_to_absolute (
     223              :           ds->expiration_rel));
     224              : 
     225              :   {
     226              :     struct TALER_NormalizedPayto payto_uri;
     227              :     const char *exchange_url;
     228              :     const struct TALER_TESTING_Command *exchange_cmd;
     229              : 
     230           14 :     exchange_cmd = TALER_TESTING_interpreter_get_command (is,
     231              :                                                           "exchange");
     232           14 :     if (NULL == exchange_cmd)
     233              :     {
     234            0 :       GNUNET_break (0);
     235            0 :       TALER_TESTING_interpreter_fail (is);
     236            0 :       return;
     237              :     }
     238           14 :     GNUNET_assert (
     239              :       GNUNET_OK ==
     240              :       TALER_TESTING_get_trait_exchange_url (
     241              :         exchange_cmd,
     242              :         &exchange_url));
     243              :     payto_uri
     244           14 :       = TALER_reserve_make_payto (
     245              :           exchange_url,
     246           14 :           &ds->account_pub.reserve_pub);
     247           14 :     TALER_normalized_payto_hash (payto_uri,
     248              :                                  &ds->h_payto);
     249           14 :     GNUNET_free (payto_uri.normalized_payto);
     250              :   }
     251              : 
     252           14 :   GNUNET_assert (0 ==
     253              :                  json_object_set_new (
     254              :                    ds->contract_terms,
     255              :                    "pay_deadline",
     256              :                    GNUNET_JSON_from_timestamp (ds->purse_expiration)));
     257           14 :   ds->merge_timestamp = GNUNET_TIME_timestamp_get ();
     258           14 :   ds->dh = TALER_EXCHANGE_post_reserves_purse_create (
     259              :     TALER_TESTING_interpreter_get_context (is),
     260              :     TALER_TESTING_get_exchange_url (is),
     261              :     TALER_TESTING_get_keys (is),
     262           14 :     &ds->account_priv.reserve_priv,
     263           14 :     &ds->purse_priv,
     264           14 :     &ds->merge_priv,
     265           14 :     &ds->contract_priv,
     266           14 :     ds->contract_terms,
     267           14 :     ds->pay_purse_fee,
     268              :     ds->merge_timestamp);
     269           14 :   if (NULL == ds->dh)
     270              :   {
     271            0 :     GNUNET_break (0);
     272            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     273              :                 "Could not purse reserve\n");
     274            0 :     TALER_TESTING_interpreter_fail (is);
     275            0 :     return;
     276              :   }
     277           14 :   GNUNET_assert (GNUNET_OK ==
     278              :                  TALER_EXCHANGE_post_reserves_purse_set_options (
     279              :                    ds->dh,
     280              :                    TALER_EXCHANGE_post_reserves_purse_option_upload_contract ())
     281              :                  );
     282           14 :   GNUNET_assert (TALER_EC_NONE ==
     283              :                  TALER_EXCHANGE_post_reserves_purse_start (ds->dh,
     284              :                                                            &purse_cb,
     285              :                                                            ds));
     286              : }
     287              : 
     288              : 
     289              : /**
     290              :  * Free the state of a "purse" CMD, and possibly cancel a
     291              :  * pending operation thereof.
     292              :  *
     293              :  * @param cls closure, must be a `struct ReservePurseState`.
     294              :  * @param cmd the command which is being cleaned up.
     295              :  */
     296              : static void
     297           14 : purse_cleanup (void *cls,
     298              :                const struct TALER_TESTING_Command *cmd)
     299              : {
     300           14 :   struct ReservePurseState *ds = cls;
     301              : 
     302           14 :   if (NULL != ds->dh)
     303              :   {
     304            0 :     TALER_TESTING_command_incomplete (ds->is,
     305              :                                       cmd->label);
     306            0 :     TALER_EXCHANGE_post_reserves_purse_cancel (ds->dh);
     307            0 :     ds->dh = NULL;
     308              :   }
     309           14 :   json_decref (ds->contract_terms);
     310           14 :   GNUNET_free (ds);
     311           14 : }
     312              : 
     313              : 
     314              : /**
     315              :  * Offer internal data from a "purse" CMD, to other commands.
     316              :  *
     317              :  * @param cls closure.
     318              :  * @param[out] ret result.
     319              :  * @param trait name of the trait.
     320              :  * @param index index number of the object to offer.
     321              :  * @return #GNUNET_OK on success.
     322              :  */
     323              : static enum GNUNET_GenericReturnValue
     324           51 : purse_traits (void *cls,
     325              :               const void **ret,
     326              :               const char *trait,
     327              :               unsigned int index)
     328              : {
     329           51 :   struct ReservePurseState *ds = cls;
     330              :   struct TALER_TESTING_Trait traits[] = {
     331           51 :     TALER_TESTING_make_trait_timestamp (
     332              :       0,
     333           51 :       &ds->merge_timestamp),
     334           51 :     TALER_TESTING_make_trait_contract_terms (
     335           51 :       ds->contract_terms),
     336           51 :     TALER_TESTING_make_trait_purse_priv (
     337           51 :       &ds->purse_priv),
     338           51 :     TALER_TESTING_make_trait_purse_pub (
     339           51 :       &ds->purse_pub),
     340           51 :     TALER_TESTING_make_trait_merge_priv (
     341           51 :       &ds->merge_priv),
     342           51 :     TALER_TESTING_make_trait_merge_pub (
     343           51 :       &ds->merge_pub),
     344           51 :     TALER_TESTING_make_trait_contract_priv (
     345           51 :       &ds->contract_priv),
     346           51 :     TALER_TESTING_make_trait_account_priv (
     347           51 :       &ds->account_priv),
     348           51 :     TALER_TESTING_make_trait_account_pub (
     349           51 :       &ds->account_pub),
     350           51 :     TALER_TESTING_make_trait_reserve_priv (
     351           51 :       &ds->account_priv.reserve_priv),
     352           51 :     TALER_TESTING_make_trait_reserve_pub (
     353           51 :       &ds->account_pub.reserve_pub),
     354           51 :     TALER_TESTING_make_trait_reserve_sig (
     355           51 :       &ds->reserve_sig),
     356           51 :     TALER_TESTING_make_trait_legi_requirement_row (
     357           51 :       &ds->requirement_row),
     358           51 :     TALER_TESTING_make_trait_h_normalized_payto (
     359           51 :       &ds->h_payto),
     360           51 :     TALER_TESTING_trait_end ()
     361              :   };
     362              : 
     363           51 :   return TALER_TESTING_get_trait (traits,
     364              :                                   ret,
     365              :                                   trait,
     366              :                                   index);
     367              : }
     368              : 
     369              : 
     370              : struct TALER_TESTING_Command
     371           14 : TALER_TESTING_cmd_purse_create_with_reserve (
     372              :   const char *label,
     373              :   unsigned int expected_http_status,
     374              :   const char *contract_terms,
     375              :   bool upload_contract,
     376              :   bool pay_purse_fee,
     377              :   struct GNUNET_TIME_Relative expiration,
     378              :   const char *reserve_ref)
     379              : {
     380              :   struct ReservePurseState *ds;
     381              :   json_error_t err;
     382              : 
     383           14 :   ds = GNUNET_new (struct ReservePurseState);
     384           14 :   ds->expiration_rel = expiration;
     385           14 :   ds->contract_terms = json_loads (contract_terms,
     386              :                                    0 /* flags */,
     387              :                                    &err);
     388           14 :   GNUNET_assert (NULL != ds->contract_terms);
     389           14 :   ds->pay_purse_fee = pay_purse_fee;
     390           14 :   ds->reserve_ref = reserve_ref;
     391           14 :   ds->expected_response_code = expected_http_status;
     392              : 
     393              :   {
     394           14 :     struct TALER_TESTING_Command cmd = {
     395              :       .cls = ds,
     396              :       .label = label,
     397              :       .run = &purse_run,
     398              :       .cleanup = &purse_cleanup,
     399              :       .traits = &purse_traits
     400              :     };
     401              : 
     402           14 :     return cmd;
     403              :   }
     404              : }
     405              : 
     406              : 
     407              : /* end of testing_api_cmd_reserve_purse.c */
        

Generated by: LCOV version 2.0-1