LCOV - code coverage report
Current view: top level - testing - testing_api_cmd_withdraw.c (source / functions) Hit Total Coverage
Test: GNU Taler exchange coverage report Lines: 105 200 52.5 %
Date: 2021-08-30 06:43:37 Functions: 7 10 70.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   Copyright (C) 2018-2020 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_withdraw.c
      21             :  * @brief main interpreter loop for testcases
      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_testing_lib.h"
      31             : #include "backoff.h"
      32             : 
      33             : 
      34             : /**
      35             :  * How often do we retry before giving up?
      36             :  */
      37             : #define NUM_RETRIES 15
      38             : 
      39             : /**
      40             :  * How long do we wait AT LEAST if the exchange says the reserve is unknown?
      41             :  */
      42             : #define UNKNOWN_MIN_BACKOFF GNUNET_TIME_relative_multiply ( \
      43             :     GNUNET_TIME_UNIT_MILLISECONDS, 10)
      44             : 
      45             : /**
      46             :  * How long do we wait AT MOST if the exchange says the reserve is unknown?
      47             :  */
      48             : #define UNKNOWN_MAX_BACKOFF GNUNET_TIME_relative_multiply ( \
      49             :     GNUNET_TIME_UNIT_MILLISECONDS, 100)
      50             : 
      51             : /**
      52             :  * State for a "withdraw" CMD.
      53             :  */
      54             : struct WithdrawState
      55             : {
      56             : 
      57             :   /**
      58             :    * Which reserve should we withdraw from?
      59             :    */
      60             :   const char *reserve_reference;
      61             : 
      62             :   /**
      63             :    * Reference to a withdraw or reveal operation from which we should
      64             :    * re-use the private coin key, or NULL for regular withdrawal.
      65             :    */
      66             :   const char *reuse_coin_key_ref;
      67             : 
      68             :   /**
      69             :    * String describing the denomination value we should withdraw.
      70             :    * A corresponding denomination key must exist in the exchange's
      71             :    * offerings.  Can be NULL if @e pk is set instead.
      72             :    */
      73             :   struct TALER_Amount amount;
      74             : 
      75             :   /**
      76             :    * If @e amount is NULL, this specifies the denomination key to
      77             :    * use.  Otherwise, this will be set (by the interpreter) to the
      78             :    * denomination PK matching @e amount.
      79             :    */
      80             :   struct TALER_EXCHANGE_DenomPublicKey *pk;
      81             : 
      82             :   /**
      83             :    * Exchange base URL.  Only used as offered trait.
      84             :    */
      85             :   char *exchange_url;
      86             : 
      87             :   /**
      88             :    * Interpreter state (during command).
      89             :    */
      90             :   struct TALER_TESTING_Interpreter *is;
      91             : 
      92             :   /**
      93             :    * Set (by the interpreter) to the exchange's signature over the
      94             :    * coin's public key.
      95             :    */
      96             :   struct TALER_DenominationSignature sig;
      97             : 
      98             :   /**
      99             :    * Private key material of the coin, set by the interpreter.
     100             :    */
     101             :   struct TALER_PlanchetSecretsP ps;
     102             : 
     103             :   /**
     104             :    * Reserve history entry that corresponds to this operation.
     105             :    * Will be of type #TALER_EXCHANGE_RTT_WITHDRAWAL.
     106             :    */
     107             :   struct TALER_EXCHANGE_ReserveHistory reserve_history;
     108             : 
     109             :   /**
     110             :    * Withdraw handle (while operation is running).
     111             :    */
     112             :   struct TALER_EXCHANGE_WithdrawHandle *wsh;
     113             : 
     114             :   /**
     115             :    * Task scheduled to try later.
     116             :    */
     117             :   struct GNUNET_SCHEDULER_Task *retry_task;
     118             : 
     119             :   /**
     120             :    * How long do we wait until we retry?
     121             :    */
     122             :   struct GNUNET_TIME_Relative backoff;
     123             : 
     124             :   /**
     125             :    * Total withdraw backoff applied.
     126             :    */
     127             :   struct GNUNET_TIME_Relative total_backoff;
     128             : 
     129             :   /**
     130             :    * Expected HTTP response code to the request.
     131             :    */
     132             :   unsigned int expected_response_code;
     133             : 
     134             :   /**
     135             :    * Was this command modified via
     136             :    * #TALER_TESTING_cmd_withdraw_with_retry to
     137             :    * enable retries? How often should we still retry?
     138             :    */
     139             :   unsigned int do_retry;
     140             : };
     141             : 
     142             : 
     143             : /**
     144             :  * Run the command.
     145             :  *
     146             :  * @param cls closure.
     147             :  * @param cmd the commaind being run.
     148             :  * @param is interpreter state.
     149             :  */
     150             : static void
     151             : withdraw_run (void *cls,
     152             :               const struct TALER_TESTING_Command *cmd,
     153             :               struct TALER_TESTING_Interpreter *is);
     154             : 
     155             : 
     156             : /**
     157             :  * Task scheduled to re-try #withdraw_run.
     158             :  *
     159             :  * @param cls a `struct WithdrawState`
     160             :  */
     161             : static void
     162           0 : do_retry (void *cls)
     163             : {
     164           0 :   struct WithdrawState *ws = cls;
     165             : 
     166           0 :   ws->retry_task = NULL;
     167           0 :   ws->is->commands[ws->is->ip].last_req_time
     168           0 :     = GNUNET_TIME_absolute_get ();
     169           0 :   withdraw_run (ws,
     170             :                 NULL,
     171             :                 ws->is);
     172           0 : }
     173             : 
     174             : 
     175             : /**
     176             :  * "reserve withdraw" operation callback; checks that the
     177             :  * response code is expected and store the exchange signature
     178             :  * in the state.
     179             :  *
     180             :  * @param cls closure.
     181             :  * @param hr HTTP response details
     182             :  * @param sig signature over the coin, NULL on error.
     183             :  */
     184             : static void
     185          39 : reserve_withdraw_cb (void *cls,
     186             :                      const struct TALER_EXCHANGE_HttpResponse *hr,
     187             :                      const struct TALER_DenominationSignature *sig)
     188             : {
     189          39 :   struct WithdrawState *ws = cls;
     190          39 :   struct TALER_TESTING_Interpreter *is = ws->is;
     191             : 
     192          39 :   ws->wsh = NULL;
     193          39 :   if (ws->expected_response_code != hr->http_status)
     194             :   {
     195           0 :     if (0 != ws->do_retry)
     196             :     {
     197           0 :       if (TALER_EC_EXCHANGE_WITHDRAW_RESERVE_UNKNOWN != hr->ec)
     198           0 :         ws->do_retry--; /* we don't count reserve unknown as failures here */
     199           0 :       if ( (0 == hr->http_status) ||
     200           0 :            (TALER_EC_GENERIC_DB_SOFT_FAILURE == hr->ec) ||
     201           0 :            (TALER_EC_EXCHANGE_WITHDRAW_INSUFFICIENT_FUNDS == hr->ec) ||
     202           0 :            (TALER_EC_EXCHANGE_WITHDRAW_RESERVE_UNKNOWN == hr->ec) ||
     203           0 :            (MHD_HTTP_INTERNAL_SERVER_ERROR == hr->http_status) )
     204             :       {
     205           0 :         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     206             :                     "Retrying withdraw failed with %u/%d\n",
     207             :                     hr->http_status,
     208             :                     (int) hr->ec);
     209             :         /* on DB conflicts, do not use backoff */
     210           0 :         if (TALER_EC_GENERIC_DB_SOFT_FAILURE == hr->ec)
     211           0 :           ws->backoff = GNUNET_TIME_UNIT_ZERO;
     212           0 :         else if (TALER_EC_EXCHANGE_WITHDRAW_RESERVE_UNKNOWN != hr->ec)
     213           0 :           ws->backoff = EXCHANGE_LIB_BACKOFF (ws->backoff);
     214             :         else
     215           0 :           ws->backoff = GNUNET_TIME_relative_max (UNKNOWN_MIN_BACKOFF,
     216             :                                                   ws->backoff);
     217           0 :         ws->backoff = GNUNET_TIME_relative_min (ws->backoff,
     218             :                                                 UNKNOWN_MAX_BACKOFF);
     219           0 :         ws->total_backoff = GNUNET_TIME_relative_add (ws->total_backoff,
     220             :                                                       ws->backoff);
     221           0 :         ws->is->commands[ws->is->ip].num_tries++;
     222           0 :         ws->retry_task = GNUNET_SCHEDULER_add_delayed (ws->backoff,
     223             :                                                        &do_retry,
     224             :                                                        ws);
     225           0 :         return;
     226             :       }
     227             :     }
     228           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     229             :                 "Unexpected response code %u/%d to command %s in %s:%u\n",
     230             :                 hr->http_status,
     231             :                 (int) hr->ec,
     232             :                 TALER_TESTING_interpreter_get_current_label (is),
     233             :                 __FILE__,
     234             :                 __LINE__);
     235           0 :     json_dumpf (hr->reply,
     236             :                 stderr,
     237             :                 0);
     238           0 :     GNUNET_break (0);
     239           0 :     TALER_TESTING_interpreter_fail (is);
     240           0 :     return;
     241             :   }
     242          39 :   switch (hr->http_status)
     243             :   {
     244          35 :   case MHD_HTTP_OK:
     245          35 :     if (NULL == sig)
     246             :     {
     247           0 :       GNUNET_break (0);
     248           0 :       TALER_TESTING_interpreter_fail (is);
     249           0 :       return;
     250             :     }
     251          70 :     ws->sig.rsa_signature = GNUNET_CRYPTO_rsa_signature_dup (
     252          35 :       sig->rsa_signature);
     253          35 :     if (0 != ws->total_backoff.rel_value_us)
     254             :     {
     255           0 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     256             :                   "Total withdraw backoff for %s was %s\n",
     257             :                   is->commands[is->ip].label,
     258             :                   GNUNET_STRINGS_relative_time_to_string (ws->total_backoff,
     259             :                                                           GNUNET_YES));
     260             :     }
     261          35 :     break;
     262           0 :   case MHD_HTTP_FORBIDDEN:
     263             :     /* nothing to check */
     264           0 :     break;
     265           3 :   case MHD_HTTP_CONFLICT:
     266             :     /* nothing to check */
     267           3 :     break;
     268           1 :   case MHD_HTTP_GONE:
     269             :     /* theoretically could check that the key was actually */
     270           1 :     break;
     271           0 :   case MHD_HTTP_NOT_FOUND:
     272             :     /* nothing to check */
     273           0 :     break;
     274           0 :   default:
     275             :     /* Unsupported status code (by test harness) */
     276           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     277             :                 "Withdraw test command does not support status code %u\n",
     278             :                 hr->http_status);
     279           0 :     GNUNET_break (0);
     280           0 :     break;
     281             :   }
     282          39 :   TALER_TESTING_interpreter_next (is);
     283             : }
     284             : 
     285             : 
     286             : /**
     287             :  * Parser reference to a coin.
     288             :  *
     289             :  * @param coin_reference of format $LABEL['#' $INDEX]?
     290             :  * @param[out] cref where we return a copy of $LABEL
     291             :  * @param[out] idx where we set $INDEX
     292             :  * @return #GNUNET_SYSERR if $INDEX is present but not numeric
     293             :  */
     294             : static int
     295           1 : parse_coin_reference (const char *coin_reference,
     296             :                       char **cref,
     297             :                       unsigned int *idx)
     298             : {
     299             :   const char *index;
     300             : 
     301             :   /* We allow command references of the form "$LABEL#$INDEX" or
     302             :      just "$LABEL", which implies the index is 0. Figure out
     303             :      which one it is. */
     304           1 :   index = strchr (coin_reference, '#');
     305           1 :   if (NULL == index)
     306             :   {
     307           1 :     *idx = 0;
     308           1 :     *cref = GNUNET_strdup (coin_reference);
     309           1 :     return GNUNET_OK;
     310             :   }
     311           0 :   *cref = GNUNET_strndup (coin_reference,
     312             :                           index - coin_reference);
     313           0 :   if (1 != sscanf (index + 1,
     314             :                    "%u",
     315             :                    idx))
     316             :   {
     317           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     318             :                 "Numeric index (not `%s') required after `#' in command reference of command in %s:%u\n",
     319             :                 index,
     320             :                 __FILE__,
     321             :                 __LINE__);
     322           0 :     GNUNET_free (*cref);
     323           0 :     *cref = NULL;
     324           0 :     return GNUNET_SYSERR;
     325             :   }
     326           0 :   return GNUNET_OK;
     327             : }
     328             : 
     329             : 
     330             : /**
     331             :  * Run the command.
     332             :  */
     333             : static void
     334          39 : withdraw_run (void *cls,
     335             :               const struct TALER_TESTING_Command *cmd,
     336             :               struct TALER_TESTING_Interpreter *is)
     337             : {
     338          39 :   struct WithdrawState *ws = cls;
     339             :   const struct TALER_ReservePrivateKeyP *rp;
     340             :   const struct TALER_TESTING_Command *create_reserve;
     341             :   const struct TALER_EXCHANGE_DenomPublicKey *dpk;
     342             : 
     343             :   (void) cmd;
     344             :   create_reserve
     345          39 :     = TALER_TESTING_interpreter_lookup_command (
     346             :         is,
     347             :         ws->reserve_reference);
     348          39 :   if (NULL == create_reserve)
     349             :   {
     350           0 :     GNUNET_break (0);
     351           0 :     TALER_TESTING_interpreter_fail (is);
     352           0 :     return;
     353             :   }
     354          39 :   if (GNUNET_OK !=
     355          39 :       TALER_TESTING_get_trait_reserve_priv (create_reserve,
     356             :                                             0,
     357             :                                             &rp))
     358             :   {
     359           0 :     GNUNET_break (0);
     360           0 :     TALER_TESTING_interpreter_fail (is);
     361           0 :     return;
     362             :   }
     363          39 :   if (NULL == ws->reuse_coin_key_ref)
     364             :   {
     365          38 :     TALER_planchet_setup_random (&ws->ps);
     366             :   }
     367             :   else
     368             :   {
     369             :     const struct TALER_CoinSpendPrivateKeyP *coin_priv;
     370             :     const struct TALER_TESTING_Command *cref;
     371             :     char *cstr;
     372             :     unsigned int index;
     373             : 
     374           1 :     GNUNET_assert (GNUNET_OK ==
     375             :                    parse_coin_reference (ws->reuse_coin_key_ref,
     376             :                                          &cstr,
     377             :                                          &index));
     378           1 :     cref = TALER_TESTING_interpreter_lookup_command (is,
     379             :                                                      cstr);
     380           1 :     GNUNET_assert (NULL != cref);
     381           1 :     GNUNET_free (cstr);
     382           1 :     GNUNET_assert (GNUNET_OK ==
     383             :                    TALER_TESTING_get_trait_coin_priv (cref,
     384             :                                                       index,
     385             :                                                       &coin_priv));
     386           1 :     TALER_planchet_setup_random (&ws->ps);
     387           1 :     ws->ps.coin_priv = *coin_priv;
     388             :   }
     389          39 :   ws->is = is;
     390          39 :   if (NULL == ws->pk)
     391             :   {
     392          39 :     dpk = TALER_TESTING_find_pk (TALER_EXCHANGE_get_keys (is->exchange),
     393          39 :                                  &ws->amount);
     394          39 :     if (NULL == dpk)
     395             :     {
     396           0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     397             :                   "Failed to determine denomination key at %s\n",
     398             :                   (NULL != cmd) ? cmd->label : "<retried command>");
     399           0 :       GNUNET_break (0);
     400           0 :       TALER_TESTING_interpreter_fail (is);
     401           0 :       return;
     402             :     }
     403             :     /* We copy the denomination key, as re-querying /keys
     404             :      * would free the old one. */
     405          39 :     ws->pk = TALER_EXCHANGE_copy_denomination_key (dpk);
     406             :   }
     407             :   else
     408             :   {
     409           0 :     ws->amount = ws->pk->value;
     410             :   }
     411          39 :   ws->reserve_history.type = TALER_EXCHANGE_RTT_WITHDRAWAL;
     412          39 :   GNUNET_assert (0 <=
     413             :                  TALER_amount_add (&ws->reserve_history.amount,
     414             :                                    &ws->amount,
     415             :                                    &ws->pk->fee_withdraw));
     416          39 :   ws->reserve_history.details.withdraw.fee = ws->pk->fee_withdraw;
     417          78 :   ws->wsh = TALER_EXCHANGE_withdraw (is->exchange,
     418          39 :                                      ws->pk,
     419             :                                      rp,
     420          39 :                                      &ws->ps,
     421             :                                      &reserve_withdraw_cb,
     422             :                                      ws);
     423          39 :   if (NULL == ws->wsh)
     424             :   {
     425           0 :     GNUNET_break (0);
     426           0 :     TALER_TESTING_interpreter_fail (is);
     427           0 :     return;
     428             :   }
     429             : }
     430             : 
     431             : 
     432             : /**
     433             :  * Free the state of a "withdraw" CMD, and possibly cancel
     434             :  * a pending operation thereof.
     435             :  *
     436             :  * @param cls closure.
     437             :  * @param cmd the command being freed.
     438             :  */
     439             : static void
     440          39 : withdraw_cleanup (void *cls,
     441             :                   const struct TALER_TESTING_Command *cmd)
     442             : {
     443          39 :   struct WithdrawState *ws = cls;
     444             : 
     445          39 :   if (NULL != ws->wsh)
     446             :   {
     447           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     448             :                 "Command %s did not complete\n",
     449             :                 cmd->label);
     450           0 :     TALER_EXCHANGE_withdraw_cancel (ws->wsh);
     451           0 :     ws->wsh = NULL;
     452             :   }
     453          39 :   if (NULL != ws->retry_task)
     454             :   {
     455           0 :     GNUNET_SCHEDULER_cancel (ws->retry_task);
     456           0 :     ws->retry_task = NULL;
     457             :   }
     458          39 :   if (NULL != ws->sig.rsa_signature)
     459             :   {
     460          35 :     GNUNET_CRYPTO_rsa_signature_free (ws->sig.rsa_signature);
     461          35 :     ws->sig.rsa_signature = NULL;
     462             :   }
     463          39 :   if (NULL != ws->pk)
     464             :   {
     465          39 :     TALER_EXCHANGE_destroy_denomination_key (ws->pk);
     466          39 :     ws->pk = NULL;
     467             :   }
     468          39 :   GNUNET_free (ws->exchange_url);
     469          39 :   GNUNET_free (ws);
     470          39 : }
     471             : 
     472             : 
     473             : /**
     474             :  * Offer internal data to a "withdraw" CMD state to other
     475             :  * commands.
     476             :  *
     477             :  * @param cls closure
     478             :  * @param[out] ret result (could be anything)
     479             :  * @param trait name of the trait
     480             :  * @param index index number of the object to offer.
     481             :  * @return #GNUNET_OK on success
     482             :  */
     483             : static int
     484         458 : withdraw_traits (void *cls,
     485             :                  const void **ret,
     486             :                  const char *trait,
     487             :                  unsigned int index)
     488             : {
     489         458 :   struct WithdrawState *ws = cls;
     490             :   const struct TALER_TESTING_Command *reserve_cmd;
     491             :   const struct TALER_ReservePrivateKeyP *reserve_priv;
     492             :   const struct TALER_ReservePublicKeyP *reserve_pub;
     493             : 
     494             :   /* We offer the reserve key where these coins were withdrawn
     495             :    * from. */
     496         458 :   reserve_cmd = TALER_TESTING_interpreter_lookup_command (ws->is,
     497             :                                                           ws->reserve_reference);
     498             : 
     499         458 :   if (NULL == reserve_cmd)
     500             :   {
     501           0 :     GNUNET_break (0);
     502           0 :     TALER_TESTING_interpreter_fail (ws->is);
     503           0 :     return GNUNET_SYSERR;
     504             :   }
     505             : 
     506         458 :   if (GNUNET_OK !=
     507         458 :       TALER_TESTING_get_trait_reserve_priv (reserve_cmd,
     508             :                                             0,
     509             :                                             &reserve_priv))
     510             :   {
     511           0 :     GNUNET_break (0);
     512           0 :     TALER_TESTING_interpreter_fail (ws->is);
     513           0 :     return GNUNET_SYSERR;
     514             :   }
     515         458 :   if (GNUNET_OK !=
     516         458 :       TALER_TESTING_get_trait_reserve_pub (reserve_cmd,
     517             :                                            0,
     518             :                                            &reserve_pub))
     519             :   {
     520           0 :     GNUNET_break (0);
     521           0 :     TALER_TESTING_interpreter_fail (ws->is);
     522           0 :     return GNUNET_SYSERR;
     523             :   }
     524         458 :   if (NULL == ws->exchange_url)
     525             :     ws->exchange_url
     526          36 :       = GNUNET_strdup (TALER_EXCHANGE_get_base_url (ws->is->exchange));
     527             :   {
     528             :     struct TALER_TESTING_Trait traits[] = {
     529             :       /* history entry MUST be first due to response code logic below! */
     530         458 :       TALER_TESTING_make_trait_reserve_history (0,
     531         458 :                                                 &ws->reserve_history),
     532         458 :       TALER_TESTING_make_trait_coin_priv (0 /* only one coin */,
     533         458 :                                           &ws->ps.coin_priv),
     534         458 :       TALER_TESTING_make_trait_blinding_key (0 /* only one coin */,
     535         458 :                                              &ws->ps.blinding_key),
     536         458 :       TALER_TESTING_make_trait_denom_pub (0 /* only one coin */,
     537         458 :                                           ws->pk),
     538         458 :       TALER_TESTING_make_trait_denom_sig (0 /* only one coin */,
     539         458 :                                           &ws->sig),
     540         458 :       TALER_TESTING_make_trait_reserve_priv (0,
     541             :                                              reserve_priv),
     542         458 :       TALER_TESTING_make_trait_reserve_pub (0,
     543             :                                             reserve_pub),
     544         458 :       TALER_TESTING_make_trait_amount_obj (0,
     545         458 :                                            &ws->amount),
     546         458 :       TALER_TESTING_make_trait_url (TALER_TESTING_UT_EXCHANGE_BASE_URL,
     547         458 :                                     ws->exchange_url),
     548         458 :       TALER_TESTING_trait_end ()
     549             :     };
     550             : 
     551         458 :     return TALER_TESTING_get_trait ((ws->expected_response_code == MHD_HTTP_OK)
     552             :                                     ? &traits[0] /* we have reserve history */
     553             :                                     : &traits[1],/* skip reserve history */
     554             :                                     ret,
     555             :                                     trait,
     556             :                                     index);
     557             :   }
     558             : }
     559             : 
     560             : 
     561             : /**
     562             :  * Create a withdraw command, letting the caller specify
     563             :  * the desired amount as string.
     564             :  *
     565             :  * @param label command label.
     566             :  * @param reserve_reference command providing us with a reserve to withdraw from
     567             :  * @param amount how much we withdraw.
     568             :  * @param expected_response_code which HTTP response code
     569             :  *        we expect from the exchange.
     570             :  * @return the withdraw command to be executed by the interpreter.
     571             :  */
     572             : struct TALER_TESTING_Command
     573          39 : TALER_TESTING_cmd_withdraw_amount (const char *label,
     574             :                                    const char *reserve_reference,
     575             :                                    const char *amount,
     576             :                                    unsigned int expected_response_code)
     577             : {
     578             :   struct WithdrawState *ws;
     579             : 
     580          39 :   ws = GNUNET_new (struct WithdrawState);
     581          39 :   ws->reserve_reference = reserve_reference;
     582          39 :   if (GNUNET_OK !=
     583          39 :       TALER_string_to_amount (amount,
     584             :                               &ws->amount))
     585             :   {
     586           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     587             :                 "Failed to parse amount `%s' at %s\n",
     588             :                 amount,
     589             :                 label);
     590           0 :     GNUNET_assert (0);
     591             :   }
     592          39 :   ws->expected_response_code = expected_response_code;
     593             :   {
     594          39 :     struct TALER_TESTING_Command cmd = {
     595             :       .cls = ws,
     596             :       .label = label,
     597             :       .run = &withdraw_run,
     598             :       .cleanup = &withdraw_cleanup,
     599             :       .traits = &withdraw_traits
     600             :     };
     601             : 
     602          39 :     return cmd;
     603             :   }
     604             : }
     605             : 
     606             : 
     607             : /**
     608             :  * Create a withdraw command, letting the caller specify
     609             :  * the desired amount as string and also re-using an existing
     610             :  * coin private key in the process (violating the specification,
     611             :  * which will result in an error when spending the coin!).
     612             :  *
     613             :  * @param label command label.
     614             :  * @param reserve_reference command providing us with a reserve to withdraw from
     615             :  * @param amount how much we withdraw.
     616             :  * @param coin_ref reference to (withdraw/reveal) command of a coin
     617             :  *        from which we should re-use the private key
     618             :  * @param expected_response_code which HTTP response code
     619             :  *        we expect from the exchange.
     620             :  * @return the withdraw command to be executed by the interpreter.
     621             :  */
     622             : struct TALER_TESTING_Command
     623           1 : TALER_TESTING_cmd_withdraw_amount_reuse_key (
     624             :   const char *label,
     625             :   const char *reserve_reference,
     626             :   const char *amount,
     627             :   const char *coin_ref,
     628             :   unsigned int expected_response_code)
     629             : {
     630             :   struct TALER_TESTING_Command cmd;
     631             : 
     632           1 :   cmd = TALER_TESTING_cmd_withdraw_amount (label,
     633             :                                            reserve_reference,
     634             :                                            amount,
     635             :                                            expected_response_code);
     636             :   {
     637           1 :     struct WithdrawState *ws = cmd.cls;
     638             : 
     639           1 :     ws->reuse_coin_key_ref = coin_ref;
     640             :   }
     641           1 :   return cmd;
     642             : }
     643             : 
     644             : 
     645             : /**
     646             :  * Create withdraw command, letting the caller specify the
     647             :  * amount by a denomination key.
     648             :  *
     649             :  * @param label command label.
     650             :  * @param reserve_reference reference to the reserve to withdraw
     651             :  *        from; will provide reserve priv to sign the request.
     652             :  * @param dk denomination public key.
     653             :  * @param expected_response_code expected HTTP response code.
     654             :  *
     655             :  * @return the command.
     656             :  */
     657             : struct TALER_TESTING_Command
     658           0 : TALER_TESTING_cmd_withdraw_denomination (
     659             :   const char *label,
     660             :   const char *reserve_reference,
     661             :   const struct TALER_EXCHANGE_DenomPublicKey *dk,
     662             :   unsigned int expected_response_code)
     663             : {
     664             :   struct WithdrawState *ws;
     665             : 
     666           0 :   if (NULL == dk)
     667             :   {
     668           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     669             :                 "Denomination key not specified at %s\n",
     670             :                 label);
     671           0 :     GNUNET_assert (0);
     672             :   }
     673           0 :   ws = GNUNET_new (struct WithdrawState);
     674           0 :   ws->reserve_reference = reserve_reference;
     675           0 :   ws->pk = TALER_EXCHANGE_copy_denomination_key (dk);
     676           0 :   ws->expected_response_code = expected_response_code;
     677             :   {
     678           0 :     struct TALER_TESTING_Command cmd = {
     679             :       .cls = ws,
     680             :       .label = label,
     681             :       .run = &withdraw_run,
     682             :       .cleanup = &withdraw_cleanup,
     683             :       .traits = &withdraw_traits
     684             :     };
     685             : 
     686           0 :     return cmd;
     687             :   }
     688             : }
     689             : 
     690             : 
     691             : /**
     692             :  * Modify a withdraw command to enable retries when the
     693             :  * reserve is not yet full or we get other transient
     694             :  * errors from the exchange.
     695             :  *
     696             :  * @param cmd a withdraw command
     697             :  * @return the command with retries enabled
     698             :  */
     699             : struct TALER_TESTING_Command
     700           0 : TALER_TESTING_cmd_withdraw_with_retry (struct TALER_TESTING_Command cmd)
     701             : {
     702             :   struct WithdrawState *ws;
     703             : 
     704           0 :   GNUNET_assert (&withdraw_run == cmd.run);
     705           0 :   ws = cmd.cls;
     706           0 :   ws->do_retry = NUM_RETRIES;
     707           0 :   return cmd;
     708             : }
     709             : 
     710             : 
     711             : /* end of testing_api_cmd_withdraw.c */

Generated by: LCOV version 1.14