LCOV - code coverage report
Current view: top level - testing - testing_api_cmd_age_withdraw.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 82.5 % 240 198
Test Date: 2026-03-07 14:54:45 Functions: 100.0 % 10 10

            Line data    Source code
       1              : /*
       2              :   This file is part of TALER
       3              :   Copyright (C) 2023-2025 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_age_withdraw.c
      21              :  * @brief implements the withdraw command for age-restricted coins
      22              :  * @author Özgür Kesim
      23              :  */
      24              : 
      25              : #include "taler/platform.h"
      26              : #include "taler/taler_exchange_service.h"
      27              : #include "taler/taler_json_lib.h"
      28              : #include <gnunet/gnunet_common.h>
      29              : #include <microhttpd.h>
      30              : #include <gnunet/gnunet_curl_lib.h>
      31              : #include "taler/taler_signatures.h"
      32              : #include "taler/taler_extensions.h"
      33              : #include "taler/taler_testing_lib.h"
      34              : 
      35              : /*
      36              :  * The output state of coin
      37              :  */
      38              : struct CoinOutputState
      39              : {
      40              : 
      41              :   /**
      42              :    * The calculated details during "withdraw", for the selected coin.
      43              :    */
      44              :   struct TALER_EXCHANGE_WithdrawCoinPrivateDetails details;
      45              : 
      46              :   /**
      47              :    * The (wanted) value of the coin, MUST be the same as input.denom_pub.value;
      48              :    */
      49              :   struct TALER_Amount amount;
      50              : 
      51              : };
      52              : 
      53              : /**
      54              :  * State for a "age withdraw" CMD:
      55              :  */
      56              : 
      57              : struct AgeWithdrawState
      58              : {
      59              : 
      60              :   /**
      61              :    * Interpreter state (during command)
      62              :    */
      63              :   struct TALER_TESTING_Interpreter *is;
      64              : 
      65              :   /**
      66              :    * The age-withdraw handle
      67              :    */
      68              :   struct TALER_EXCHANGE_PostWithdrawHandle *handle;
      69              : 
      70              :   /**
      71              :    * Exchange base URL.  Only used as offered trait.
      72              :    */
      73              :   char *exchange_url;
      74              : 
      75              :   /**
      76              :    * URI of the reserve we are withdrawing from.
      77              :    */
      78              :   struct TALER_NormalizedPayto reserve_payto_uri;
      79              : 
      80              :   /**
      81              :    * Private key of the reserve we are withdrawing from.
      82              :    */
      83              :   struct TALER_ReservePrivateKeyP reserve_priv;
      84              : 
      85              :   /**
      86              :    * Public key of the reserve we are withdrawing from.
      87              :    */
      88              :   struct TALER_ReservePublicKeyP reserve_pub;
      89              : 
      90              :   /**
      91              :    * Which reserve should we withdraw from?
      92              :    */
      93              :   const char *reserve_reference;
      94              : 
      95              :   /**
      96              :    * Expected HTTP response code to the request.
      97              :    */
      98              :   unsigned int expected_response_code;
      99              : 
     100              :   /**
     101              :    * Age mask
     102              :    */
     103              :   struct TALER_AgeMask mask;
     104              : 
     105              :   /**
     106              :    * The maximum age we commit to
     107              :    */
     108              :   uint8_t max_age;
     109              : 
     110              :   /**
     111              :    * Number of coins to withdraw
     112              :    */
     113              :   size_t num_coins;
     114              : 
     115              :   /**
     116              :    * The @e num_coins denomination public keys that are provided
     117              :    * to the `TALER_EXCHANGE_post_withdraw()` API.
     118              :    */
     119              :   struct TALER_EXCHANGE_DenomPublicKey *denoms_pub;
     120              : 
     121              :   /**
     122              :    * The master seed from which all the other seeds are derived from
     123              :    */
     124              :   struct TALER_WithdrawMasterSeedP seed;
     125              : 
     126              :   /**
     127              :    * The #TALER_CNC_KAPPA seeds derived from @e seed
     128              :    */
     129              :   struct TALER_KappaWithdrawMasterSeedP kappa_seed;
     130              : 
     131              :   /**
     132              :    * The master seed from which all the other seeds are derived from
     133              :    */
     134              :   struct TALER_BlindingMasterSeedP blinding_seed;
     135              : 
     136              :   /**
     137              :    * The output state of @e num_coins coins, calculated during the
     138              :    * "age-withdraw" operation.
     139              :    */
     140              :   struct CoinOutputState *coin_outputs;
     141              : 
     142              :   /**
     143              :    * The index returned by the exchange for the "age-withdraw" operation,
     144              :    * of the kappa coin candidates that we do not disclose and keep.
     145              :    */
     146              :   uint8_t noreveal_index;
     147              : 
     148              :   /**
     149              :    * The hash of the commitment, needed for the reveal step.
     150              :    */
     151              :   struct TALER_HashBlindedPlanchetsP planchets_h;
     152              : 
     153              :   /**
     154              :      *  The hash of the selected blinded planchets
     155              :      */
     156              :   struct TALER_HashBlindedPlanchetsP selected_h;
     157              : 
     158              :   /**
     159              :    * Set to the KYC requirement payto hash *if* the exchange replied with a
     160              :    * request for KYC.
     161              :    */
     162              :   struct TALER_NormalizedPaytoHashP h_payto;
     163              : 
     164              :   /**
     165              :    * Set to the KYC requirement row *if* the exchange replied with
     166              :    * a request for KYC.
     167              :    */
     168              :   uint64_t requirement_row;
     169              : 
     170              :   /**
     171              :    * Reserve history entry that corresponds to this withdraw.
     172              :    * Will be of type #TALER_EXCHANGE_RTT_WITHDRAWAL.
     173              :    */
     174              :   struct TALER_EXCHANGE_ReserveHistoryEntry reserve_history;
     175              : };
     176              : 
     177              : /**
     178              :  * Callback for the "age-withdraw" operation;  It checks that the response
     179              :  * code is expected and store the exchange signature in the state.
     180              :  *
     181              :  * @param cls Closure of type `struct AgeWithdrawState *`
     182              :  * @param response Response details
     183              :  */
     184              : static void
     185            5 : age_withdraw_cb (
     186              :   void *cls,
     187              :   const struct TALER_EXCHANGE_PostWithdrawResponse *response)
     188              : {
     189            5 :   struct AgeWithdrawState *aws = cls;
     190            5 :   struct TALER_TESTING_Interpreter *is = aws->is;
     191              : 
     192            5 :   aws->handle = NULL;
     193            5 :   if (aws->expected_response_code != response->hr.http_status)
     194              :   {
     195            0 :     TALER_TESTING_unexpected_status_with_body (is,
     196              :                                                response->hr.http_status,
     197              :                                                aws->expected_response_code,
     198              :                                                response->hr.reply);
     199            0 :     return;
     200              :   }
     201              : 
     202            5 :   switch (response->hr.http_status)
     203              :   {
     204            3 :   case MHD_HTTP_CREATED:
     205            3 :     aws->noreveal_index = response->details.created.noreveal_index;
     206            3 :     aws->planchets_h = response->details.created.planchets_h;
     207            3 :     aws->selected_h = response->details.created.selected_h;
     208            3 :     aws->reserve_history.details.withdraw.planchets_h = aws->planchets_h;
     209            3 :     aws->reserve_history.details.withdraw.selected_h = aws->selected_h;
     210            3 :     aws->reserve_history.details.withdraw.noreveal_index = aws->noreveal_index;
     211            3 :     aws->kappa_seed = response->details.created.kappa_seed;
     212              : 
     213            3 :     GNUNET_assert (aws->num_coins == response->details.created.num_coins);
     214           10 :     for (size_t n = 0; n < aws->num_coins; n++)
     215              :     {
     216            7 :       aws->coin_outputs[n].details = response->details.created.coin_details[n];
     217            7 :       TALER_age_commitment_proof_deep_copy (
     218            7 :         &aws->coin_outputs[n].details.age_commitment_proof,
     219            7 :         &response->details.created.coin_details[n].age_commitment_proof);
     220            7 :       TALER_denom_ewv_copy (
     221            7 :         &aws->coin_outputs[n].details.blinding_values,
     222            7 :         &response->details.created.coin_details[n].blinding_values);
     223              :     }
     224            3 :     break;
     225            0 :   case MHD_HTTP_FORBIDDEN:
     226              :   case MHD_HTTP_NOT_FOUND:
     227              :   case MHD_HTTP_GONE:
     228              :     /* nothing to check */
     229            0 :     break;
     230            2 :   case MHD_HTTP_CONFLICT:
     231              :     /* FIXME[oec]: Add this to the response-type and handle it here */
     232            2 :     break;
     233            0 :   case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
     234              :   default:
     235              :     /* Unsupported status code (by test harness) */
     236            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     237              :                 "test command for age-withdraw not support status code %u, body:\n"
     238              :                 ">>%s<<\n",
     239              :                 response->hr.http_status,
     240              :                 json_dumps (response->hr.reply, JSON_INDENT (2)));
     241            0 :     GNUNET_break (0);
     242            0 :     break;
     243              :   }
     244              : 
     245              :   /* We are done with this command, pick the next one */
     246            5 :   TALER_TESTING_interpreter_next (is);
     247              : }
     248              : 
     249              : 
     250              : /**
     251              :  * Run the command for age-withdraw.
     252              :  */
     253              : static void
     254            5 : age_withdraw_run (
     255              :   void *cls,
     256              :   const struct TALER_TESTING_Command *cmd,
     257              :   struct TALER_TESTING_Interpreter *is)
     258              : {
     259            5 :   struct AgeWithdrawState *aws = cls;
     260            5 :   struct TALER_EXCHANGE_Keys *keys = TALER_TESTING_get_keys (is);
     261              :   const struct TALER_ReservePrivateKeyP *rp;
     262              :   const struct TALER_TESTING_Command *create_reserve;
     263              :   const struct TALER_EXCHANGE_DenomPublicKey *dpk;
     264              : 
     265            5 :   aws->is = is;
     266              : 
     267              :   /* Prepare the reserve related data */
     268              :   create_reserve
     269            5 :     = TALER_TESTING_interpreter_lookup_command (
     270              :         is,
     271              :         aws->reserve_reference);
     272              : 
     273            5 :   if (NULL == create_reserve)
     274              :   {
     275            0 :     GNUNET_break (0);
     276            0 :     TALER_TESTING_interpreter_fail (is);
     277            0 :     return;
     278              :   }
     279            5 :   if (GNUNET_OK !=
     280            5 :       TALER_TESTING_get_trait_reserve_priv (create_reserve,
     281              :                                             &rp))
     282              :   {
     283            0 :     GNUNET_break (0);
     284            0 :     TALER_TESTING_interpreter_fail (is);
     285            0 :     return;
     286              :   }
     287            5 :   if (NULL == aws->exchange_url)
     288              :     aws->exchange_url
     289            5 :       = GNUNET_strdup (TALER_TESTING_get_exchange_url (is));
     290            5 :   aws->reserve_priv = *rp;
     291            5 :   GNUNET_CRYPTO_eddsa_key_get_public (&aws->reserve_priv.eddsa_priv,
     292              :                                       &aws->reserve_pub.eddsa_pub);
     293              :   aws->reserve_payto_uri
     294            5 :     = TALER_reserve_make_payto (aws->exchange_url,
     295            5 :                                 &aws->reserve_pub);
     296              : 
     297            5 :   aws->denoms_pub = GNUNET_new_array (aws->num_coins,
     298              :                                       struct TALER_EXCHANGE_DenomPublicKey);
     299              : 
     300            5 :   GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
     301            5 :                               &aws->seed,
     302              :                               sizeof(aws->seed));
     303              : 
     304           14 :   for (unsigned int i = 0; i<aws->num_coins; i++)
     305              :   {
     306            9 :     struct TALER_EXCHANGE_DenomPublicKey *denom_pub = &aws->denoms_pub[i];
     307            9 :     struct CoinOutputState *cos = &aws->coin_outputs[i];
     308              : 
     309              :     /* Find denomination */
     310            9 :     dpk = TALER_TESTING_find_pk (keys,
     311            9 :                                  &cos->amount,
     312              :                                  true); /* _always_ use denominations with age-striction */
     313            9 :     if (NULL == dpk)
     314              :     {
     315            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     316              :                   "Failed to determine denomination key for amount at %s\n",
     317              :                   (NULL != cmd) ? cmd->label : "<retried command>");
     318            0 :       GNUNET_break (0);
     319            0 :       TALER_TESTING_interpreter_fail (is);
     320            0 :       return;
     321              :     }
     322              : 
     323              :     /* We copy the denomination key, as re-querying /keys
     324              :      * would free the old one. */
     325            9 :     *denom_pub = *dpk;
     326            9 :     TALER_denom_pub_copy (&denom_pub->key,
     327              :                           &dpk->key);
     328              : 
     329              :     /* Accumulate the expected total amount and fee for the history */
     330            9 :     GNUNET_assert (0 <=
     331              :                    TALER_amount_add (&aws->reserve_history.amount,
     332              :                                      &cos->amount,
     333              :                                      &denom_pub->fees.withdraw));
     334            9 :     if (i == 0)
     335            5 :       GNUNET_assert (GNUNET_OK ==
     336              :                      TALER_amount_set_zero (
     337              :                        denom_pub->fees.withdraw.currency,
     338              :                        &aws->reserve_history.details.withdraw.fee));
     339              : 
     340            9 :     GNUNET_assert (0 <=
     341              :                    TALER_amount_add (&aws->reserve_history.details.withdraw.fee,
     342              :                                      &aws->reserve_history.details.withdraw.fee,
     343              :                                      &denom_pub->fees.withdraw));
     344              : 
     345              :   }
     346              :   /* Save the expected history entry */
     347            5 :   aws->reserve_history.type = TALER_EXCHANGE_RTT_WITHDRAWAL;
     348            5 :   aws->reserve_history.details.withdraw.age_restricted = true;
     349            5 :   aws->reserve_history.details.withdraw.max_age = aws->max_age;
     350              : 
     351              : 
     352              :   /* Execute the age-restricted variant of withdraw protocol */
     353            5 :   aws->handle =
     354            5 :     TALER_EXCHANGE_post_withdraw_create (
     355              :       TALER_TESTING_interpreter_get_context (is),
     356              :       TALER_TESTING_get_exchange_url (is),
     357              :       keys,
     358              :       rp,
     359              :       aws->num_coins,
     360            5 :       aws->denoms_pub,
     361            5 :       &aws->seed,
     362            5 :       aws->max_age);
     363            5 :   if (NULL == aws->handle)
     364              :   {
     365            0 :     GNUNET_break (0);
     366            0 :     TALER_TESTING_interpreter_fail (is);
     367            0 :     return;
     368              :   }
     369            5 :   GNUNET_assert (GNUNET_OK ==
     370              :                  TALER_EXCHANGE_post_withdraw_set_options (
     371              :                    aws->handle,
     372              :                    TALER_EXCHANGE_post_withdraw_option_with_age_proof (
     373              :                      aws->max_age)));
     374            5 :   GNUNET_assert (TALER_EC_NONE ==
     375              :                  TALER_EXCHANGE_post_withdraw_start (aws->handle,
     376              :                                                      &age_withdraw_cb,
     377              :                                                      aws));
     378              : }
     379              : 
     380              : 
     381              : /**
     382              :  * Free the state of a "age withdraw" CMD, and possibly cancel a
     383              :  * pending operation thereof
     384              :  *
     385              :  * @param cls Closure of type `struct AgeWithdrawState`
     386              :  * @param cmd The command being freed.
     387              :  */
     388              : static void
     389            5 : age_withdraw_cleanup (
     390              :   void *cls,
     391              :   const struct TALER_TESTING_Command *cmd)
     392              : {
     393            5 :   struct AgeWithdrawState *aws = cls;
     394              : 
     395            5 :   if (NULL != aws->handle)
     396              :   {
     397            0 :     TALER_TESTING_command_incomplete (aws->is,
     398              :                                       cmd->label);
     399            0 :     TALER_EXCHANGE_post_withdraw_cancel (aws->handle);
     400            0 :     aws->handle = NULL;
     401              :   }
     402              : 
     403            5 :   if (NULL != aws->denoms_pub)
     404              :   {
     405           14 :     for (size_t n = 0; n < aws->num_coins; n++)
     406            9 :       TALER_denom_pub_free (&aws->denoms_pub[n].key);
     407              : 
     408            5 :     GNUNET_free (aws->denoms_pub);
     409            5 :     aws->denoms_pub = NULL;
     410              :   }
     411              : 
     412            5 :   if (NULL != aws->coin_outputs)
     413              :   {
     414           14 :     for (size_t n = 0; n < aws->num_coins; n++)
     415              :     {
     416            9 :       struct CoinOutputState *out = &aws->coin_outputs[n];
     417            9 :       TALER_age_commitment_proof_free (&out->details.age_commitment_proof);
     418            9 :       TALER_denom_ewv_free (&out->details.blinding_values);
     419              :     }
     420            5 :     GNUNET_free (aws->coin_outputs);
     421            5 :     aws->coin_outputs = NULL;
     422              :   }
     423              : 
     424            5 :   GNUNET_free (aws->exchange_url);
     425            5 :   aws->exchange_url  = NULL;
     426            5 :   GNUNET_free (aws->reserve_payto_uri.normalized_payto);
     427            5 :   aws->reserve_payto_uri.normalized_payto = NULL;
     428            5 :   GNUNET_free (aws);
     429            5 : }
     430              : 
     431              : 
     432              : /**
     433              :  * Offer internal data of a "age withdraw" CMD state to other commands.
     434              :  *
     435              :  * @param cls Closure of type `struct AgeWithdrawState`
     436              :  * @param[out] ret result (could be anything)
     437              :  * @param trait name of the trait
     438              :  * @param idx index number of the object to offer.
     439              :  * @return #GNUNET_OK on success
     440              :  */
     441              : static enum GNUNET_GenericReturnValue
     442            4 : age_withdraw_traits (
     443              :   void *cls,
     444              :   const void **ret,
     445              :   const char *trait,
     446              :   unsigned int idx)
     447              : {
     448            4 :   struct AgeWithdrawState *aws = cls;
     449            4 :   struct CoinOutputState *out = &aws->coin_outputs[idx];
     450            4 :   struct TALER_EXCHANGE_WithdrawCoinPrivateDetails *details =
     451            4 :     &aws->coin_outputs[idx].details;
     452              :   struct TALER_TESTING_Trait traits[] = {
     453              :     /* history entry MUST be first due to response code logic below! */
     454            4 :     TALER_TESTING_make_trait_reserve_history (idx,
     455            4 :                                               &aws->reserve_history),
     456            4 :     TALER_TESTING_make_trait_denom_pub (idx,
     457            4 :                                         &aws->denoms_pub[idx]),
     458            4 :     TALER_TESTING_make_trait_reserve_priv (&aws->reserve_priv),
     459            4 :     TALER_TESTING_make_trait_reserve_pub (&aws->reserve_pub),
     460            4 :     TALER_TESTING_make_trait_withdraw_commitment (&aws->planchets_h),
     461            4 :     TALER_TESTING_make_trait_amounts (idx,
     462            4 :                                       &out->amount),
     463              :     /* FIXME[oec]: add legal requirement to response and handle it here, as well
     464              :     TALER_TESTING_make_trait_legi_requirement_row (&aws->requirement_row),
     465              :     TALER_TESTING_make_trait_h_payto (&aws->h_payto),
     466              :     */
     467            4 :     TALER_TESTING_make_trait_normalized_payto_uri (&aws->reserve_payto_uri),
     468            4 :     TALER_TESTING_make_trait_exchange_url (aws->exchange_url),
     469            4 :     TALER_TESTING_make_trait_coin_priv (idx,
     470            4 :                                         &details->coin_priv),
     471            4 :     TALER_TESTING_make_trait_withdraw_seed (&aws->seed),
     472              :     /* FIXME[oec]: needed!?
     473              :     TALER_TESTING_make_trait_planchet_secrets (idx,
     474              :                                                &aws->secrets[k][idx]),
     475              :     */
     476            4 :     TALER_TESTING_make_trait_blinding_key (idx,
     477            4 :                                            &details->blinding_key),
     478            4 :     TALER_TESTING_make_trait_exchange_blinding_values (idx,
     479            4 :                                                        &details->blinding_values
     480              :                                                        ),
     481            4 :     TALER_TESTING_make_trait_age_commitment_proof (
     482              :       idx,
     483            4 :       &details->age_commitment_proof),
     484            4 :     TALER_TESTING_make_trait_h_age_commitment (
     485              :       idx,
     486            4 :       &details->h_age_commitment),
     487            4 :     TALER_TESTING_trait_end ()
     488              :   };
     489              : 
     490            4 :   if (idx >= aws->num_coins)
     491            0 :     return GNUNET_NO;
     492              : 
     493            4 :   return TALER_TESTING_get_trait ((aws->expected_response_code == MHD_HTTP_OK)
     494              :                                   ? &traits[0] /* we have reserve history */
     495              :                                   : &traits[1], /* skip reserve history */
     496              :                                   ret,
     497              :                                   trait,
     498              :                                   idx);
     499              : }
     500              : 
     501              : 
     502              : struct TALER_TESTING_Command
     503            5 : TALER_TESTING_cmd_withdraw_with_age_proof (const char *label,
     504              :                                            const char *reserve_reference,
     505              :                                            uint8_t max_age,
     506              :                                            unsigned int
     507              :                                            expected_response_code,
     508              :                                            const char *amount,
     509              :                                            ...)
     510              : {
     511              :   struct AgeWithdrawState *aws;
     512              :   unsigned int cnt;
     513              :   va_list ap;
     514              : 
     515            5 :   aws = GNUNET_new (struct AgeWithdrawState);
     516            5 :   aws->reserve_reference = reserve_reference;
     517            5 :   aws->expected_response_code = expected_response_code;
     518            5 :   aws->mask = TALER_extensions_get_age_restriction_mask ();
     519            5 :   aws->max_age = TALER_get_lowest_age (&aws->mask,
     520              :                                        max_age);
     521            5 :   cnt = 1;
     522            5 :   va_start (ap, amount);
     523            9 :   while (NULL != (va_arg (ap, const char *)))
     524            4 :     cnt++;
     525            5 :   aws->num_coins = cnt;
     526            5 :   aws->coin_outputs = GNUNET_new_array (cnt,
     527              :                                         struct CoinOutputState);
     528            5 :   va_end (ap);
     529            5 :   va_start (ap, amount);
     530              : 
     531           14 :   for (unsigned int i = 0; i<aws->num_coins; i++)
     532              :   {
     533            9 :     struct CoinOutputState *out = &aws->coin_outputs[i];
     534            9 :     if (GNUNET_OK !=
     535            9 :         TALER_string_to_amount (amount,
     536              :                                 &out->amount))
     537              :     {
     538            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     539              :                   "Failed to parse amount `%s' at %s\n",
     540              :                   amount,
     541              :                   label);
     542            0 :       GNUNET_assert (0);
     543              :     }
     544              :     /* move on to next vararg! */
     545            9 :     amount = va_arg (ap, const char *);
     546              :   }
     547              : 
     548            5 :   GNUNET_assert (NULL == amount);
     549            5 :   va_end (ap);
     550              : 
     551              :   {
     552            5 :     struct TALER_TESTING_Command cmd = {
     553              :       .cls = aws,
     554              :       .label = label,
     555              :       .run = &age_withdraw_run,
     556              :       .cleanup = &age_withdraw_cleanup,
     557              :       .traits = &age_withdraw_traits,
     558              :     };
     559              : 
     560            5 :     return cmd;
     561              :   }
     562              : }
     563              : 
     564              : 
     565              : /**
     566              :  * The state for the age-withdraw-reveal operation
     567              :  */
     568              : struct AgeRevealWithdrawState
     569              : {
     570              :   /**
     571              :    * The reference to the CMD resembling the previous call to age-withdraw
     572              :    */
     573              :   const char *age_withdraw_reference;
     574              : 
     575              :   /**
     576              :    * The state to the previous age-withdraw command
     577              :    */
     578              :   const struct AgeWithdrawState *aws;
     579              : 
     580              :   /**
     581              :    * The expected response code from the call to the
     582              :    * age-withdraw-reveal operation
     583              :    */
     584              :   unsigned int expected_response_code;
     585              : 
     586              :   /**
     587              :    * Interpreter state (during command)
     588              :    */
     589              :   struct TALER_TESTING_Interpreter *is;
     590              : 
     591              :   /**
     592              :    * The handle to the reveal-operation
     593              :    */
     594              :   struct TALER_EXCHANGE_PostRevealWithdrawHandle *handle;
     595              : 
     596              : 
     597              :   /**
     598              :    * Number of coins, extracted form the age withdraw command
     599              :    */
     600              :   size_t num_coins;
     601              : 
     602              :   /**
     603              :    * The signatures of the @e num_coins coins returned
     604              :    */
     605              :   struct TALER_DenominationSignature *denom_sigs;
     606              : 
     607              : };
     608              : 
     609              : 
     610              : /**
     611              :  * Callback for the reveal response
     612              :  *
     613              :  * @param cls Closure of type `struct AgeRevealWithdrawState`
     614              :  * @param response The response
     615              :  */
     616              : static void
     617            3 : age_reveal_withdraw_cb (
     618              :   void *cls,
     619              :   const struct TALER_EXCHANGE_PostRevealWithdrawResponse *response)
     620              : {
     621            3 :   struct AgeRevealWithdrawState *awrs = cls;
     622            3 :   struct TALER_TESTING_Interpreter *is = awrs->is;
     623              : 
     624            3 :   awrs->handle = NULL;
     625            3 :   if (awrs->expected_response_code != response->hr.http_status)
     626              :   {
     627            0 :     TALER_TESTING_unexpected_status_with_body (is,
     628              :                                                response->hr.http_status,
     629              :                                                awrs->expected_response_code,
     630              :                                                response->hr.reply);
     631            0 :     return;
     632              :   }
     633            3 :   switch (response->hr.http_status)
     634              :   {
     635            3 :   case MHD_HTTP_OK:
     636              :     {
     637            3 :       const struct AgeWithdrawState *aws = awrs->aws;
     638            3 :       GNUNET_assert (awrs->num_coins == response->details.ok.num_sigs);
     639            3 :       awrs->denom_sigs = GNUNET_new_array (awrs->num_coins,
     640              :                                            struct TALER_DenominationSignature);
     641           10 :       for (size_t n = 0; n < awrs->num_coins; n++)
     642              :       {
     643            7 :         GNUNET_assert (GNUNET_OK ==
     644              :                        TALER_denom_sig_unblind (
     645              :                          &awrs->denom_sigs[n],
     646              :                          &response->details.ok.blinded_denom_sigs[n],
     647              :                          &aws->coin_outputs[n].details.blinding_key,
     648              :                          &aws->coin_outputs[n].details.h_coin_pub,
     649              :                          &aws->coin_outputs[n].details.blinding_values,
     650              :                          &aws->denoms_pub[n].key));
     651            7 :         TALER_denom_sig_free (&awrs->denom_sigs[n]);
     652              :       }
     653              : 
     654            3 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     655              :                   "age-withdraw reveal success!\n");
     656            3 :       GNUNET_free (awrs->denom_sigs);
     657              :     }
     658            3 :     break;
     659            0 :   case MHD_HTTP_NOT_FOUND:
     660              :   case MHD_HTTP_FORBIDDEN:
     661              :     /* nothing to check */
     662            0 :     break;
     663              :   /* FIXME[oec]: handle more cases !? */
     664            0 :   default:
     665              :     /* Unsupported status code (by test harness) */
     666            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     667              :                 "Age withdraw reveal test command does not support status code %u\n",
     668              :                 response->hr.http_status);
     669            0 :     GNUNET_break (0);
     670            0 :     break;
     671              :   }
     672              : 
     673              :   /* We are done with this command, pick the next one */
     674            3 :   TALER_TESTING_interpreter_next (is);
     675              : }
     676              : 
     677              : 
     678              : /**
     679              :  * Run the command for age-withdraw-reveal
     680              :  */
     681              : static void
     682            3 : age_reveal_withdraw_run (
     683              :   void *cls,
     684              :   const struct TALER_TESTING_Command *cmd,
     685              :   struct TALER_TESTING_Interpreter *is)
     686              : {
     687            3 :   struct AgeRevealWithdrawState *awrs = cls;
     688              :   const struct TALER_TESTING_Command *age_withdraw_cmd;
     689              :   const struct AgeWithdrawState *aws;
     690              : 
     691              :   (void) cmd;
     692            3 :   awrs->is = is;
     693              : 
     694              :   /*
     695              :    * Get the command and state for the previous call to "age witdraw"
     696              :    */
     697              :   age_withdraw_cmd  =
     698            3 :     TALER_TESTING_interpreter_lookup_command (is,
     699              :                                               awrs->age_withdraw_reference);
     700            3 :   if (NULL == age_withdraw_cmd)
     701              :   {
     702            0 :     GNUNET_break (0);
     703            0 :     TALER_TESTING_interpreter_fail (is);
     704            0 :     return;
     705              :   }
     706            3 :   GNUNET_assert (age_withdraw_cmd->run == age_withdraw_run);
     707            3 :   aws = age_withdraw_cmd->cls;
     708            3 :   awrs->aws = aws;
     709            3 :   awrs->num_coins = aws->num_coins;
     710              : 
     711              :   {
     712              :     struct TALER_RevealWithdrawMasterSeedsP revealed_seeds;
     713            3 :     size_t j = 0;
     714           12 :     for (uint8_t k = 0; k < TALER_CNC_KAPPA; k++)
     715              :     {
     716            9 :       if (aws->noreveal_index == k)
     717            3 :         continue;
     718              : 
     719            6 :       revealed_seeds.tuple[j] = aws->kappa_seed.tuple[k];
     720            6 :       j++;
     721              :     }
     722              : 
     723            3 :     awrs->handle =
     724            3 :       TALER_EXCHANGE_post_reveal_withdraw_create (
     725              :         TALER_TESTING_interpreter_get_context (is),
     726              :         TALER_TESTING_get_exchange_url (is),
     727            3 :         aws->num_coins,
     728              :         &aws->planchets_h,
     729              :         &revealed_seeds);
     730            3 :     GNUNET_assert (NULL != awrs->handle);
     731            3 :     GNUNET_assert (TALER_EC_NONE ==
     732              :                    TALER_EXCHANGE_post_reveal_withdraw_start (
     733              :                      awrs->handle,
     734              :                      &age_reveal_withdraw_cb,
     735              :                      awrs));
     736              :   }
     737              : }
     738              : 
     739              : 
     740              : /**
     741              :  * Free the state of a "age-withdraw-reveal" CMD, and possibly
     742              :  * cancel a pending operation thereof
     743              :  *
     744              :  * @param cls Closure of type `struct AgeRevealWithdrawState`
     745              :  * @param cmd The command being freed.
     746              :  */
     747              : static void
     748            3 : age_reveal_withdraw_cleanup (
     749              :   void *cls,
     750              :   const struct TALER_TESTING_Command *cmd)
     751              : {
     752            3 :   struct AgeRevealWithdrawState *awrs = cls;
     753              : 
     754            3 :   if (NULL != awrs->handle)
     755              :   {
     756            0 :     TALER_TESTING_command_incomplete (awrs->is,
     757              :                                       cmd->label);
     758            0 :     TALER_EXCHANGE_post_reveal_withdraw_cancel (awrs->handle);
     759            0 :     awrs->handle = NULL;
     760              :   }
     761            3 :   GNUNET_free (awrs->denom_sigs);
     762            3 :   awrs->denom_sigs = NULL;
     763            3 :   GNUNET_free (awrs);
     764            3 : }
     765              : 
     766              : 
     767              : /**
     768              :  * Offer internal data of a "age withdraw reveal" CMD state to other commands.
     769              :  *
     770              :  * @param cls Closure of they `struct AgeRevealWithdrawState`
     771              :  * @param[out] ret result (could be anything)
     772              :  * @param trait name of the trait
     773              :  * @param idx index number of the object to offer.
     774              :  * @return #GNUNET_OK on success
     775              :  */
     776              : static enum GNUNET_GenericReturnValue
     777            4 : age_reveal_withdraw_traits (
     778              :   void *cls,
     779              :   const void **ret,
     780              :   const char *trait,
     781              :   unsigned int idx)
     782              : {
     783            4 :   struct AgeRevealWithdrawState *awrs = cls;
     784              :   struct TALER_TESTING_Trait traits[] = {
     785            4 :     TALER_TESTING_make_trait_denom_sig (idx,
     786            4 :                                         &awrs->denom_sigs[idx]),
     787              :     /* FIXME: shall we provide the traits from the previous
     788              :      * call to "age withdraw" as well? */
     789            4 :     TALER_TESTING_trait_end ()
     790              :   };
     791              : 
     792            4 :   if (idx >= awrs->num_coins)
     793            0 :     return GNUNET_NO;
     794              : 
     795            4 :   return TALER_TESTING_get_trait (traits,
     796              :                                   ret,
     797              :                                   trait,
     798              :                                   idx);
     799              : }
     800              : 
     801              : 
     802              : struct TALER_TESTING_Command
     803            3 : TALER_TESTING_cmd_withdraw_reveal_age_proof (
     804              :   const char *label,
     805              :   const char *age_withdraw_reference,
     806              :   unsigned int expected_response_code)
     807              : {
     808              :   struct AgeRevealWithdrawState *awrs =
     809            3 :     GNUNET_new (struct AgeRevealWithdrawState);
     810              : 
     811            3 :   awrs->age_withdraw_reference = age_withdraw_reference;
     812            3 :   awrs->expected_response_code = expected_response_code;
     813              :   {
     814            3 :     struct TALER_TESTING_Command cmd = {
     815              :       .cls = awrs,
     816              :       .label = label,
     817              :       .run = age_reveal_withdraw_run,
     818              :       .cleanup = age_reveal_withdraw_cleanup,
     819              :       .traits = age_reveal_withdraw_traits,
     820              :     };
     821              : 
     822            3 :     return cmd;
     823              :   }
     824              : }
     825              : 
     826              : 
     827              : /* end of testing_api_cmd_age_withdraw.c */
        

Generated by: LCOV version 2.0-1