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

Generated by: LCOV version 2.0-1