LCOV - code coverage report
Current view: top level - testing - testing_api_cmd_refresh.c (source / functions) Hit Total Coverage
Test: GNU Taler exchange coverage report Lines: 0 467 0.0 %
Date: 2022-08-25 06:15:09 Functions: 0 22 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_refresh.c
      21             :  * @brief commands for testing all "refresh" features.
      22             :  * @author Marcello Stanisci
      23             :  */
      24             : #include "platform.h"
      25             : #include "taler_json_lib.h"
      26             : #include <gnunet/gnunet_curl_lib.h>
      27             : #include "taler_testing_lib.h"
      28             : #include "taler_signatures.h"
      29             : #include "backoff.h"
      30             : 
      31             : /**
      32             :  * How long do we wait AT MOST when retrying?
      33             :  */
      34             : #define MAX_BACKOFF GNUNET_TIME_relative_multiply ( \
      35             :     GNUNET_TIME_UNIT_MILLISECONDS, 100)
      36             : 
      37             : /**
      38             :  * How often do we retry before giving up?
      39             :  */
      40             : #define NUM_RETRIES 5
      41             : 
      42             : /**
      43             :  * How long do we wait AT MOST when retrying?
      44             :  */
      45             : #define MAX_BACKOFF GNUNET_TIME_relative_multiply ( \
      46             :     GNUNET_TIME_UNIT_MILLISECONDS, 100)
      47             : 
      48             : /**
      49             :  * Information about a fresh coin generated by the refresh
      50             :  * operation.
      51             :  */
      52             : struct TALER_TESTING_FreshCoinData
      53             : {
      54             : 
      55             :   /**
      56             :    * If @e amount is NULL, this specifies the denomination key to
      57             :    * use.  Otherwise, this will be set (by the interpreter) to the
      58             :    * denomination PK matching @e amount.
      59             :    */
      60             :   const struct TALER_EXCHANGE_DenomPublicKey *pk;
      61             : 
      62             :   /**
      63             :    * Set (by the interpreter) to the exchange's signature over the
      64             :    * coin's public key.
      65             :    */
      66             :   struct TALER_DenominationSignature sig;
      67             : 
      68             :   /**
      69             :    * Set (by the interpreter) to the coin's private key.
      70             :    */
      71             :   struct TALER_CoinSpendPrivateKeyP coin_priv;
      72             : 
      73             :   /*
      74             :    * Fresh age commitment for the coin with proof and its hash, NULL if not
      75             :    * applicable.
      76             :    */
      77             :   struct TALER_AgeCommitmentProof *age_commitment_proof;
      78             :   struct TALER_AgeCommitmentHash *h_age_commitment;
      79             : 
      80             :   /**
      81             :    * The blinding key (needed for recoup operations).
      82             :    */
      83             :   union TALER_DenominationBlindingKeyP blinding_key;
      84             : 
      85             : };
      86             : 
      87             : 
      88             : /**
      89             :  * State for a "refresh melt" command.
      90             :  */
      91             : struct RefreshMeltState
      92             : {
      93             : 
      94             :   /**
      95             :    * Reference to reserve_withdraw operations for coin to
      96             :    * be used for the /refresh/melt operation.
      97             :    */
      98             :   const char *coin_reference;
      99             : 
     100             :   /**
     101             :    * Data used in the refresh operation.
     102             :    */
     103             :   struct TALER_EXCHANGE_RefreshData refresh_data;
     104             : 
     105             :   /**
     106             :    * Reference to a previous melt command.
     107             :    */
     108             :   const char *melt_reference;
     109             : 
     110             :   /**
     111             :    * Melt handle while operation is running.
     112             :    */
     113             :   struct TALER_EXCHANGE_MeltHandle *rmh;
     114             : 
     115             :   /**
     116             :    * Interpreter state.
     117             :    */
     118             :   struct TALER_TESTING_Interpreter *is;
     119             : 
     120             :   /**
     121             :    * Array of the denomination public keys
     122             :    * corresponding to the @e num_fresh_coins;
     123             :    */
     124             :   struct TALER_EXCHANGE_DenomPublicKey *fresh_pks;
     125             : 
     126             :   /**
     127             :    * Array of @e num_fresh_coins of results from
     128             :    * the melt operation.
     129             :    */
     130             :   struct TALER_EXCHANGE_MeltBlindingDetail *mbds;
     131             : 
     132             :   /**
     133             :    * Entropy seed for the refresh-melt operation.
     134             :    */
     135             :   struct TALER_RefreshMasterSecretP rms;
     136             : 
     137             :   /**
     138             :    * Private key of the dirty coin being melted.
     139             :    */
     140             :   const struct TALER_CoinSpendPrivateKeyP *melt_priv;
     141             : 
     142             :   /**
     143             :    * Task scheduled to try later.
     144             :    */
     145             :   struct GNUNET_SCHEDULER_Task *retry_task;
     146             : 
     147             :   /**
     148             :    * How long do we wait until we retry?
     149             :    */
     150             :   struct GNUNET_TIME_Relative backoff;
     151             : 
     152             :   /**
     153             :    * How long did we wait in total for retries?
     154             :    */
     155             :   struct GNUNET_TIME_Relative total_backoff;
     156             : 
     157             :   /**
     158             :    * Amounts to be generated during melt.
     159             :    */
     160             :   const char **melt_fresh_amounts;
     161             : 
     162             :   /**
     163             :    * Number of fresh coins generated by the melt.
     164             :    */
     165             :   unsigned int num_fresh_coins;
     166             : 
     167             :   /**
     168             :    * Expected HTTP response code.
     169             :    */
     170             :   unsigned int expected_response_code;
     171             : 
     172             :   /**
     173             :    * if set to #GNUNET_YES, then two /refresh/melt operations
     174             :    * will be performed.  This is needed to trigger the logic
     175             :    * that manages those already-made requests.  Note: it
     176             :    * is not possible to just copy-and-paste a test refresh melt
     177             :    * CMD to have the same effect, because every data preparation
     178             :    * generates new planchets that (in turn) make the whole "hash"
     179             :    * different from any previous one, therefore NOT allowing the
     180             :    * exchange to pick any previous /rerfesh/melt operation from
     181             :    * the database.
     182             :    */
     183             :   bool double_melt;
     184             : 
     185             :   /**
     186             :    * How often should we retry on (transient) failures?
     187             :    */
     188             :   unsigned int do_retry;
     189             : 
     190             :   /**
     191             :    * Set by the melt callback as it comes from the exchange.
     192             :    */
     193             :   uint16_t noreveal_index;
     194             : };
     195             : 
     196             : 
     197             : /**
     198             :  * State for a "refresh reveal" CMD.
     199             :  */
     200             : struct RefreshRevealState
     201             : {
     202             :   /**
     203             :    * Link to a "refresh melt" command.
     204             :    */
     205             :   const char *melt_reference;
     206             : 
     207             :   /**
     208             :    * Reveal handle while operation is running.
     209             :    */
     210             :   struct TALER_EXCHANGE_RefreshesRevealHandle *rrh;
     211             : 
     212             :   /**
     213             :    * Convenience struct to keep in one place all the
     214             :    * data related to one fresh coin, set by the reveal callback
     215             :    * as it comes from the exchange.
     216             :    */
     217             :   struct TALER_TESTING_FreshCoinData *fresh_coins;
     218             : 
     219             :   /**
     220             :    * Array of @e num_fresh_coins planchet secrets derived
     221             :    * from the transfer secret per fresh coin.
     222             :    */
     223             :   struct TALER_PlanchetMasterSecretP *psa;
     224             : 
     225             :   /**
     226             :    * Interpreter state.
     227             :    */
     228             :   struct TALER_TESTING_Interpreter *is;
     229             : 
     230             :   /**
     231             :    * Task scheduled to try later.
     232             :    */
     233             :   struct GNUNET_SCHEDULER_Task *retry_task;
     234             : 
     235             :   /**
     236             :    * How long do we wait until we retry?
     237             :    */
     238             :   struct GNUNET_TIME_Relative backoff;
     239             : 
     240             :   /**
     241             :    * How long did we wait in total for retries?
     242             :    */
     243             :   struct GNUNET_TIME_Relative total_backoff;
     244             : 
     245             :   /**
     246             :    * Number of fresh coins withdrawn, set by the
     247             :    * reveal callback as it comes from the exchange,
     248             :    * it is the length of the @e fresh_coins array.
     249             :    */
     250             :   unsigned int num_fresh_coins;
     251             : 
     252             :   /**
     253             :    * Expected HTTP response code.
     254             :    */
     255             :   unsigned int expected_response_code;
     256             : 
     257             :   /**
     258             :    * How often should we retry on (transient) failures?
     259             :    */
     260             :   unsigned int do_retry;
     261             : 
     262             : };
     263             : 
     264             : 
     265             : /**
     266             :  * State for a "refresh link" CMD.
     267             :  */
     268             : struct RefreshLinkState
     269             : {
     270             :   /**
     271             :    * Link to a "refresh reveal" command.
     272             :    */
     273             :   const char *reveal_reference;
     274             : 
     275             :   /**
     276             :    * Handle to the ongoing operation.
     277             :    */
     278             :   struct TALER_EXCHANGE_LinkHandle *rlh;
     279             : 
     280             :   /**
     281             :    * Interpreter state.
     282             :    */
     283             :   struct TALER_TESTING_Interpreter *is;
     284             : 
     285             :   /**
     286             :    * Task scheduled to try later.
     287             :    */
     288             :   struct GNUNET_SCHEDULER_Task *retry_task;
     289             : 
     290             :   /**
     291             :    * How long do we wait until we retry?
     292             :    */
     293             :   struct GNUNET_TIME_Relative backoff;
     294             : 
     295             :   /**
     296             :    * How long did we wait in total for retries?
     297             :    */
     298             :   struct GNUNET_TIME_Relative total_backoff;
     299             : 
     300             :   /**
     301             :    * Expected HTTP response code.
     302             :    */
     303             :   unsigned int expected_response_code;
     304             : 
     305             :   /**
     306             :    * How often should we retry on (transient) failures?
     307             :    */
     308             :   unsigned int do_retry;
     309             : 
     310             : };
     311             : 
     312             : 
     313             : /**
     314             :  * Run the command.
     315             :  *
     316             :  * @param cls closure.
     317             :  * @param cmd the command to execute.
     318             :  * @param is the interpreter state.
     319             :  */
     320             : static void
     321             : refresh_reveal_run (void *cls,
     322             :                     const struct TALER_TESTING_Command *cmd,
     323             :                     struct TALER_TESTING_Interpreter *is);
     324             : 
     325             : 
     326             : /**
     327             :  * Task scheduled to re-try #refresh_reveal_run.
     328             :  *
     329             :  * @param cls a `struct RefreshRevealState`
     330             :  */
     331             : static void
     332           0 : do_reveal_retry (void *cls)
     333             : {
     334           0 :   struct RefreshRevealState *rrs = cls;
     335             : 
     336           0 :   rrs->retry_task = NULL;
     337           0 :   rrs->is->commands[rrs->is->ip].last_req_time
     338           0 :     = GNUNET_TIME_absolute_get ();
     339           0 :   refresh_reveal_run (rrs,
     340             :                       NULL,
     341             :                       rrs->is);
     342           0 : }
     343             : 
     344             : 
     345             : /**
     346             :  * "refresh reveal" request callback; it checks that the response
     347             :  * code is expected and copies into its command's state the data
     348             :  * coming from the exchange, namely the fresh coins.
     349             :  *
     350             :  * @param cls closure, a `struct RefreshRevealState`
     351             :  * @param rr HTTP response details
     352             :  */
     353             : static void
     354           0 : reveal_cb (void *cls,
     355             :            const struct TALER_EXCHANGE_RevealResult *rr)
     356             : {
     357           0 :   struct RefreshRevealState *rrs = cls;
     358           0 :   const struct TALER_EXCHANGE_HttpResponse *hr = &rr->hr;
     359             :   const struct TALER_TESTING_Command *melt_cmd;
     360             : 
     361           0 :   rrs->rrh = NULL;
     362           0 :   if (rrs->expected_response_code != hr->http_status)
     363             :   {
     364           0 :     if (0 != rrs->do_retry)
     365             :     {
     366           0 :       rrs->do_retry--;
     367           0 :       if ( (0 == hr->http_status) ||
     368           0 :            (TALER_EC_GENERIC_DB_SOFT_FAILURE == hr->ec) ||
     369           0 :            (MHD_HTTP_INTERNAL_SERVER_ERROR == hr->http_status) )
     370             :       {
     371           0 :         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     372             :                     "Retrying refresh reveal failed with %u/%d\n",
     373             :                     hr->http_status,
     374             :                     (int) hr->ec);
     375             :         /* on DB conflicts, do not use backoff */
     376           0 :         if (TALER_EC_GENERIC_DB_SOFT_FAILURE == hr->ec)
     377           0 :           rrs->backoff = GNUNET_TIME_UNIT_ZERO;
     378             :         else
     379           0 :           rrs->backoff = GNUNET_TIME_randomized_backoff (rrs->backoff,
     380             :                                                          MAX_BACKOFF);
     381           0 :         rrs->total_backoff = GNUNET_TIME_relative_add (rrs->total_backoff,
     382             :                                                        rrs->backoff);
     383           0 :         rrs->is->commands[rrs->is->ip].num_tries++;
     384           0 :         rrs->retry_task = GNUNET_SCHEDULER_add_delayed (rrs->backoff,
     385             :                                                         &do_reveal_retry,
     386             :                                                         rrs);
     387           0 :         return;
     388             :       }
     389             :     }
     390           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     391             :                 "Unexpected response code %u/%d to command %s in %s:%u\n",
     392             :                 hr->http_status,
     393             :                 (int) hr->ec,
     394             :                 rrs->is->commands[rrs->is->ip].label,
     395             :                 __FILE__,
     396             :                 __LINE__);
     397           0 :     json_dumpf (hr->reply,
     398             :                 stderr,
     399             :                 0);
     400           0 :     TALER_TESTING_interpreter_fail (rrs->is);
     401           0 :     return;
     402             :   }
     403           0 :   melt_cmd = TALER_TESTING_interpreter_lookup_command (rrs->is,
     404             :                                                        rrs->melt_reference);
     405           0 :   if (NULL == melt_cmd)
     406             :   {
     407           0 :     GNUNET_break (0);
     408           0 :     TALER_TESTING_interpreter_fail (rrs->is);
     409           0 :     return;
     410             :   }
     411           0 :   switch (hr->http_status)
     412             :   {
     413           0 :   case MHD_HTTP_OK:
     414           0 :     rrs->num_fresh_coins = rr->details.success.num_coins;
     415           0 :     rrs->psa = GNUNET_new_array (rrs->num_fresh_coins,
     416             :                                  struct TALER_PlanchetMasterSecretP);
     417           0 :     rrs->fresh_coins = GNUNET_new_array (rrs->num_fresh_coins,
     418             :                                          struct TALER_TESTING_FreshCoinData);
     419           0 :     for (unsigned int i = 0; i<rrs->num_fresh_coins; i++)
     420             :     {
     421           0 :       const struct TALER_EXCHANGE_RevealedCoinInfo *coin
     422           0 :         = &rr->details.success.coins[i];
     423           0 :       struct TALER_TESTING_FreshCoinData *fc = &rrs->fresh_coins[i];
     424             : 
     425           0 :       rrs->psa[i] = coin->ps;
     426           0 :       fc->blinding_key = coin->bks;
     427           0 :       if (GNUNET_OK !=
     428           0 :           TALER_TESTING_get_trait_denom_pub (melt_cmd,
     429             :                                              i,
     430             :                                              &fc->pk))
     431             :       {
     432           0 :         GNUNET_break (0);
     433           0 :         TALER_TESTING_interpreter_fail (rrs->is);
     434           0 :         return;
     435             :       }
     436           0 :       fc->coin_priv = coin->coin_priv;
     437           0 :       fc->age_commitment_proof = coin->age_commitment_proof;
     438           0 :       fc->h_age_commitment = coin->h_age_commitment;
     439             : 
     440           0 :       TALER_denom_sig_deep_copy (&fc->sig,
     441             :                                  &coin->sig);
     442             :     }
     443           0 :     if (0 != rrs->total_backoff.rel_value_us)
     444             :     {
     445           0 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     446             :                   "Total reveal backoff for %s was %s\n",
     447             :                   rrs->is->commands[rrs->is->ip].label,
     448             :                   GNUNET_STRINGS_relative_time_to_string (rrs->total_backoff,
     449             :                                                           GNUNET_YES));
     450             :     }
     451           0 :     break;
     452           0 :   default:
     453           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     454             :                 "Unknown HTTP status %u/%d\n",
     455             :                 hr->http_status,
     456             :                 (int) hr->ec);
     457             :   }
     458           0 :   TALER_TESTING_interpreter_next (rrs->is);
     459             : }
     460             : 
     461             : 
     462             : /**
     463             :  * Run the command.
     464             :  *
     465             :  * @param cls closure.
     466             :  * @param cmd the command to execute.
     467             :  * @param is the interpreter state.
     468             :  */
     469             : static void
     470             : melt_run (void *cls,
     471             :           const struct TALER_TESTING_Command *cmd,
     472             :           struct TALER_TESTING_Interpreter *is);
     473             : 
     474             : 
     475             : /**
     476             :  * Run the command.
     477             :  *
     478             :  * @param cls closure.
     479             :  * @param cmd the command to execute.
     480             :  * @param is the interpreter state.
     481             :  */
     482             : static void
     483           0 : refresh_reveal_run (void *cls,
     484             :                     const struct TALER_TESTING_Command *cmd,
     485             :                     struct TALER_TESTING_Interpreter *is)
     486             : {
     487           0 :   struct RefreshRevealState *rrs = cls;
     488             :   struct RefreshMeltState *rms;
     489             :   const struct TALER_TESTING_Command *melt_cmd;
     490             : 
     491           0 :   rrs->is = is;
     492           0 :   melt_cmd = TALER_TESTING_interpreter_lookup_command (is,
     493             :                                                        rrs->melt_reference);
     494           0 :   if (NULL == melt_cmd)
     495             :   {
     496           0 :     GNUNET_break (0);
     497           0 :     TALER_TESTING_interpreter_fail (rrs->is);
     498           0 :     return;
     499             :   }
     500           0 :   GNUNET_assert (melt_cmd->run == &melt_run);
     501           0 :   rms = melt_cmd->cls;
     502           0 :   {
     503           0 :     struct TALER_ExchangeWithdrawValues alg_values[rms->num_fresh_coins];
     504             : 
     505           0 :     for (unsigned int i = 0; i<rms->num_fresh_coins; i++)
     506           0 :       alg_values[i] = rms->mbds[i].alg_value;
     507           0 :     rrs->rrh = TALER_EXCHANGE_refreshes_reveal (is->exchange,
     508           0 :                                                 &rms->rms,
     509           0 :                                                 &rms->refresh_data,
     510             :                                                 rms->num_fresh_coins,
     511             :                                                 alg_values,
     512           0 :                                                 rms->noreveal_index,
     513             :                                                 &reveal_cb,
     514             :                                                 rrs);
     515             :   }
     516           0 :   if (NULL == rrs->rrh)
     517             :   {
     518           0 :     GNUNET_break (0);
     519           0 :     TALER_TESTING_interpreter_fail (is);
     520           0 :     return;
     521             :   }
     522             : }
     523             : 
     524             : 
     525             : /**
     526             :  * Free the state from a "refresh reveal" CMD, and possibly
     527             :  * cancel a pending operation thereof.
     528             :  *
     529             :  * @param cls closure.
     530             :  * @param cmd the command which is being cleaned up.
     531             :  */
     532             : static void
     533           0 : refresh_reveal_cleanup (void *cls,
     534             :                         const struct TALER_TESTING_Command *cmd)
     535             : {
     536           0 :   struct RefreshRevealState *rrs = cls;
     537             : 
     538             :   (void) cmd;
     539           0 :   if (NULL != rrs->rrh)
     540             :   {
     541           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     542             :                 "Command %u (%s) did not complete\n",
     543             :                 rrs->is->ip,
     544             :                 cmd->label);
     545           0 :     TALER_EXCHANGE_refreshes_reveal_cancel (rrs->rrh);
     546           0 :     rrs->rrh = NULL;
     547             :   }
     548           0 :   if (NULL != rrs->retry_task)
     549             :   {
     550           0 :     GNUNET_SCHEDULER_cancel (rrs->retry_task);
     551           0 :     rrs->retry_task = NULL;
     552             :   }
     553             : 
     554           0 :   for (unsigned int j = 0; j < rrs->num_fresh_coins; j++)
     555           0 :     TALER_denom_sig_free (&rrs->fresh_coins[j].sig);
     556             : 
     557           0 :   GNUNET_free (rrs->fresh_coins);
     558           0 :   GNUNET_free (rrs->psa);
     559           0 :   rrs->num_fresh_coins = 0;
     560           0 :   GNUNET_free (rrs);
     561           0 : }
     562             : 
     563             : 
     564             : /**
     565             :  * Run the command.
     566             :  *
     567             :  * @param cls closure.
     568             :  * @param cmd the command to execute.
     569             :  * @param is the interpreter state.
     570             :  */
     571             : static void
     572             : refresh_link_run (void *cls,
     573             :                   const struct TALER_TESTING_Command *cmd,
     574             :                   struct TALER_TESTING_Interpreter *is);
     575             : 
     576             : 
     577             : /**
     578             :  * Task scheduled to re-try #refresh_link_run.
     579             :  *
     580             :  * @param cls a `struct RefreshLinkState`
     581             :  */
     582             : static void
     583           0 : do_link_retry (void *cls)
     584             : {
     585           0 :   struct RefreshLinkState *rls = cls;
     586             : 
     587           0 :   rls->retry_task = NULL;
     588           0 :   rls->is->commands[rls->is->ip].last_req_time
     589           0 :     = GNUNET_TIME_absolute_get ();
     590           0 :   refresh_link_run (rls,
     591             :                     NULL,
     592             :                     rls->is);
     593           0 : }
     594             : 
     595             : 
     596             : /**
     597             :  * "refresh link" operation callback, checks that HTTP response
     598             :  * code is expected _and_ that all the linked coins were actually
     599             :  * withdrawn by the "refresh reveal" CMD.
     600             :  *
     601             :  * @param cls closure.
     602             :  * @param lr HTTP response details
     603             :  */
     604             : static void
     605           0 : link_cb (void *cls,
     606             :          const struct TALER_EXCHANGE_LinkResult *lr)
     607             : {
     608           0 :   struct RefreshLinkState *rls = cls;
     609           0 :   const struct TALER_EXCHANGE_HttpResponse *hr = &lr->hr;
     610             :   const struct TALER_TESTING_Command *reveal_cmd;
     611           0 :   struct TALER_TESTING_Command *link_cmd = &rls->is->commands[rls->is->ip];
     612             :   unsigned int found;
     613             :   const unsigned int *num_fresh_coins;
     614             : 
     615           0 :   rls->rlh = NULL;
     616           0 :   if (rls->expected_response_code != hr->http_status)
     617             :   {
     618           0 :     if (0 != rls->do_retry)
     619             :     {
     620           0 :       rls->do_retry--;
     621           0 :       if ( (0 == hr->http_status) ||
     622           0 :            (TALER_EC_GENERIC_DB_SOFT_FAILURE == hr->ec) ||
     623           0 :            (MHD_HTTP_INTERNAL_SERVER_ERROR == hr->http_status) )
     624             :       {
     625           0 :         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     626             :                     "Retrying refresh link failed with %u/%d\n",
     627             :                     hr->http_status,
     628             :                     (int) hr->ec);
     629             :         /* on DB conflicts, do not use backoff */
     630           0 :         if (TALER_EC_GENERIC_DB_SOFT_FAILURE == hr->ec)
     631           0 :           rls->backoff = GNUNET_TIME_UNIT_ZERO;
     632             :         else
     633           0 :           rls->backoff = GNUNET_TIME_randomized_backoff (rls->backoff,
     634             :                                                          MAX_BACKOFF);
     635           0 :         rls->total_backoff = GNUNET_TIME_relative_add (rls->total_backoff,
     636             :                                                        rls->backoff);
     637           0 :         rls->is->commands[rls->is->ip].num_tries++;
     638           0 :         rls->retry_task = GNUNET_SCHEDULER_add_delayed (rls->backoff,
     639             :                                                         &do_link_retry,
     640             :                                                         rls);
     641           0 :         return;
     642             :       }
     643             :     }
     644           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     645             :                 "Unexpected response code %u/%d to command %s in %s:%u\n",
     646             :                 hr->http_status,
     647             :                 (int) hr->ec,
     648             :                 link_cmd->label,
     649             :                 __FILE__,
     650             :                 __LINE__);
     651           0 :     json_dumpf (hr->reply,
     652             :                 stderr,
     653             :                 0);
     654           0 :     TALER_TESTING_interpreter_fail (rls->is);
     655           0 :     return;
     656             :   }
     657           0 :   reveal_cmd = TALER_TESTING_interpreter_lookup_command (rls->is,
     658             :                                                          rls->reveal_reference);
     659           0 :   if (NULL == reveal_cmd)
     660             :   {
     661           0 :     GNUNET_break (0);
     662           0 :     TALER_TESTING_interpreter_fail (rls->is);
     663           0 :     return;
     664             :   }
     665             : 
     666           0 :   switch (hr->http_status)
     667             :   {
     668           0 :   case MHD_HTTP_OK:
     669             :     /* check that number of coins returned matches */
     670           0 :     if (GNUNET_OK !=
     671           0 :         TALER_TESTING_get_trait_array_length (reveal_cmd,
     672             :                                               &num_fresh_coins))
     673             :     {
     674           0 :       GNUNET_break (0);
     675           0 :       TALER_TESTING_interpreter_fail (rls->is);
     676           0 :       return;
     677             :     }
     678           0 :     if (lr->details.success.num_coins != *num_fresh_coins)
     679             :     {
     680           0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     681             :                   "Unexpected number of fresh coins: %d vs %d in %s:%u\n",
     682             :                   lr->details.success.num_coins,
     683             :                   *num_fresh_coins,
     684             :                   __FILE__,
     685             :                   __LINE__);
     686           0 :       TALER_TESTING_interpreter_fail (rls->is);
     687           0 :       return;
     688             :     }
     689             :     /* check that the coins match */
     690           0 :     for (unsigned int i = 0; i<lr->details.success.num_coins; i++)
     691           0 :       for (unsigned int j = i + 1; j<lr->details.success.num_coins; j++)
     692           0 :         if (0 ==
     693           0 :             GNUNET_memcmp (&lr->details.success.coins[i].coin_priv,
     694             :                            &lr->details.success.coins[j].coin_priv))
     695           0 :           GNUNET_break (0);
     696             :     /* Note: coins might be legitimately permutated in here... */
     697           0 :     found = 0;
     698             : 
     699             :     /* Will point to the pointer inside the cmd state. */
     700             :     {
     701           0 :       const struct TALER_TESTING_FreshCoinData **fc = NULL;
     702             : 
     703           0 :       if (GNUNET_OK !=
     704           0 :           TALER_TESTING_get_trait_fresh_coins (reveal_cmd,
     705             :                                                &fc))
     706             :       {
     707           0 :         GNUNET_break (0);
     708           0 :         TALER_TESTING_interpreter_fail (rls->is);
     709           0 :         return;
     710             :       }
     711             : 
     712           0 :       for (unsigned int i = 0; i<lr->details.success.num_coins; i++)
     713             :       {
     714           0 :         const struct TALER_EXCHANGE_LinkedCoinInfo *lci_i
     715           0 :           = &lr->details.success.coins[i];
     716             : 
     717           0 :         for (unsigned int j = 0; j<lr->details.success.num_coins; j++)
     718             :         {
     719           0 :           const struct TALER_TESTING_FreshCoinData *fcj
     720           0 :             = &(*fc)[j];
     721             : 
     722           0 :           if ( (0 ==
     723           0 :                 GNUNET_memcmp (&fcj->coin_priv,
     724           0 :                                &lci_i->coin_priv)) &&
     725             :                (0 ==
     726           0 :                 TALER_denom_sig_cmp (&fcj->sig,
     727           0 :                                      &lci_i->sig)) &&
     728             :                (0 ==
     729           0 :                 TALER_denom_pub_cmp (&fcj->pk->key,
     730             :                                      &lci_i->pub)) )
     731             :           {
     732           0 :             found++;
     733           0 :             break;
     734             :           }
     735             :         } /* for j*/
     736             :       } /* for i */
     737             :     }
     738           0 :     if (found != lr->details.success.num_coins)
     739             :     {
     740           0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     741             :                   "Only %u/%u coins match expectations\n",
     742             :                   found,
     743             :                   lr->details.success.num_coins);
     744           0 :       GNUNET_break (0);
     745           0 :       TALER_TESTING_interpreter_fail (rls->is);
     746           0 :       return;
     747             :     }
     748           0 :     if (0 != rls->total_backoff.rel_value_us)
     749             :     {
     750           0 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     751             :                   "Total link backoff for %s was %s\n",
     752             :                   rls->is->commands[rls->is->ip].label,
     753             :                   GNUNET_STRINGS_relative_time_to_string (rls->total_backoff,
     754             :                                                           GNUNET_YES));
     755             :     }
     756           0 :     break;
     757           0 :   default:
     758           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     759             :                 "Unknown HTTP response code %u/%d.\n",
     760             :                 hr->http_status,
     761             :                 hr->ec);
     762             :   }
     763           0 :   TALER_TESTING_interpreter_next (rls->is);
     764             : }
     765             : 
     766             : 
     767             : /**
     768             :  * Run the command.
     769             :  *
     770             :  * @param cls closure.
     771             :  * @param cmd the command to execute.
     772             :  * @param is the interpreter state.
     773             :  */
     774             : static void
     775           0 : refresh_link_run (void *cls,
     776             :                   const struct TALER_TESTING_Command *cmd,
     777             :                   struct TALER_TESTING_Interpreter *is)
     778             : {
     779           0 :   struct RefreshLinkState *rls = cls;
     780             :   struct RefreshRevealState *rrs;
     781             :   struct RefreshMeltState *rms;
     782             :   const struct TALER_TESTING_Command *reveal_cmd;
     783             :   const struct TALER_TESTING_Command *melt_cmd;
     784             :   const struct TALER_TESTING_Command *coin_cmd;
     785             : 
     786             :   (void) cmd;
     787           0 :   rls->is = is;
     788           0 :   reveal_cmd = TALER_TESTING_interpreter_lookup_command (rls->is,
     789             :                                                          rls->reveal_reference);
     790           0 :   if (NULL == reveal_cmd)
     791             :   {
     792           0 :     GNUNET_break (0);
     793           0 :     TALER_TESTING_interpreter_fail (rls->is);
     794           0 :     return;
     795             :   }
     796           0 :   rrs = reveal_cmd->cls;
     797           0 :   melt_cmd = TALER_TESTING_interpreter_lookup_command (rls->is,
     798             :                                                        rrs->melt_reference);
     799           0 :   if (NULL == melt_cmd)
     800             :   {
     801           0 :     GNUNET_break (0);
     802           0 :     TALER_TESTING_interpreter_fail (rls->is);
     803           0 :     return;
     804             :   }
     805             : 
     806             :   /* find reserve_withdraw command */
     807           0 :   GNUNET_assert (melt_cmd->run == &melt_run);
     808           0 :   rms = melt_cmd->cls;
     809           0 :   coin_cmd = TALER_TESTING_interpreter_lookup_command (rls->is,
     810             :                                                        rms->coin_reference);
     811           0 :   if (NULL == coin_cmd)
     812             :   {
     813           0 :     GNUNET_break (0);
     814           0 :     TALER_TESTING_interpreter_fail (rls->is);
     815           0 :     return;
     816             :   }
     817             : 
     818             :   const struct TALER_CoinSpendPrivateKeyP *coin_priv;
     819           0 :   if (GNUNET_OK !=
     820           0 :       TALER_TESTING_get_trait_coin_priv (coin_cmd,
     821             :                                          0,
     822             :                                          &coin_priv))
     823             :   {
     824           0 :     GNUNET_break (0);
     825           0 :     TALER_TESTING_interpreter_fail (rls->is);
     826           0 :     return;
     827             :   }
     828             : 
     829             :   /* finally, use private key from withdraw sign command */
     830           0 :   rls->rlh = TALER_EXCHANGE_link (is->exchange,
     831             :                                   coin_priv,
     832             :                                   rms->refresh_data.melt_age_commitment_proof,
     833             :                                   &link_cb,
     834             :                                   rls);
     835             : 
     836           0 :   if (NULL == rls->rlh)
     837             :   {
     838           0 :     GNUNET_break (0);
     839           0 :     TALER_TESTING_interpreter_fail (rls->is);
     840           0 :     return;
     841             :   }
     842             : }
     843             : 
     844             : 
     845             : /**
     846             :  * Free the state of the "refresh link" CMD, and possibly
     847             :  * cancel a operation thereof.
     848             :  *
     849             :  * @param cls closure
     850             :  * @param cmd the command which is being cleaned up.
     851             :  */
     852             : static void
     853           0 : refresh_link_cleanup (void *cls,
     854             :                       const struct TALER_TESTING_Command *cmd)
     855             : {
     856           0 :   struct RefreshLinkState *rls = cls;
     857             : 
     858           0 :   if (NULL != rls->rlh)
     859             :   {
     860             : 
     861           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     862             :                 "Command %u (%s) did not complete\n",
     863             :                 rls->is->ip,
     864             :                 cmd->label);
     865           0 :     TALER_EXCHANGE_link_cancel (rls->rlh);
     866           0 :     rls->rlh = NULL;
     867             :   }
     868           0 :   if (NULL != rls->retry_task)
     869             :   {
     870           0 :     GNUNET_SCHEDULER_cancel (rls->retry_task);
     871           0 :     rls->retry_task = NULL;
     872             :   }
     873           0 :   GNUNET_free (rls);
     874           0 : }
     875             : 
     876             : 
     877             : /**
     878             :  * Task scheduled to re-try #melt_run.
     879             :  *
     880             :  * @param cls a `struct RefreshMeltState`
     881             :  */
     882             : static void
     883           0 : do_melt_retry (void *cls)
     884             : {
     885           0 :   struct RefreshMeltState *rms = cls;
     886             : 
     887           0 :   rms->retry_task = NULL;
     888           0 :   rms->is->commands[rms->is->ip].last_req_time
     889           0 :     = GNUNET_TIME_absolute_get ();
     890           0 :   melt_run (rms,
     891             :             NULL,
     892             :             rms->is);
     893           0 : }
     894             : 
     895             : 
     896             : /**
     897             :  * Callback for a "refresh melt" operation; checks if the HTTP
     898             :  * response code is okay and re-run the melt operation if the
     899             :  * CMD was set to do so.
     900             :  *
     901             :  * @param cls closure.
     902             :  * @param mr melt response details
     903             :  */
     904             : static void
     905           0 : melt_cb (void *cls,
     906             :          const struct TALER_EXCHANGE_MeltResponse *mr)
     907             : {
     908           0 :   struct RefreshMeltState *rms = cls;
     909           0 :   const struct TALER_EXCHANGE_HttpResponse *hr = &mr->hr;
     910             : 
     911           0 :   rms->rmh = NULL;
     912           0 :   if (rms->expected_response_code != hr->http_status)
     913             :   {
     914           0 :     if (0 != rms->do_retry)
     915             :     {
     916           0 :       rms->do_retry--;
     917           0 :       if ( (0 == hr->http_status) ||
     918           0 :            (TALER_EC_GENERIC_DB_SOFT_FAILURE == hr->ec) ||
     919           0 :            (MHD_HTTP_INTERNAL_SERVER_ERROR == hr->http_status) )
     920             :       {
     921           0 :         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     922             :                     "Retrying refresh melt failed with %u/%d\n",
     923             :                     hr->http_status,
     924             :                     (int) hr->ec);
     925             :         /* on DB conflicts, do not use backoff */
     926           0 :         if (TALER_EC_GENERIC_DB_SOFT_FAILURE == hr->ec)
     927           0 :           rms->backoff = GNUNET_TIME_UNIT_ZERO;
     928             :         else
     929           0 :           rms->backoff = GNUNET_TIME_randomized_backoff (rms->backoff,
     930             :                                                          MAX_BACKOFF);
     931           0 :         rms->total_backoff = GNUNET_TIME_relative_add (rms->total_backoff,
     932             :                                                        rms->backoff);
     933           0 :         rms->is->commands[rms->is->ip].num_tries++;
     934           0 :         rms->retry_task = GNUNET_SCHEDULER_add_delayed (rms->backoff,
     935             :                                                         &do_melt_retry,
     936             :                                                         rms);
     937           0 :         return;
     938             :       }
     939             :     }
     940           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     941             :                 "Unexpected response code %u/%d to command %s in %s:%u\n",
     942             :                 hr->http_status,
     943             :                 (int) hr->ec,
     944             :                 rms->is->commands[rms->is->ip].label,
     945             :                 __FILE__,
     946             :                 __LINE__);
     947           0 :     json_dumpf (hr->reply,
     948             :                 stderr,
     949             :                 0);
     950           0 :     TALER_TESTING_interpreter_fail (rms->is);
     951           0 :     return;
     952             :   }
     953           0 :   if (MHD_HTTP_OK == hr->http_status)
     954             :   {
     955           0 :     rms->noreveal_index = mr->details.success.noreveal_index;
     956           0 :     if (mr->details.success.num_mbds != rms->num_fresh_coins)
     957             :     {
     958           0 :       GNUNET_break (0);
     959           0 :       TALER_TESTING_interpreter_fail (rms->is);
     960           0 :       return;
     961             :     }
     962           0 :     GNUNET_free (rms->mbds);
     963           0 :     rms->mbds = GNUNET_memdup (mr->details.success.mbds,
     964             :                                mr->details.success.num_mbds
     965             :                                * sizeof (struct
     966             :                                          TALER_EXCHANGE_MeltBlindingDetail));
     967             :   }
     968           0 :   if (0 != rms->total_backoff.rel_value_us)
     969             :   {
     970           0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     971             :                 "Total melt backoff for %s was %s\n",
     972             :                 rms->is->commands[rms->is->ip].label,
     973             :                 GNUNET_STRINGS_relative_time_to_string (rms->total_backoff,
     974             :                                                         GNUNET_YES));
     975             :   }
     976           0 :   if (rms->double_melt)
     977             :   {
     978           0 :     TALER_LOG_DEBUG ("Doubling the melt (%s)\n",
     979             :                      rms->is->commands[rms->is->ip].label);
     980           0 :     rms->rmh = TALER_EXCHANGE_melt (rms->is->exchange,
     981           0 :                                     &rms->rms,
     982           0 :                                     &rms->refresh_data,
     983             :                                     &melt_cb,
     984             :                                     rms);
     985           0 :     rms->double_melt = false;
     986           0 :     return;
     987             :   }
     988           0 :   TALER_TESTING_interpreter_next (rms->is);
     989             : }
     990             : 
     991             : 
     992             : /**
     993             :  * Run the command.
     994             :  *
     995             :  * @param cls closure.
     996             :  * @param cmd the command to execute.
     997             :  * @param is the interpreter state.
     998             :  */
     999             : static void
    1000           0 : melt_run (void *cls,
    1001             :           const struct TALER_TESTING_Command *cmd,
    1002             :           struct TALER_TESTING_Interpreter *is)
    1003             : {
    1004           0 :   struct RefreshMeltState *rms = cls;
    1005             :   unsigned int num_fresh_coins;
    1006           0 :   const char *default_melt_fresh_amounts[] = {
    1007             :     "EUR:1", "EUR:1", "EUR:1", "EUR:0.1",
    1008             :     NULL
    1009             :   };
    1010             :   const char **melt_fresh_amounts;
    1011             : 
    1012             :   (void) cmd;
    1013           0 :   if (NULL == (melt_fresh_amounts = rms->melt_fresh_amounts))
    1014           0 :     melt_fresh_amounts = default_melt_fresh_amounts;
    1015           0 :   rms->is = is;
    1016           0 :   rms->noreveal_index = UINT16_MAX;
    1017           0 :   TALER_refresh_master_setup_random (&rms->rms);
    1018           0 :   for (num_fresh_coins = 0;
    1019           0 :        NULL != melt_fresh_amounts[num_fresh_coins];
    1020           0 :        num_fresh_coins++)
    1021             :     ;
    1022           0 :   rms->num_fresh_coins = num_fresh_coins;
    1023           0 :   rms->fresh_pks = GNUNET_new_array (
    1024             :     num_fresh_coins,
    1025             :     struct TALER_EXCHANGE_DenomPublicKey);
    1026             :   {
    1027             :     struct TALER_Amount melt_amount;
    1028             :     struct TALER_Amount fresh_amount;
    1029             :     const struct TALER_AgeCommitmentProof *age_commitment_proof;
    1030             :     const struct TALER_AgeCommitmentHash *h_age_commitment;
    1031             :     const struct TALER_DenominationSignature *melt_sig;
    1032             :     const struct TALER_EXCHANGE_DenomPublicKey *melt_denom_pub;
    1033             :     const struct TALER_TESTING_Command *coin_command;
    1034             :     bool age_restricted;
    1035             : 
    1036           0 :     if (NULL == (coin_command
    1037           0 :                    = TALER_TESTING_interpreter_lookup_command (
    1038             :                        is,
    1039             :                        rms->coin_reference)))
    1040             :     {
    1041           0 :       GNUNET_break (0);
    1042           0 :       TALER_TESTING_interpreter_fail (rms->is);
    1043           0 :       return;
    1044             :     }
    1045             : 
    1046           0 :     if (GNUNET_OK !=
    1047           0 :         TALER_TESTING_get_trait_coin_priv (coin_command,
    1048             :                                            0,
    1049             :                                            &rms->melt_priv))
    1050             :     {
    1051           0 :       GNUNET_break (0);
    1052           0 :       TALER_TESTING_interpreter_fail (rms->is);
    1053           0 :       return;
    1054             :     }
    1055             : 
    1056           0 :     if (GNUNET_OK !=
    1057           0 :         TALER_TESTING_get_trait_age_commitment_proof (coin_command,
    1058             :                                                       0,
    1059             :                                                       &age_commitment_proof))
    1060             :     {
    1061           0 :       GNUNET_break (0);
    1062           0 :       TALER_TESTING_interpreter_fail (rms->is);
    1063           0 :       return;
    1064             :     }
    1065             : 
    1066           0 :     if (GNUNET_OK !=
    1067           0 :         TALER_TESTING_get_trait_h_age_commitment (coin_command,
    1068             :                                                   0,
    1069             :                                                   &h_age_commitment))
    1070             :     {
    1071           0 :       GNUNET_break (0);
    1072           0 :       TALER_TESTING_interpreter_fail (rms->is);
    1073           0 :       return;
    1074             :     }
    1075             : 
    1076           0 :     if (GNUNET_OK !=
    1077           0 :         TALER_TESTING_get_trait_denom_sig (coin_command,
    1078             :                                            0,
    1079             :                                            &melt_sig))
    1080             :     {
    1081           0 :       GNUNET_break (0);
    1082           0 :       TALER_TESTING_interpreter_fail (rms->is);
    1083           0 :       return;
    1084             :     }
    1085             : 
    1086           0 :     if (GNUNET_OK !=
    1087           0 :         TALER_TESTING_get_trait_denom_pub (coin_command,
    1088             :                                            0,
    1089             :                                            &melt_denom_pub))
    1090             :     {
    1091           0 :       GNUNET_break (0);
    1092           0 :       TALER_TESTING_interpreter_fail (rms->is);
    1093           0 :       return;
    1094             :     }
    1095             : 
    1096             :     /* Melt amount starts with the melt fee of the old coin; we'll add the
    1097             :        values and withdraw fees of the fresh coins next */
    1098           0 :     melt_amount = melt_denom_pub->fees.refresh;
    1099           0 :     age_restricted = melt_denom_pub->key.age_mask.bits != 0;
    1100           0 :     for (unsigned int i = 0; i<num_fresh_coins; i++)
    1101             :     {
    1102             :       const struct TALER_EXCHANGE_DenomPublicKey *fresh_pk;
    1103             : 
    1104           0 :       if (GNUNET_OK !=
    1105           0 :           TALER_string_to_amount (melt_fresh_amounts[i],
    1106             :                                   &fresh_amount))
    1107             :       {
    1108           0 :         GNUNET_break (0);
    1109           0 :         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1110             :                     "Failed to parse amount `%s' at index %u\n",
    1111             :                     melt_fresh_amounts[i],
    1112             :                     i);
    1113           0 :         TALER_TESTING_interpreter_fail (rms->is);
    1114           0 :         return;
    1115             :       }
    1116           0 :       fresh_pk = TALER_TESTING_find_pk (TALER_EXCHANGE_get_keys (is->exchange),
    1117             :                                         &fresh_amount,
    1118             :                                         age_restricted);
    1119           0 :       if (NULL == fresh_pk)
    1120             :       {
    1121           0 :         GNUNET_break (0);
    1122             :         /* Subroutine logs specific error */
    1123           0 :         TALER_TESTING_interpreter_fail (rms->is);
    1124           0 :         return;
    1125             :       }
    1126           0 :       GNUNET_assert (0 <=
    1127             :                      TALER_amount_add (&melt_amount,
    1128             :                                        &melt_amount,
    1129             :                                        &fresh_amount));
    1130           0 :       GNUNET_assert (0 <=
    1131             :                      TALER_amount_add (&melt_amount,
    1132             :                                        &melt_amount,
    1133             :                                        &fresh_pk->fees.withdraw));
    1134           0 :       rms->fresh_pks[i] = *fresh_pk;
    1135             :       /* Make a deep copy of the RSA key */
    1136           0 :       TALER_denom_pub_deep_copy (&rms->fresh_pks[i].key,
    1137             :                                  &fresh_pk->key);
    1138             :     } /* end for */
    1139             : 
    1140           0 :     rms->refresh_data.melt_priv = *rms->melt_priv;
    1141           0 :     rms->refresh_data.melt_amount = melt_amount;
    1142           0 :     rms->refresh_data.melt_sig = *melt_sig;
    1143           0 :     rms->refresh_data.melt_pk = *melt_denom_pub;
    1144           0 :     rms->refresh_data.melt_age_commitment_proof = age_commitment_proof;
    1145           0 :     rms->refresh_data.melt_h_age_commitment = h_age_commitment;
    1146           0 :     rms->refresh_data.fresh_pks = rms->fresh_pks;
    1147           0 :     rms->refresh_data.fresh_pks_len = num_fresh_coins;
    1148             : 
    1149           0 :     GNUNET_assert (age_restricted ==
    1150             :                    (NULL != age_commitment_proof));
    1151             : 
    1152           0 :     rms->rmh = TALER_EXCHANGE_melt (is->exchange,
    1153           0 :                                     &rms->rms,
    1154           0 :                                     &rms->refresh_data,
    1155             :                                     &melt_cb,
    1156             :                                     rms);
    1157             : 
    1158           0 :     if (NULL == rms->rmh)
    1159             :     {
    1160           0 :       GNUNET_break (0);
    1161           0 :       TALER_TESTING_interpreter_fail (rms->is);
    1162           0 :       return;
    1163             :     }
    1164             :   }
    1165             : }
    1166             : 
    1167             : 
    1168             : /**
    1169             :  * Free the "refresh melt" CMD state, and possibly cancel a
    1170             :  * pending operation thereof.
    1171             :  *
    1172             :  * @param cls closure, must be a `struct RefreshMeltState`.
    1173             :  * @param cmd the command which is being cleaned up.
    1174             :  */
    1175             : static void
    1176           0 : melt_cleanup (void *cls,
    1177             :               const struct TALER_TESTING_Command *cmd)
    1178             : {
    1179           0 :   struct RefreshMeltState *rms = cls;
    1180             : 
    1181             :   (void) cmd;
    1182           0 :   if (NULL != rms->rmh)
    1183             :   {
    1184           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    1185             :                 "Command %u (%s) did not complete\n",
    1186             :                 rms->is->ip,
    1187             :                 rms->is->commands[rms->is->ip].label);
    1188           0 :     TALER_EXCHANGE_melt_cancel (rms->rmh);
    1189           0 :     rms->rmh = NULL;
    1190             :   }
    1191           0 :   if (NULL != rms->retry_task)
    1192             :   {
    1193           0 :     GNUNET_SCHEDULER_cancel (rms->retry_task);
    1194           0 :     rms->retry_task = NULL;
    1195             :   }
    1196           0 :   if (NULL != rms->fresh_pks)
    1197             :   {
    1198           0 :     for (unsigned int i = 0; i < rms->num_fresh_coins; i++)
    1199           0 :       TALER_denom_pub_free (&rms->fresh_pks[i].key);
    1200           0 :     GNUNET_free (rms->fresh_pks);
    1201             :   }
    1202           0 :   GNUNET_free (rms->mbds);
    1203           0 :   GNUNET_free (rms->melt_fresh_amounts);
    1204           0 :   GNUNET_free (rms);
    1205           0 : }
    1206             : 
    1207             : 
    1208             : /**
    1209             :  * Offer internal data to the "refresh melt" CMD.
    1210             :  *
    1211             :  * @param cls closure.
    1212             :  * @param[out] ret result (could be anything).
    1213             :  * @param trait name of the trait.
    1214             :  * @param index index number of the object to offer.
    1215             :  * @return #GNUNET_OK on success.
    1216             :  */
    1217             : static enum GNUNET_GenericReturnValue
    1218           0 : melt_traits (void *cls,
    1219             :              const void **ret,
    1220             :              const char *trait,
    1221             :              unsigned int index)
    1222             : {
    1223           0 :   struct RefreshMeltState *rms = cls;
    1224             : 
    1225           0 :   if (index >= rms->num_fresh_coins)
    1226             :   {
    1227           0 :     GNUNET_break (0);
    1228           0 :     return GNUNET_SYSERR;
    1229             :   }
    1230             :   {
    1231             :     struct TALER_TESTING_Trait traits[] = {
    1232           0 :       TALER_TESTING_make_trait_denom_pub (index,
    1233           0 :                                           &rms->fresh_pks[index]),
    1234           0 :       TALER_TESTING_make_trait_coin_priv (index,
    1235             :                                           rms->melt_priv),
    1236           0 :       TALER_TESTING_make_trait_age_commitment_proof (
    1237             :         index,
    1238             :         rms->refresh_data.melt_age_commitment_proof),
    1239           0 :       TALER_TESTING_make_trait_h_age_commitment (
    1240             :         index,
    1241             :         rms->refresh_data.melt_h_age_commitment),
    1242           0 :       TALER_TESTING_make_trait_exchange_wd_value (index,
    1243           0 :                                                   &rms->mbds[index].alg_value),
    1244           0 :       TALER_TESTING_make_trait_refresh_secret (&rms->rms),
    1245           0 :       TALER_TESTING_trait_end ()
    1246             :     };
    1247             : 
    1248           0 :     return TALER_TESTING_get_trait (traits,
    1249             :                                     ret,
    1250             :                                     trait,
    1251             :                                     index);
    1252             :   }
    1253             : }
    1254             : 
    1255             : 
    1256             : /**
    1257             :  * Parse list of amounts for melt operation.
    1258             :  *
    1259             :  * @param[in,out] rms where to store the list
    1260             :  * @param ap NULL-termianted list of amounts to be melted (one per fresh coin)
    1261             :  * @return #GNUNET_OK on success
    1262             :  */
    1263             : static enum GNUNET_GenericReturnValue
    1264           0 : parse_amounts (struct RefreshMeltState *rms,
    1265             :                va_list ap)
    1266             : {
    1267             :   unsigned int len;
    1268             :   unsigned int off;
    1269             :   const char *amount;
    1270             : 
    1271           0 :   len = 0;
    1272           0 :   off = 0;
    1273           0 :   while (NULL != (amount = va_arg (ap, const char *)))
    1274             :   {
    1275           0 :     if (len == off)
    1276             :     {
    1277             :       struct TALER_Amount a;
    1278             : 
    1279           0 :       GNUNET_array_grow (rms->melt_fresh_amounts,
    1280             :                          len,
    1281             :                          off + 16);
    1282           0 :       if (GNUNET_OK !=
    1283           0 :           TALER_string_to_amount (amount, &a))
    1284             :       {
    1285           0 :         GNUNET_break (0);
    1286           0 :         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1287             :                     "Failed to parse amount `%s' at index %u\n",
    1288             :                     amount, off);
    1289           0 :         GNUNET_free (rms->melt_fresh_amounts);
    1290           0 :         rms->melt_fresh_amounts = NULL;
    1291           0 :         return GNUNET_SYSERR;
    1292             :       }
    1293           0 :       rms->melt_fresh_amounts[off++] = amount;
    1294             :     }
    1295             :   }
    1296           0 :   if (0 == off)
    1297           0 :     return GNUNET_OK; /* no amounts given == use defaults! */
    1298             :   /* ensure NULL-termination */
    1299           0 :   GNUNET_array_grow (rms->melt_fresh_amounts,
    1300             :                      len,
    1301             :                      off + 1);
    1302           0 :   return GNUNET_OK;
    1303             : }
    1304             : 
    1305             : 
    1306             : struct TALER_TESTING_Command
    1307           0 : TALER_TESTING_cmd_melt (const char *label,
    1308             :                         const char *coin_reference,
    1309             :                         unsigned int expected_response_code,
    1310             :                         ...)
    1311             : {
    1312             :   struct RefreshMeltState *rms;
    1313             :   va_list ap;
    1314             : 
    1315           0 :   rms = GNUNET_new (struct RefreshMeltState);
    1316           0 :   rms->coin_reference = coin_reference;
    1317           0 :   rms->expected_response_code = expected_response_code;
    1318           0 :   va_start (ap,
    1319             :             expected_response_code);
    1320           0 :   GNUNET_assert (GNUNET_OK ==
    1321             :                  parse_amounts (rms, ap));
    1322           0 :   va_end (ap);
    1323             :   {
    1324           0 :     struct TALER_TESTING_Command cmd = {
    1325             :       .label = label,
    1326             :       .cls = rms,
    1327             :       .run = &melt_run,
    1328             :       .cleanup = &melt_cleanup,
    1329             :       .traits = &melt_traits
    1330             :     };
    1331             : 
    1332           0 :     return cmd;
    1333             :   }
    1334             : }
    1335             : 
    1336             : 
    1337             : struct TALER_TESTING_Command
    1338           0 : TALER_TESTING_cmd_melt_double (const char *label,
    1339             :                                const char *coin_reference,
    1340             :                                unsigned int expected_response_code,
    1341             :                                ...)
    1342             : {
    1343             :   struct RefreshMeltState *rms;
    1344             :   va_list ap;
    1345             : 
    1346           0 :   rms = GNUNET_new (struct RefreshMeltState);
    1347           0 :   rms->coin_reference = coin_reference;
    1348           0 :   rms->expected_response_code = expected_response_code;
    1349           0 :   rms->double_melt = true;
    1350           0 :   va_start (ap,
    1351             :             expected_response_code);
    1352           0 :   GNUNET_assert (GNUNET_OK ==
    1353             :                  parse_amounts (rms, ap));
    1354           0 :   va_end (ap);
    1355             :   {
    1356           0 :     struct TALER_TESTING_Command cmd = {
    1357             :       .label = label,
    1358             :       .cls = rms,
    1359             :       .run = &melt_run,
    1360             :       .cleanup = &melt_cleanup,
    1361             :       .traits = &melt_traits
    1362             :     };
    1363             : 
    1364           0 :     return cmd;
    1365             :   }
    1366             : }
    1367             : 
    1368             : 
    1369             : struct TALER_TESTING_Command
    1370           0 : TALER_TESTING_cmd_melt_with_retry (struct TALER_TESTING_Command cmd)
    1371             : {
    1372             :   struct RefreshMeltState *rms;
    1373             : 
    1374           0 :   GNUNET_assert (&melt_run == cmd.run);
    1375           0 :   rms = cmd.cls;
    1376           0 :   rms->do_retry = NUM_RETRIES;
    1377           0 :   return cmd;
    1378             : }
    1379             : 
    1380             : 
    1381             : /**
    1382             :  * Offer internal data from a "refresh reveal" CMD.
    1383             :  *
    1384             :  * @param cls closure.
    1385             :  * @param[out] ret result (could be anything).
    1386             :  * @param trait name of the trait.
    1387             :  * @param index index number of the object to offer.
    1388             :  * @return #GNUNET_OK on success.
    1389             :  */
    1390             : static enum GNUNET_GenericReturnValue
    1391           0 : refresh_reveal_traits (void *cls,
    1392             :                        const void **ret,
    1393             :                        const char *trait,
    1394             :                        unsigned int index)
    1395             : {
    1396           0 :   struct RefreshRevealState *rrs = cls;
    1397             : 
    1398           0 :   if (index >= rrs->num_fresh_coins)
    1399           0 :     return GNUNET_SYSERR;
    1400             : 
    1401             :   {
    1402             :     struct TALER_TESTING_Trait traits[] = {
    1403           0 :       TALER_TESTING_make_trait_coin_priv (
    1404             :         index,
    1405           0 :         &rrs->fresh_coins[index].coin_priv),
    1406           0 :       TALER_TESTING_make_trait_age_commitment_proof (
    1407             :         index,
    1408           0 :         rrs->fresh_coins[index].age_commitment_proof),
    1409           0 :       TALER_TESTING_make_trait_h_age_commitment (
    1410             :         index,
    1411           0 :         rrs->fresh_coins[index].h_age_commitment),
    1412           0 :       TALER_TESTING_make_trait_denom_pub (
    1413             :         index,
    1414           0 :         rrs->fresh_coins[index].pk),
    1415           0 :       TALER_TESTING_make_trait_denom_sig (
    1416             :         index,
    1417           0 :         &rrs->fresh_coins[index].sig),
    1418           0 :       TALER_TESTING_make_trait_blinding_key (
    1419             :         index,
    1420           0 :         &rrs->fresh_coins[index].blinding_key),
    1421           0 :       TALER_TESTING_make_trait_array_length (
    1422           0 :         &rrs->num_fresh_coins),
    1423           0 :       TALER_TESTING_make_trait_fresh_coins (
    1424           0 :         (const struct TALER_TESTING_FreshCoinData **) &rrs->fresh_coins),
    1425           0 :       TALER_TESTING_make_trait_planchet_secrets (index,
    1426           0 :                                                  &rrs->psa[index]),
    1427           0 :       TALER_TESTING_trait_end ()
    1428             :     };
    1429           0 :     return TALER_TESTING_get_trait (traits,
    1430             :                                     ret,
    1431             :                                     trait,
    1432             :                                     index);
    1433             :   }
    1434             : }
    1435             : 
    1436             : 
    1437             : struct TALER_TESTING_Command
    1438           0 : TALER_TESTING_cmd_refresh_reveal (const char *label,
    1439             :                                   const char *melt_reference,
    1440             :                                   unsigned int expected_response_code)
    1441             : {
    1442             :   struct RefreshRevealState *rrs;
    1443             : 
    1444           0 :   rrs = GNUNET_new (struct RefreshRevealState);
    1445           0 :   rrs->melt_reference = melt_reference;
    1446           0 :   rrs->expected_response_code = expected_response_code;
    1447             :   {
    1448           0 :     struct TALER_TESTING_Command cmd = {
    1449             :       .cls = rrs,
    1450             :       .label = label,
    1451             :       .run = &refresh_reveal_run,
    1452             :       .cleanup = &refresh_reveal_cleanup,
    1453             :       .traits = &refresh_reveal_traits
    1454             :     };
    1455             : 
    1456           0 :     return cmd;
    1457             :   }
    1458             : }
    1459             : 
    1460             : 
    1461             : struct TALER_TESTING_Command
    1462           0 : TALER_TESTING_cmd_refresh_reveal_with_retry (struct TALER_TESTING_Command cmd)
    1463             : {
    1464             :   struct RefreshRevealState *rrs;
    1465             : 
    1466           0 :   GNUNET_assert (&refresh_reveal_run == cmd.run);
    1467           0 :   rrs = cmd.cls;
    1468           0 :   rrs->do_retry = NUM_RETRIES;
    1469           0 :   return cmd;
    1470             : }
    1471             : 
    1472             : 
    1473             : struct TALER_TESTING_Command
    1474           0 : TALER_TESTING_cmd_refresh_link (const char *label,
    1475             :                                 const char *reveal_reference,
    1476             :                                 unsigned int expected_response_code)
    1477             : {
    1478             :   struct RefreshLinkState *rrs;
    1479             : 
    1480           0 :   rrs = GNUNET_new (struct RefreshLinkState);
    1481           0 :   rrs->reveal_reference = reveal_reference;
    1482           0 :   rrs->expected_response_code = expected_response_code;
    1483             :   {
    1484           0 :     struct TALER_TESTING_Command cmd = {
    1485             :       .cls = rrs,
    1486             :       .label = label,
    1487             :       .run = &refresh_link_run,
    1488             :       .cleanup = &refresh_link_cleanup
    1489             :     };
    1490             : 
    1491           0 :     return cmd;
    1492             :   }
    1493             : }
    1494             : 
    1495             : 
    1496             : struct TALER_TESTING_Command
    1497           0 : TALER_TESTING_cmd_refresh_link_with_retry (struct TALER_TESTING_Command cmd)
    1498             : {
    1499             :   struct RefreshLinkState *rls;
    1500             : 
    1501           0 :   GNUNET_assert (&refresh_link_run == cmd.run);
    1502           0 :   rls = cmd.cls;
    1503           0 :   rls->do_retry = NUM_RETRIES;
    1504           0 :   return cmd;
    1505             : }

Generated by: LCOV version 1.14