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-03-07 14:54:45 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/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           14 : purse_cb (void *cls,
     148              :           const struct TALER_EXCHANGE_PostReservesPurseResponse *dr)
     149              : {
     150           14 :   struct ReservePurseState *ds = cls;
     151              : 
     152           14 :   ds->dh = NULL;
     153           14 :   ds->reserve_sig = *dr->reserve_sig;
     154           14 :   if (ds->expected_response_code != dr->hr.http_status)
     155              :   {
     156            0 :     TALER_TESTING_unexpected_status (ds->is,
     157              :                                      dr->hr.http_status,
     158              :                                      ds->expected_response_code);
     159            0 :     return;
     160              :   }
     161           14 :   switch (dr->hr.http_status)
     162              :   {
     163            1 :   case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
     164              :     /* KYC required */
     165            1 :     ds->requirement_row =
     166            1 :       dr->details.unavailable_for_legal_reasons.requirement_row;
     167            1 :     GNUNET_break (0 ==
     168              :                   GNUNET_memcmp (
     169              :                     &ds->h_payto,
     170              :                     &dr->details.unavailable_for_legal_reasons.h_payto));
     171            1 :     break;
     172              :   }
     173           14 :   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           14 : purse_run (void *cls,
     186              :            const struct TALER_TESTING_Command *cmd,
     187              :            struct TALER_TESTING_Interpreter *is)
     188              : {
     189           14 :   struct ReservePurseState *ds = cls;
     190              :   const struct TALER_ReservePrivateKeyP *reserve_priv;
     191              :   const struct TALER_TESTING_Command *ref;
     192              : 
     193              :   (void) cmd;
     194           14 :   ds->is = is;
     195           14 :   ref = TALER_TESTING_interpreter_lookup_command (ds->is,
     196              :                                                   ds->reserve_ref);
     197           14 :   GNUNET_assert (NULL != ref);
     198           14 :   if (GNUNET_OK !=
     199           14 :       TALER_TESTING_get_trait_reserve_priv (ref,
     200              :                                             &reserve_priv))
     201              :   {
     202            0 :     GNUNET_break (0);
     203            0 :     TALER_TESTING_interpreter_fail (ds->is);
     204            0 :     return;
     205              :   }
     206           14 :   ds->account_priv.reserve_priv = *reserve_priv;
     207           14 :   GNUNET_CRYPTO_eddsa_key_create (
     208              :     &ds->purse_priv.eddsa_priv);
     209           14 :   GNUNET_CRYPTO_eddsa_key_get_public (
     210           14 :     &ds->purse_priv.eddsa_priv,
     211              :     &ds->purse_pub.eddsa_pub);
     212           14 :   GNUNET_CRYPTO_eddsa_key_get_public (
     213           14 :     &ds->account_priv.reserve_priv.eddsa_priv,
     214              :     &ds->account_pub.reserve_pub.eddsa_pub);
     215           14 :   GNUNET_CRYPTO_eddsa_key_create (
     216              :     &ds->merge_priv.eddsa_priv);
     217           14 :   GNUNET_CRYPTO_eddsa_key_get_public (
     218           14 :     &ds->merge_priv.eddsa_priv,
     219              :     &ds->merge_pub.eddsa_pub);
     220           14 :   GNUNET_CRYPTO_ecdhe_key_create (
     221              :     &ds->contract_priv.ecdhe_priv);
     222              :   ds->purse_expiration
     223           14 :     = 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           14 :     exchange_cmd = TALER_TESTING_interpreter_get_command (is,
     233              :                                                           "exchange");
     234           14 :     if (NULL == exchange_cmd)
     235              :     {
     236            0 :       GNUNET_break (0);
     237            0 :       TALER_TESTING_interpreter_fail (is);
     238            0 :       return;
     239              :     }
     240           14 :     GNUNET_assert (
     241              :       GNUNET_OK ==
     242              :       TALER_TESTING_get_trait_exchange_url (
     243              :         exchange_cmd,
     244              :         &exchange_url));
     245              :     payto_uri
     246           14 :       = TALER_reserve_make_payto (
     247              :           exchange_url,
     248           14 :           &ds->account_pub.reserve_pub);
     249           14 :     TALER_normalized_payto_hash (payto_uri,
     250              :                                  &ds->h_payto);
     251           14 :     GNUNET_free (payto_uri.normalized_payto);
     252              :   }
     253              : 
     254           14 :   GNUNET_assert (0 ==
     255              :                  json_object_set_new (
     256              :                    ds->contract_terms,
     257              :                    "pay_deadline",
     258              :                    GNUNET_JSON_from_timestamp (ds->purse_expiration)));
     259           14 :   ds->merge_timestamp = GNUNET_TIME_timestamp_get ();
     260           14 :   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           14 :     &ds->account_priv.reserve_priv,
     265           14 :     &ds->purse_priv,
     266           14 :     &ds->merge_priv,
     267           14 :     &ds->contract_priv,
     268           14 :     ds->contract_terms,
     269           14 :     ds->pay_purse_fee,
     270              :     ds->merge_timestamp);
     271           14 :   if (NULL == ds->dh)
     272              :   {
     273            0 :     GNUNET_break (0);
     274            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     275              :                 "Could not purse reserve\n");
     276            0 :     TALER_TESTING_interpreter_fail (is);
     277            0 :     return;
     278              :   }
     279           14 :   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           14 :   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           14 : purse_cleanup (void *cls,
     300              :                const struct TALER_TESTING_Command *cmd)
     301              : {
     302           14 :   struct ReservePurseState *ds = cls;
     303              : 
     304           14 :   if (NULL != ds->dh)
     305              :   {
     306            0 :     TALER_TESTING_command_incomplete (ds->is,
     307              :                                       cmd->label);
     308            0 :     TALER_EXCHANGE_post_reserves_purse_cancel (ds->dh);
     309            0 :     ds->dh = NULL;
     310              :   }
     311           14 :   json_decref (ds->contract_terms);
     312           14 :   GNUNET_free (ds);
     313           14 : }
     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           51 : purse_traits (void *cls,
     327              :               const void **ret,
     328              :               const char *trait,
     329              :               unsigned int index)
     330              : {
     331           51 :   struct ReservePurseState *ds = cls;
     332              :   struct TALER_TESTING_Trait traits[] = {
     333           51 :     TALER_TESTING_make_trait_timestamp (
     334              :       0,
     335           51 :       &ds->merge_timestamp),
     336           51 :     TALER_TESTING_make_trait_contract_terms (
     337           51 :       ds->contract_terms),
     338           51 :     TALER_TESTING_make_trait_purse_priv (
     339           51 :       &ds->purse_priv),
     340           51 :     TALER_TESTING_make_trait_purse_pub (
     341           51 :       &ds->purse_pub),
     342           51 :     TALER_TESTING_make_trait_merge_priv (
     343           51 :       &ds->merge_priv),
     344           51 :     TALER_TESTING_make_trait_merge_pub (
     345           51 :       &ds->merge_pub),
     346           51 :     TALER_TESTING_make_trait_contract_priv (
     347           51 :       &ds->contract_priv),
     348           51 :     TALER_TESTING_make_trait_account_priv (
     349           51 :       &ds->account_priv),
     350           51 :     TALER_TESTING_make_trait_account_pub (
     351           51 :       &ds->account_pub),
     352           51 :     TALER_TESTING_make_trait_reserve_priv (
     353           51 :       &ds->account_priv.reserve_priv),
     354           51 :     TALER_TESTING_make_trait_reserve_pub (
     355           51 :       &ds->account_pub.reserve_pub),
     356           51 :     TALER_TESTING_make_trait_reserve_sig (
     357           51 :       &ds->reserve_sig),
     358           51 :     TALER_TESTING_make_trait_legi_requirement_row (
     359           51 :       &ds->requirement_row),
     360           51 :     TALER_TESTING_make_trait_h_normalized_payto (
     361           51 :       &ds->h_payto),
     362           51 :     TALER_TESTING_trait_end ()
     363              :   };
     364              : 
     365           51 :   return TALER_TESTING_get_trait (traits,
     366              :                                   ret,
     367              :                                   trait,
     368              :                                   index);
     369              : }
     370              : 
     371              : 
     372              : struct TALER_TESTING_Command
     373           14 : 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           14 :   ds = GNUNET_new (struct ReservePurseState);
     386           14 :   ds->expiration_rel = expiration;
     387           14 :   ds->contract_terms = json_loads (contract_terms,
     388              :                                    0 /* flags */,
     389              :                                    &err);
     390           14 :   GNUNET_assert (NULL != ds->contract_terms);
     391           14 :   ds->pay_purse_fee = pay_purse_fee;
     392           14 :   ds->reserve_ref = reserve_ref;
     393           14 :   ds->expected_response_code = expected_http_status;
     394              : 
     395              :   {
     396           14 :     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           14 :     return cmd;
     405              :   }
     406              : }
     407              : 
     408              : 
     409              : /* end of testing_api_cmd_reserve_purse.c */
        

Generated by: LCOV version 2.0-1