LCOV - code coverage report
Current view: top level - testing - testing_api_cmd_age_withdraw.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 194 236 82.2 %
Date: 2025-06-05 21:03:14 Functions: 10 10 100.0 %

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

Generated by: LCOV version 1.16