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

Generated by: LCOV version 2.0-1