LCOV - code coverage report
Current view: top level - testing - testing_api_cmd_batch_withdraw.c (source / functions) Hit Total Coverage
Test: GNU Taler exchange coverage report Lines: 0 177 0.0 %
Date: 2022-08-25 06:15:09 Functions: 0 5 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   Copyright (C) 2018-2022 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_batch_withdraw.c
      21             :  * @brief implements the batch withdraw command
      22             :  * @author Christian Grothoff
      23             :  * @author Marcello Stanisci
      24             :  */
      25             : #include "platform.h"
      26             : #include "taler_json_lib.h"
      27             : #include <microhttpd.h>
      28             : #include <gnunet/gnunet_curl_lib.h>
      29             : #include "taler_signatures.h"
      30             : #include "taler_extensions.h"
      31             : #include "taler_testing_lib.h"
      32             : 
      33             : /**
      34             :  * Information we track per withdrawn coin.
      35             :  */
      36             : struct CoinState
      37             : {
      38             : 
      39             :   /**
      40             :    * String describing the denomination value we should withdraw.
      41             :    * A corresponding denomination key must exist in the exchange's
      42             :    * offerings.  Can be NULL if @e pk is set instead.
      43             :    */
      44             :   struct TALER_Amount amount;
      45             : 
      46             :   /**
      47             :    * If @e amount is NULL, this specifies the denomination key to
      48             :    * use.  Otherwise, this will be set (by the interpreter) to the
      49             :    * denomination PK matching @e amount.
      50             :    */
      51             :   struct TALER_EXCHANGE_DenomPublicKey *pk;
      52             : 
      53             :   /**
      54             :    * Private key of the coin.
      55             :    */
      56             :   struct TALER_CoinSpendPrivateKeyP coin_priv;
      57             : 
      58             :   /**
      59             :    * Blinding key used during the operation.
      60             :    */
      61             :   union TALER_DenominationBlindingKeyP bks;
      62             : 
      63             :   /**
      64             :    * Values contributed from the exchange during the
      65             :    * withdraw protocol.
      66             :    */
      67             :   struct TALER_ExchangeWithdrawValues exchange_vals;
      68             : 
      69             :   /**
      70             :    * Set (by the interpreter) to the exchange's signature over the
      71             :    * coin's public key.
      72             :    */
      73             :   struct TALER_DenominationSignature sig;
      74             : 
      75             :   /**
      76             :    * Private key material of the coin, set by the interpreter.
      77             :    */
      78             :   struct TALER_PlanchetMasterSecretP ps;
      79             : 
      80             :   /**
      81             :    * If age > 0, put here the corresponding age commitment with its proof and
      82             :    * its hash, respectivelly, NULL otherwise.
      83             :    */
      84             :   struct TALER_AgeCommitmentProof *age_commitment_proof;
      85             :   struct TALER_AgeCommitmentHash *h_age_commitment;
      86             : 
      87             :   /**
      88             :    * Reserve history entry that corresponds to this coin.
      89             :    * Will be of type #TALER_EXCHANGE_RTT_WITHDRAWAL.
      90             :    */
      91             :   struct TALER_EXCHANGE_ReserveHistoryEntry reserve_history;
      92             : 
      93             : 
      94             : };
      95             : 
      96             : 
      97             : /**
      98             :  * State for a "batch withdraw" CMD.
      99             :  */
     100             : struct BatchWithdrawState
     101             : {
     102             : 
     103             :   /**
     104             :    * Which reserve should we withdraw from?
     105             :    */
     106             :   const char *reserve_reference;
     107             : 
     108             :   /**
     109             :    * Exchange base URL.  Only used as offered trait.
     110             :    */
     111             :   char *exchange_url;
     112             : 
     113             :   /**
     114             :    * URI if the reserve we are withdrawing from.
     115             :    */
     116             :   char *reserve_payto_uri;
     117             : 
     118             :   /**
     119             :    * Private key of the reserve we are withdrawing from.
     120             :    */
     121             :   struct TALER_ReservePrivateKeyP reserve_priv;
     122             : 
     123             :   /**
     124             :    * Public key of the reserve we are withdrawing from.
     125             :    */
     126             :   struct TALER_ReservePublicKeyP reserve_pub;
     127             : 
     128             :   /**
     129             :    * Interpreter state (during command).
     130             :    */
     131             :   struct TALER_TESTING_Interpreter *is;
     132             : 
     133             :   /**
     134             :    * Withdraw handle (while operation is running).
     135             :    */
     136             :   struct TALER_EXCHANGE_BatchWithdrawHandle *wsh;
     137             : 
     138             :   /**
     139             :    * Array of coin states.
     140             :    */
     141             :   struct CoinState *coins;
     142             : 
     143             :   /**
     144             :    * Set to the KYC requirement payto hash *if* the exchange replied with a
     145             :    * request for KYC.
     146             :    */
     147             :   struct TALER_PaytoHashP h_payto;
     148             : 
     149             :   /**
     150             :    * Set to the KYC requirement row *if* the exchange replied with
     151             :    * a request for KYC.
     152             :    */
     153             :   uint64_t requirement_row;
     154             : 
     155             :   /**
     156             :    * Length of the @e coins array.
     157             :    */
     158             :   unsigned int num_coins;
     159             : 
     160             :   /**
     161             :    * Expected HTTP response code to the request.
     162             :    */
     163             :   unsigned int expected_response_code;
     164             : 
     165             :   /**
     166             :    * An age > 0 signifies age restriction is required.
     167             :    * Same for all coins in the batch.
     168             :    */
     169             :   uint8_t age;
     170             : };
     171             : 
     172             : 
     173             : /**
     174             :  * "batch withdraw" operation callback; checks that the
     175             :  * response code is expected and store the exchange signature
     176             :  * in the state.
     177             :  *
     178             :  * @param cls closure.
     179             :  * @param wr withdraw response details
     180             :  */
     181             : static void
     182           0 : reserve_batch_withdraw_cb (void *cls,
     183             :                            const struct
     184             :                            TALER_EXCHANGE_BatchWithdrawResponse *wr)
     185             : {
     186           0 :   struct BatchWithdrawState *ws = cls;
     187           0 :   struct TALER_TESTING_Interpreter *is = ws->is;
     188             : 
     189           0 :   ws->wsh = NULL;
     190           0 :   if (ws->expected_response_code != wr->hr.http_status)
     191             :   {
     192           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     193             :                 "Unexpected response code %u/%d to command %s in %s:%u\n",
     194             :                 wr->hr.http_status,
     195             :                 (int) wr->hr.ec,
     196             :                 TALER_TESTING_interpreter_get_current_label (is),
     197             :                 __FILE__,
     198             :                 __LINE__);
     199           0 :     json_dumpf (wr->hr.reply,
     200             :                 stderr,
     201             :                 0);
     202           0 :     GNUNET_break (0);
     203           0 :     TALER_TESTING_interpreter_fail (is);
     204           0 :     return;
     205             :   }
     206           0 :   switch (wr->hr.http_status)
     207             :   {
     208           0 :   case MHD_HTTP_OK:
     209           0 :     for (unsigned int i = 0; i<ws->num_coins; i++)
     210             :     {
     211           0 :       struct CoinState *cs = &ws->coins[i];
     212           0 :       const struct TALER_EXCHANGE_PrivateCoinDetails *pcd
     213           0 :         = &wr->details.success.coins[i];
     214             : 
     215           0 :       TALER_denom_sig_deep_copy (&cs->sig,
     216             :                                  &pcd->sig);
     217           0 :       cs->coin_priv = pcd->coin_priv;
     218           0 :       cs->bks = pcd->bks;
     219           0 :       cs->exchange_vals = pcd->exchange_vals;
     220             :     }
     221           0 :     break;
     222           0 :   case MHD_HTTP_FORBIDDEN:
     223             :     /* nothing to check */
     224           0 :     break;
     225           0 :   case MHD_HTTP_NOT_FOUND:
     226             :     /* nothing to check */
     227           0 :     break;
     228           0 :   case MHD_HTTP_CONFLICT:
     229             :     /* nothing to check */
     230           0 :     break;
     231           0 :   case MHD_HTTP_GONE:
     232             :     /* theoretically could check that the key was actually */
     233           0 :     break;
     234           0 :   case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
     235             :     /* nothing to check */
     236             :     ws->requirement_row
     237           0 :       = wr->details.unavailable_for_legal_reasons.requirement_row;
     238             :     ws->h_payto
     239           0 :       = wr->details.unavailable_for_legal_reasons.h_payto;
     240           0 :     break;
     241           0 :   default:
     242             :     /* Unsupported status code (by test harness) */
     243           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     244             :                 "Batch withdraw test command does not support status code %u\n",
     245             :                 wr->hr.http_status);
     246           0 :     GNUNET_break (0);
     247           0 :     break;
     248             :   }
     249           0 :   TALER_TESTING_interpreter_next (is);
     250             : }
     251             : 
     252             : 
     253             : /**
     254             :  * Run the command.
     255             :  */
     256             : static void
     257           0 : batch_withdraw_run (void *cls,
     258             :                     const struct TALER_TESTING_Command *cmd,
     259             :                     struct TALER_TESTING_Interpreter *is)
     260           0 : {
     261           0 :   struct BatchWithdrawState *ws = cls;
     262             :   const struct TALER_ReservePrivateKeyP *rp;
     263             :   const struct TALER_TESTING_Command *create_reserve;
     264             :   const struct TALER_EXCHANGE_DenomPublicKey *dpk;
     265           0 :   struct TALER_EXCHANGE_WithdrawCoinInput wcis[ws->num_coins];
     266             : 
     267             :   (void) cmd;
     268           0 :   ws->is = is;
     269             :   create_reserve
     270           0 :     = TALER_TESTING_interpreter_lookup_command (
     271             :         is,
     272             :         ws->reserve_reference);
     273             : 
     274           0 :   if (NULL == create_reserve)
     275             :   {
     276           0 :     GNUNET_break (0);
     277           0 :     TALER_TESTING_interpreter_fail (is);
     278           0 :     return;
     279             :   }
     280           0 :   if (GNUNET_OK !=
     281           0 :       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           0 :   if (NULL == ws->exchange_url)
     289             :     ws->exchange_url
     290           0 :       = GNUNET_strdup (TALER_EXCHANGE_get_base_url (is->exchange));
     291           0 :   ws->reserve_priv = *rp;
     292           0 :   GNUNET_CRYPTO_eddsa_key_get_public (&ws->reserve_priv.eddsa_priv,
     293             :                                       &ws->reserve_pub.eddsa_pub);
     294             :   ws->reserve_payto_uri
     295           0 :     = TALER_reserve_make_payto (ws->exchange_url,
     296           0 :                                 &ws->reserve_pub);
     297             : 
     298           0 :   for (unsigned int i = 0; i<ws->num_coins; i++)
     299             :   {
     300           0 :     struct CoinState *cs = &ws->coins[i];
     301           0 :     struct TALER_EXCHANGE_WithdrawCoinInput *wci = &wcis[i];
     302             : 
     303           0 :     TALER_planchet_master_setup_random (&cs->ps);
     304           0 :     dpk = TALER_TESTING_find_pk (TALER_EXCHANGE_get_keys (is->exchange),
     305           0 :                                  &cs->amount,
     306           0 :                                  ws->age > 0);
     307           0 :     if (NULL == dpk)
     308             :     {
     309           0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     310             :                   "Failed to determine denomination key at %s\n",
     311             :                   (NULL != cmd) ? cmd->label : "<retried command>");
     312           0 :       GNUNET_break (0);
     313           0 :       TALER_TESTING_interpreter_fail (is);
     314           0 :       return;
     315             :     }
     316             :     /* We copy the denomination key, as re-querying /keys
     317             :      * would free the old one. */
     318           0 :     cs->pk = TALER_EXCHANGE_copy_denomination_key (dpk);
     319           0 :     cs->reserve_history.type = TALER_EXCHANGE_RTT_WITHDRAWAL;
     320           0 :     GNUNET_assert (0 <=
     321             :                    TALER_amount_add (&cs->reserve_history.amount,
     322             :                                      &cs->amount,
     323             :                                      &cs->pk->fees.withdraw));
     324           0 :     cs->reserve_history.details.withdraw.fee = cs->pk->fees.withdraw;
     325             : 
     326           0 :     wci->pk = cs->pk;
     327           0 :     wci->ps = &cs->ps;
     328           0 :     wci->ach = cs->h_age_commitment;
     329             :   }
     330           0 :   ws->wsh = TALER_EXCHANGE_batch_withdraw (is->exchange,
     331             :                                            rp,
     332             :                                            wcis,
     333             :                                            ws->num_coins,
     334             :                                            &reserve_batch_withdraw_cb,
     335             :                                            ws);
     336           0 :   if (NULL == ws->wsh)
     337             :   {
     338           0 :     GNUNET_break (0);
     339           0 :     TALER_TESTING_interpreter_fail (is);
     340           0 :     return;
     341             :   }
     342             : }
     343             : 
     344             : 
     345             : /**
     346             :  * Free the state of a "withdraw" CMD, and possibly cancel
     347             :  * a pending operation thereof.
     348             :  *
     349             :  * @param cls closure.
     350             :  * @param cmd the command being freed.
     351             :  */
     352             : static void
     353           0 : batch_withdraw_cleanup (void *cls,
     354             :                         const struct TALER_TESTING_Command *cmd)
     355             : {
     356           0 :   struct BatchWithdrawState *ws = cls;
     357             : 
     358           0 :   if (NULL != ws->wsh)
     359             :   {
     360           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     361             :                 "Command %s did not complete\n",
     362             :                 cmd->label);
     363           0 :     TALER_EXCHANGE_batch_withdraw_cancel (ws->wsh);
     364           0 :     ws->wsh = NULL;
     365             :   }
     366           0 :   for (unsigned int i = 0; i<ws->num_coins; i++)
     367             :   {
     368           0 :     struct CoinState *cs = &ws->coins[i];
     369             : 
     370           0 :     TALER_denom_sig_free (&cs->sig);
     371           0 :     if (NULL != cs->pk)
     372             :     {
     373           0 :       TALER_EXCHANGE_destroy_denomination_key (cs->pk);
     374           0 :       cs->pk = NULL;
     375             :     }
     376           0 :     if (NULL != cs->age_commitment_proof)
     377             :     {
     378           0 :       TALER_age_commitment_proof_free (cs->age_commitment_proof);
     379           0 :       cs->age_commitment_proof = NULL;
     380             :     }
     381           0 :     if (NULL != cs->h_age_commitment)
     382           0 :       GNUNET_free (cs->h_age_commitment);
     383             :   }
     384           0 :   GNUNET_free (ws->coins);
     385           0 :   GNUNET_free (ws->exchange_url);
     386           0 :   GNUNET_free (ws->reserve_payto_uri);
     387           0 :   GNUNET_free (ws);
     388           0 : }
     389             : 
     390             : 
     391             : /**
     392             :  * Offer internal data to a "withdraw" CMD state to other
     393             :  * commands.
     394             :  *
     395             :  * @param cls closure
     396             :  * @param[out] ret result (could be anything)
     397             :  * @param trait name of the trait
     398             :  * @param index index number of the object to offer.
     399             :  * @return #GNUNET_OK on success
     400             :  */
     401             : static enum GNUNET_GenericReturnValue
     402           0 : batch_withdraw_traits (void *cls,
     403             :                        const void **ret,
     404             :                        const char *trait,
     405             :                        unsigned int index)
     406             : {
     407           0 :   struct BatchWithdrawState *ws = cls;
     408           0 :   struct CoinState *cs = &ws->coins[index];
     409             :   struct TALER_TESTING_Trait traits[] = {
     410             :     /* history entry MUST be first due to response code logic below! */
     411           0 :     TALER_TESTING_make_trait_reserve_history (index,
     412           0 :                                               &cs->reserve_history),
     413           0 :     TALER_TESTING_make_trait_coin_priv (index,
     414           0 :                                         &cs->coin_priv),
     415           0 :     TALER_TESTING_make_trait_planchet_secrets (index,
     416           0 :                                                &cs->ps),
     417           0 :     TALER_TESTING_make_trait_blinding_key (index,
     418           0 :                                            &cs->bks),
     419           0 :     TALER_TESTING_make_trait_exchange_wd_value (index,
     420           0 :                                                 &cs->exchange_vals),
     421           0 :     TALER_TESTING_make_trait_denom_pub (index,
     422           0 :                                         cs->pk),
     423           0 :     TALER_TESTING_make_trait_denom_sig (index,
     424           0 :                                         &cs->sig),
     425           0 :     TALER_TESTING_make_trait_reserve_priv (&ws->reserve_priv),
     426           0 :     TALER_TESTING_make_trait_reserve_pub (&ws->reserve_pub),
     427           0 :     TALER_TESTING_make_trait_amounts (index,
     428           0 :                                       &cs->amount),
     429           0 :     TALER_TESTING_make_trait_legi_requirement_row (&ws->requirement_row),
     430           0 :     TALER_TESTING_make_trait_h_payto (
     431           0 :       &ws->h_payto),
     432           0 :     TALER_TESTING_make_trait_payto_uri (
     433           0 :       (const char **) &ws->reserve_payto_uri),
     434           0 :     TALER_TESTING_make_trait_exchange_url (
     435           0 :       (const char **) &ws->exchange_url),
     436           0 :     TALER_TESTING_make_trait_age_commitment_proof (index,
     437           0 :                                                    cs->age_commitment_proof),
     438           0 :     TALER_TESTING_make_trait_h_age_commitment (index,
     439           0 :                                                cs->h_age_commitment),
     440           0 :     TALER_TESTING_trait_end ()
     441             :   };
     442             : 
     443           0 :   if (index >= ws->num_coins)
     444           0 :     return GNUNET_NO;
     445           0 :   return TALER_TESTING_get_trait ((ws->expected_response_code == MHD_HTTP_OK)
     446             :                                   ? &traits[0]   /* we have reserve history */
     447             :                                   : &traits[1],  /* skip reserve history */
     448             :                                   ret,
     449             :                                   trait,
     450             :                                   index);
     451             : }
     452             : 
     453             : 
     454             : struct TALER_TESTING_Command
     455           0 : TALER_TESTING_cmd_batch_withdraw (const char *label,
     456             :                                   const char *reserve_reference,
     457             :                                   uint8_t age,
     458             :                                   unsigned int expected_response_code,
     459             :                                   const char *amount,
     460             :                                   ...)
     461             : {
     462             :   struct BatchWithdrawState *ws;
     463             :   unsigned int cnt;
     464             :   va_list ap;
     465             : 
     466           0 :   ws = GNUNET_new (struct BatchWithdrawState);
     467           0 :   ws->age = age;
     468           0 :   ws->reserve_reference = reserve_reference;
     469           0 :   ws->expected_response_code = expected_response_code;
     470             : 
     471           0 :   cnt = 1;
     472           0 :   va_start (ap, amount);
     473           0 :   while (NULL != (va_arg (ap, const char *)))
     474           0 :     cnt++;
     475           0 :   ws->num_coins = cnt;
     476           0 :   ws->coins = GNUNET_new_array (cnt,
     477             :                                 struct CoinState);
     478           0 :   va_end (ap);
     479           0 :   va_start (ap, amount);
     480           0 :   for (unsigned int i = 0; i<ws->num_coins; i++)
     481             :   {
     482           0 :     struct CoinState *cs = &ws->coins[i];
     483             : 
     484           0 :     if (0 < age)
     485             :     {
     486             :       struct TALER_AgeCommitmentProof *acp;
     487             :       struct TALER_AgeCommitmentHash *hac;
     488             :       struct GNUNET_HashCode seed;
     489             :       struct TALER_AgeMask mask;
     490             : 
     491           0 :       acp = GNUNET_new (struct TALER_AgeCommitmentProof);
     492           0 :       hac = GNUNET_new (struct TALER_AgeCommitmentHash);
     493           0 :       mask = TALER_extensions_age_restriction_ageMask ();
     494           0 :       GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
     495             :                                   &seed,
     496             :                                   sizeof(seed));
     497             : 
     498           0 :       if (GNUNET_OK !=
     499           0 :           TALER_age_restriction_commit (
     500             :             &mask,
     501             :             age,
     502             :             &seed,
     503             :             acp))
     504             :       {
     505           0 :         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     506             :                     "Failed to generate age commitment for age %d at %s\n",
     507             :                     age,
     508             :                     label);
     509           0 :         GNUNET_assert (0);
     510             :       }
     511             : 
     512           0 :       TALER_age_commitment_hash (&acp->commitment,
     513             :                                  hac);
     514           0 :       cs->age_commitment_proof = acp;
     515           0 :       cs->h_age_commitment = hac;
     516             :     }
     517             : 
     518           0 :     if (GNUNET_OK !=
     519           0 :         TALER_string_to_amount (amount,
     520             :                                 &cs->amount))
     521             :     {
     522           0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     523             :                   "Failed to parse amount `%s' at %s\n",
     524             :                   amount,
     525             :                   label);
     526           0 :       GNUNET_assert (0);
     527             :     }
     528             :     /* move on to next vararg! */
     529           0 :     amount = va_arg (ap, const char *);
     530             :   }
     531           0 :   GNUNET_assert (NULL == amount);
     532           0 :   va_end (ap);
     533             : 
     534             :   {
     535           0 :     struct TALER_TESTING_Command cmd = {
     536             :       .cls = ws,
     537             :       .label = label,
     538             :       .run = &batch_withdraw_run,
     539             :       .cleanup = &batch_withdraw_cleanup,
     540             :       .traits = &batch_withdraw_traits
     541             :     };
     542             : 
     543           0 :     return cmd;
     544             :   }
     545             : }
     546             : 
     547             : 
     548             : /* end of testing_api_cmd_batch_withdraw.c */

Generated by: LCOV version 1.14