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 % 236 194
Test Date: 2025-12-26 23:00:34 Functions: 100.0 % 10 10

            Line data    Source code
       1              : /*
       2              :   This file is part of TALER
       3              :   Copyright (C) 2023-2025 Taler Systems SA
       4              : 
       5              :   TALER is free software; you can redistribute it and/or modify it
       6              :   under the terms of the GNU General Public License as published by
       7              :   the Free Software Foundation; either version 3, or (at your
       8              :   option) any later version.
       9              : 
      10              :   TALER is distributed in the hope that it will be useful, but
      11              :   WITHOUT ANY WARRANTY; without even the implied warranty of
      12              :   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      13              :   General Public License for more details.
      14              : 
      15              :   You should have received a copy of the GNU General Public
      16              :   License along with TALER; see the file COPYING.  If not, see
      17              :   <http://www.gnu.org/licenses/>
      18              : */
      19              : /**
      20              :  * @file testing/testing_api_cmd_age_withdraw.c
      21              :  * @brief implements the withdraw command for age-restricted coins
      22              :  * @author Özgür Kesim
      23              :  */
      24              : 
      25              : #include "taler/platform.h"
      26              : #include "taler/taler_exchange_service.h"
      27              : #include "taler/taler_json_lib.h"
      28              : #include <gnunet/gnunet_common.h>
      29              : #include <microhttpd.h>
      30              : #include <gnunet/gnunet_curl_lib.h>
      31              : #include "taler/taler_signatures.h"
      32              : #include "taler/taler_extensions.h"
      33              : #include "taler/taler_testing_lib.h"
      34              : 
      35              : /*
      36              :  * The output state of coin
      37              :  */
      38              : struct CoinOutputState
      39              : {
      40              : 
      41              :   /**
      42              :    * The calculated details during "withdraw", for the selected coin.
      43              :    */
      44              :   struct TALER_EXCHANGE_WithdrawCoinPrivateDetails details;
      45              : 
      46              :   /**
      47              :    * The (wanted) value of the coin, MUST be the same as input.denom_pub.value;
      48              :    */
      49              :   struct TALER_Amount amount;
      50              : 
      51              : };
      52              : 
      53              : /**
      54              :  * State for a "age withdraw" CMD:
      55              :  */
      56              : 
      57              : struct AgeWithdrawState
      58              : {
      59              : 
      60              :   /**
      61              :    * Interpreter state (during command)
      62              :    */
      63              :   struct TALER_TESTING_Interpreter *is;
      64              : 
      65              :   /**
      66              :    * The age-withdraw handle
      67              :    */
      68              :   struct TALER_EXCHANGE_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              :                                                        ),
     476            4 :     TALER_TESTING_make_trait_age_commitment_proof (
     477              :       idx,
     478            4 :       &details->age_commitment_proof),
     479            4 :     TALER_TESTING_make_trait_h_age_commitment (
     480              :       idx,
     481            4 :       &details->h_age_commitment),
     482            4 :     TALER_TESTING_trait_end ()
     483              :   };
     484              : 
     485            4 :   if (idx >= aws->num_coins)
     486            0 :     return GNUNET_NO;
     487              : 
     488            4 :   return TALER_TESTING_get_trait ((aws->expected_response_code == MHD_HTTP_OK)
     489              :                                   ? &traits[0] /* we have reserve history */
     490              :                                   : &traits[1], /* skip reserve history */
     491              :                                   ret,
     492              :                                   trait,
     493              :                                   idx);
     494              : }
     495              : 
     496              : 
     497              : struct TALER_TESTING_Command
     498            5 : TALER_TESTING_cmd_withdraw_with_age_proof (const char *label,
     499              :                                            const char *reserve_reference,
     500              :                                            uint8_t max_age,
     501              :                                            unsigned int
     502              :                                            expected_response_code,
     503              :                                            const char *amount,
     504              :                                            ...)
     505              : {
     506              :   struct AgeWithdrawState *aws;
     507              :   unsigned int cnt;
     508              :   va_list ap;
     509              : 
     510            5 :   aws = GNUNET_new (struct AgeWithdrawState);
     511            5 :   aws->reserve_reference = reserve_reference;
     512            5 :   aws->expected_response_code = expected_response_code;
     513            5 :   aws->mask = TALER_extensions_get_age_restriction_mask ();
     514            5 :   aws->max_age = TALER_get_lowest_age (&aws->mask,
     515              :                                        max_age);
     516            5 :   cnt = 1;
     517            5 :   va_start (ap, amount);
     518            9 :   while (NULL != (va_arg (ap, const char *)))
     519            4 :     cnt++;
     520            5 :   aws->num_coins = cnt;
     521            5 :   aws->coin_outputs = GNUNET_new_array (cnt,
     522              :                                         struct CoinOutputState);
     523            5 :   va_end (ap);
     524            5 :   va_start (ap, amount);
     525              : 
     526           14 :   for (unsigned int i = 0; i<aws->num_coins; i++)
     527              :   {
     528            9 :     struct CoinOutputState *out = &aws->coin_outputs[i];
     529            9 :     if (GNUNET_OK !=
     530            9 :         TALER_string_to_amount (amount,
     531              :                                 &out->amount))
     532              :     {
     533            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     534              :                   "Failed to parse amount `%s' at %s\n",
     535              :                   amount,
     536              :                   label);
     537            0 :       GNUNET_assert (0);
     538              :     }
     539              :     /* move on to next vararg! */
     540            9 :     amount = va_arg (ap, const char *);
     541              :   }
     542              : 
     543            5 :   GNUNET_assert (NULL == amount);
     544            5 :   va_end (ap);
     545              : 
     546              :   {
     547            5 :     struct TALER_TESTING_Command cmd = {
     548              :       .cls = aws,
     549              :       .label = label,
     550              :       .run = &age_withdraw_run,
     551              :       .cleanup = &age_withdraw_cleanup,
     552              :       .traits = &age_withdraw_traits,
     553              :     };
     554              : 
     555            5 :     return cmd;
     556              :   }
     557              : }
     558              : 
     559              : 
     560              : /**
     561              :  * The state for the age-withdraw-reveal operation
     562              :  */
     563              : struct AgeRevealWithdrawState
     564              : {
     565              :   /**
     566              :    * The reference to the CMD resembling the previous call to age-withdraw
     567              :    */
     568              :   const char *age_withdraw_reference;
     569              : 
     570              :   /**
     571              :    * The state to the previous age-withdraw command
     572              :    */
     573              :   const struct AgeWithdrawState *aws;
     574              : 
     575              :   /**
     576              :    * The expected response code from the call to the
     577              :    * age-withdraw-reveal operation
     578              :    */
     579              :   unsigned int expected_response_code;
     580              : 
     581              :   /**
     582              :    * Interpreter state (during command)
     583              :    */
     584              :   struct TALER_TESTING_Interpreter *is;
     585              : 
     586              :   /**
     587              :    * The handle to the reveal-operation
     588              :    */
     589              :   struct TALER_EXCHANGE_RevealWithdrawHandle *handle;
     590              : 
     591              : 
     592              :   /**
     593              :    * Number of coins, extracted form the age withdraw command
     594              :    */
     595              :   size_t num_coins;
     596              : 
     597              :   /**
     598              :    * The signatures of the @e num_coins coins returned
     599              :    */
     600              :   struct TALER_DenominationSignature *denom_sigs;
     601              : 
     602              : };
     603              : 
     604              : 
     605              : /**
     606              :  * Callback for the reveal response
     607              :  *
     608              :  * @param cls Closure of type `struct AgeRevealWithdrawState`
     609              :  * @param response The response
     610              :  */
     611              : static void
     612            3 : age_reveal_withdraw_cb (
     613              :   void *cls,
     614              :   const struct TALER_EXCHANGE_RevealWithdrawResponse *response)
     615              : {
     616            3 :   struct AgeRevealWithdrawState *awrs = cls;
     617            3 :   struct TALER_TESTING_Interpreter *is = awrs->is;
     618              : 
     619            3 :   awrs->handle = NULL;
     620            3 :   if (awrs->expected_response_code != response->hr.http_status)
     621              :   {
     622            0 :     TALER_TESTING_unexpected_status_with_body (is,
     623              :                                                response->hr.http_status,
     624              :                                                awrs->expected_response_code,
     625              :                                                response->hr.reply);
     626            0 :     return;
     627              :   }
     628            3 :   switch (response->hr.http_status)
     629              :   {
     630            3 :   case MHD_HTTP_OK:
     631              :     {
     632            3 :       const struct AgeWithdrawState *aws = awrs->aws;
     633            3 :       GNUNET_assert (awrs->num_coins == response->details.ok.num_sigs);
     634            3 :       awrs->denom_sigs = GNUNET_new_array (awrs->num_coins,
     635              :                                            struct TALER_DenominationSignature);
     636           10 :       for (size_t n = 0; n < awrs->num_coins; n++)
     637              :       {
     638            7 :         GNUNET_assert (GNUNET_OK ==
     639              :                        TALER_denom_sig_unblind (
     640              :                          &awrs->denom_sigs[n],
     641              :                          &response->details.ok.blinded_denom_sigs[n],
     642              :                          &aws->coin_outputs[n].details.blinding_key,
     643              :                          &aws->coin_outputs[n].details.h_coin_pub,
     644              :                          &aws->coin_outputs[n].details.blinding_values,
     645              :                          &aws->denoms_pub[n].key));
     646            7 :         TALER_denom_sig_free (&awrs->denom_sigs[n]);
     647              :       }
     648              : 
     649            3 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     650              :                   "age-withdraw reveal success!\n");
     651            3 :       GNUNET_free (awrs->denom_sigs);
     652              :     }
     653            3 :     break;
     654            0 :   case MHD_HTTP_NOT_FOUND:
     655              :   case MHD_HTTP_FORBIDDEN:
     656              :     /* nothing to check */
     657            0 :     break;
     658              :   /* FIXME[oec]: handle more cases !? */
     659            0 :   default:
     660              :     /* Unsupported status code (by test harness) */
     661            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     662              :                 "Age withdraw reveal test command does not support status code %u\n",
     663              :                 response->hr.http_status);
     664            0 :     GNUNET_break (0);
     665            0 :     break;
     666              :   }
     667              : 
     668              :   /* We are done with this command, pick the next one */
     669            3 :   TALER_TESTING_interpreter_next (is);
     670              : }
     671              : 
     672              : 
     673              : /**
     674              :  * Run the command for age-withdraw-reveal
     675              :  */
     676              : static void
     677            3 : age_reveal_withdraw_run (
     678              :   void *cls,
     679              :   const struct TALER_TESTING_Command *cmd,
     680              :   struct TALER_TESTING_Interpreter *is)
     681              : {
     682            3 :   struct AgeRevealWithdrawState *awrs = cls;
     683              :   const struct TALER_TESTING_Command *age_withdraw_cmd;
     684              :   const struct AgeWithdrawState *aws;
     685              : 
     686              :   (void) cmd;
     687            3 :   awrs->is = is;
     688              : 
     689              :   /*
     690              :    * Get the command and state for the previous call to "age witdraw"
     691              :    */
     692              :   age_withdraw_cmd  =
     693            3 :     TALER_TESTING_interpreter_lookup_command (is,
     694              :                                               awrs->age_withdraw_reference);
     695            3 :   if (NULL == age_withdraw_cmd)
     696              :   {
     697            0 :     GNUNET_break (0);
     698            0 :     TALER_TESTING_interpreter_fail (is);
     699            0 :     return;
     700              :   }
     701            3 :   GNUNET_assert (age_withdraw_cmd->run == age_withdraw_run);
     702            3 :   aws = age_withdraw_cmd->cls;
     703            3 :   awrs->aws = aws;
     704            3 :   awrs->num_coins = aws->num_coins;
     705              : 
     706              :   {
     707              :     struct TALER_RevealWithdrawMasterSeedsP revealed_seeds;
     708            3 :     size_t j = 0;
     709           12 :     for (uint8_t k = 0; k < TALER_CNC_KAPPA; k++)
     710              :     {
     711            9 :       if (aws->noreveal_index == k)
     712            3 :         continue;
     713              : 
     714            6 :       revealed_seeds.tuple[j] = aws->kappa_seed.tuple[k];
     715            6 :       j++;
     716              :     }
     717              : 
     718            3 :     awrs->handle =
     719            3 :       TALER_EXCHANGE_reveal_withdraw (
     720              :         TALER_TESTING_interpreter_get_context (is),
     721              :         TALER_TESTING_get_exchange_url (is),
     722            3 :         aws->num_coins,
     723              :         &aws->planchets_h,
     724              :         &revealed_seeds,
     725              :         age_reveal_withdraw_cb,
     726              :         awrs);
     727              :   }
     728              : }
     729              : 
     730              : 
     731              : /**
     732              :  * Free the state of a "age-withdraw-reveal" CMD, and possibly
     733              :  * cancel a pending operation thereof
     734              :  *
     735              :  * @param cls Closure of type `struct AgeRevealWithdrawState`
     736              :  * @param cmd The command being freed.
     737              :  */
     738              : static void
     739            3 : age_reveal_withdraw_cleanup (
     740              :   void *cls,
     741              :   const struct TALER_TESTING_Command *cmd)
     742              : {
     743            3 :   struct AgeRevealWithdrawState *awrs = cls;
     744              : 
     745            3 :   if (NULL != awrs->handle)
     746              :   {
     747            0 :     TALER_TESTING_command_incomplete (awrs->is,
     748              :                                       cmd->label);
     749            0 :     TALER_EXCHANGE_reveal_withdraw_cancel (awrs->handle);
     750            0 :     awrs->handle = NULL;
     751              :   }
     752            3 :   GNUNET_free (awrs->denom_sigs);
     753            3 :   awrs->denom_sigs = NULL;
     754            3 :   GNUNET_free (awrs);
     755            3 : }
     756              : 
     757              : 
     758              : /**
     759              :  * Offer internal data of a "age withdraw reveal" CMD state to other commands.
     760              :  *
     761              :  * @param cls Closure of they `struct AgeRevealWithdrawState`
     762              :  * @param[out] ret result (could be anything)
     763              :  * @param trait name of the trait
     764              :  * @param idx index number of the object to offer.
     765              :  * @return #GNUNET_OK on success
     766              :  */
     767              : static enum GNUNET_GenericReturnValue
     768            4 : age_reveal_withdraw_traits (
     769              :   void *cls,
     770              :   const void **ret,
     771              :   const char *trait,
     772              :   unsigned int idx)
     773              : {
     774            4 :   struct AgeRevealWithdrawState *awrs = cls;
     775              :   struct TALER_TESTING_Trait traits[] = {
     776            4 :     TALER_TESTING_make_trait_denom_sig (idx,
     777            4 :                                         &awrs->denom_sigs[idx]),
     778              :     /* FIXME: shall we provide the traits from the previous
     779              :      * call to "age withdraw" as well? */
     780            4 :     TALER_TESTING_trait_end ()
     781              :   };
     782              : 
     783            4 :   if (idx >= awrs->num_coins)
     784            0 :     return GNUNET_NO;
     785              : 
     786            4 :   return TALER_TESTING_get_trait (traits,
     787              :                                   ret,
     788              :                                   trait,
     789              :                                   idx);
     790              : }
     791              : 
     792              : 
     793              : struct TALER_TESTING_Command
     794            3 : TALER_TESTING_cmd_withdraw_reveal_age_proof (
     795              :   const char *label,
     796              :   const char *age_withdraw_reference,
     797              :   unsigned int expected_response_code)
     798              : {
     799              :   struct AgeRevealWithdrawState *awrs =
     800            3 :     GNUNET_new (struct AgeRevealWithdrawState);
     801              : 
     802            3 :   awrs->age_withdraw_reference = age_withdraw_reference;
     803            3 :   awrs->expected_response_code = expected_response_code;
     804              :   {
     805            3 :     struct TALER_TESTING_Command cmd = {
     806              :       .cls = awrs,
     807              :       .label = label,
     808              :       .run = age_reveal_withdraw_run,
     809              :       .cleanup = age_reveal_withdraw_cleanup,
     810              :       .traits = age_reveal_withdraw_traits,
     811              :     };
     812              : 
     813            3 :     return cmd;
     814              :   }
     815              : }
     816              : 
     817              : 
     818              : /* end of testing_api_cmd_age_withdraw.c */
        

Generated by: LCOV version 2.0-1