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

Generated by: LCOV version 2.0-1