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

Generated by: LCOV version 2.0-1