exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

testing_api_cmd_refresh.c (36359B)


      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  * @author Özgür Kesim
     24  */
     25 #include "taler/taler_json_lib.h"
     26 #include <gnunet/gnunet_curl_lib.h>
     27 struct MeltState;
     28 struct RevealMeltState;
     29 #define TALER_EXCHANGE_POST_MELT_RESULT_CLOSURE struct MeltState
     30 #include "taler/exchange/post-melt.h"
     31 #define TALER_EXCHANGE_POST_REVEAL_MELT_RESULT_CLOSURE struct RevealMeltState
     32 #include "taler/exchange/post-reveal-melt.h"
     33 #include "taler/taler_testing_lib.h"
     34 #include "taler/taler_signatures.h"
     35 #include "backoff.h"
     36 
     37 /**
     38  * How long do we wait AT MOST when retrying?
     39  */
     40 #define MAX_BACKOFF GNUNET_TIME_relative_multiply ( \
     41           GNUNET_TIME_UNIT_MILLISECONDS, 100)
     42 
     43 /**
     44  * How often do we retry before giving up?
     45  */
     46 #define NUM_RETRIES 5
     47 
     48 /**
     49  * How long do we wait AT MOST when retrying?
     50  */
     51 #define MAX_BACKOFF GNUNET_TIME_relative_multiply ( \
     52           GNUNET_TIME_UNIT_MILLISECONDS, 100)
     53 
     54 /**
     55  * Information about a fresh coin generated by the refresh
     56  * operation.
     57  */
     58 struct TALER_TESTING_FreshCoinData
     59 {
     60 
     61   /**
     62    * If @e amount is NULL, this specifies the denomination key to
     63    * use.  Otherwise, this will be set (by the interpreter) to the
     64    * denomination PK matching @e amount.
     65    */
     66   const struct TALER_EXCHANGE_DenomPublicKey *pk;
     67 
     68   /**
     69    * Set (by the interpreter) to the exchange's signature over the
     70    * coin's public key.
     71    */
     72   struct TALER_DenominationSignature sig;
     73 
     74   /**
     75    * Set (by the interpreter) to the coin's private key.
     76    */
     77   struct TALER_CoinSpendPrivateKeyP coin_priv;
     78 
     79   /**
     80    * Set (by the interpreter) to the coin's public key.
     81    */
     82   struct TALER_CoinSpendPublicKeyP coin_pub;
     83 
     84   /**
     85    * Fresh age commitment for the coin with proof and its hash, NULL if not
     86    * applicable.
     87    */
     88   struct TALER_AgeCommitmentProof *age_commitment_proof;
     89   struct TALER_AgeCommitmentHashP h_age_commitment;
     90 
     91   /**
     92    * The blinding key (needed for recoup operations).
     93    */
     94   union GNUNET_CRYPTO_BlindingSecretP blinding_key;
     95 
     96 };
     97 
     98 
     99 /**
    100  * State for a "refresh melt" command.
    101  */
    102 struct MeltState
    103 {
    104 
    105   /**
    106    * Reference to reserve_withdraw operations for coin to
    107    * be used for the /refresh/melt operation.
    108    */
    109   const char *coin_reference;
    110 
    111   /**
    112    * Our command.
    113    */
    114   const struct TALER_TESTING_Command *cmd;
    115 
    116   /**
    117    * Reference to a previous melt command.
    118    */
    119   const char *melt_reference;
    120 
    121   /**
    122    * Melt handle while operation is running.
    123    */
    124   struct TALER_EXCHANGE_PostMeltHandle *mh;
    125 
    126   /**
    127    * Expected entry in the coin history created by this
    128    * operation.
    129    */
    130   struct TALER_EXCHANGE_CoinHistoryEntry che;
    131 
    132   /**
    133    * Interpreter state.
    134    */
    135   struct TALER_TESTING_Interpreter *is;
    136 
    137   /**
    138    * The input for the call to /melt
    139    */
    140   struct TALER_EXCHANGE_MeltInput melt_input;
    141 
    142   /**
    143    * Length of the @a blinding_values array with the exchange values
    144    * and blinding keys we are using.
    145    */
    146   unsigned int num_blinding_values;
    147 
    148   /**
    149    * Blinding values returned per coin.
    150    */
    151   struct TALER_ExchangeBlindingValues *blinding_values;
    152 
    153   /**
    154    * The input for the call to /reveal-melt
    155    */
    156   struct TALER_EXCHANGE_RevealMeltInput reveal_melt_input;
    157 
    158   /**
    159    * Array of the denomination public keys
    160    * corresponding to the @e num_fresh_coins;
    161    */
    162   struct TALER_EXCHANGE_DenomPublicKey *fresh_pks;
    163 
    164   /**
    165    * Private key of the dirty coin being melted.
    166    */
    167   const struct TALER_CoinSpendPrivateKeyP *melt_priv;
    168 
    169   /**
    170    * Public key of the dirty coin being melted.
    171    */
    172   struct TALER_CoinSpendPublicKeyP melt_pub;
    173 
    174   /**
    175    * Entropy seed for the refresh-melt operation.
    176    */
    177   struct TALER_PublicRefreshMasterSeedP rms;
    178 
    179   /**
    180    * If false, @e blinding_seed contains the seed for the
    181    * blinding values for CS signatures
    182    */
    183   bool no_blinding_seed;
    184 
    185   /**
    186    * If @e no_blinding_seed is false, contains the blinding
    187    * seed from which the nonces were derived for CS signatures
    188    */
    189   struct TALER_BlindingMasterSeedP blinding_seed;
    190 
    191   /**
    192    * The refresh commitment we calculated
    193    */
    194   struct TALER_RefreshCommitmentP rc;
    195 
    196   /**
    197    * The kappa refresh nonces for signing with the old coin.
    198    */
    199   struct TALER_KappaPublicRefreshNoncesP kappa_nonces;
    200 
    201   /**
    202    * Task scheduled to try later.
    203    */
    204   struct GNUNET_SCHEDULER_Task *retry_task;
    205 
    206   /**
    207    * How long do we wait until we retry?
    208    */
    209   struct GNUNET_TIME_Relative backoff;
    210 
    211   /**
    212    * How long did we wait in total for retries?
    213    */
    214   struct GNUNET_TIME_Relative total_backoff;
    215 
    216   /**
    217    * Amounts to be generated during melt.
    218    */
    219   const char **melt_fresh_amounts;
    220 
    221   /**
    222    * Number of fresh coins generated by the melt.
    223    */
    224   unsigned int num_fresh_coins;
    225 
    226   /**
    227    * Expected HTTP response code.
    228    */
    229   unsigned int expected_response_code;
    230 
    231   /**
    232    * if set to #GNUNET_YES, then two /refresh/melt operations
    233    * will be performed.  This is needed to trigger the logic
    234    * that manages those already-made requests.  Note: it
    235    * is not possible to just copy-and-paste a test refresh melt
    236    * CMD to have the same effect, because every data preparation
    237    * generates new planchets that (in turn) make the whole "hash"
    238    * different from any previous one, therefore NOT allowing the
    239    * exchange to pick any previous /rerfesh/melt operation from
    240    * the database.
    241    */
    242   bool double_melt;
    243 
    244   /**
    245    * How often should we retry on (transient) failures?
    246    */
    247   unsigned int do_retry;
    248 
    249   /**
    250    * Set by the melt callback as it comes from the exchange.
    251    */
    252   uint16_t noreveal_index;
    253 
    254   /**
    255    * The signatures over the nonces we need to reveal
    256    */
    257   struct TALER_RevealPrivateRefreshNonceSignaturesP revealed_signatures;
    258 
    259 };
    260 
    261 
    262 /**
    263  * State for a "refresh reveal" CMD.
    264  */
    265 struct RevealMeltState
    266 {
    267   /**
    268    * Link to a "refresh melt" command.
    269    */
    270   const char *melt_reference;
    271 
    272   /**
    273    * Reveal handle while operation is running.
    274    */
    275   struct TALER_EXCHANGE_PostRevealMeltHandle *rmh;
    276 
    277   /**
    278    * Our command.
    279    */
    280   const struct TALER_TESTING_Command *cmd;
    281 
    282   /**
    283    * Convenience struct to keep in one place all the
    284    * data related to one fresh coin, set by the reveal callback
    285    * as it comes from the exchange.
    286    */
    287   struct TALER_TESTING_FreshCoinData *fresh_coins;
    288 
    289   /**
    290    * Array of @e num_fresh_coins planchet secrets derived
    291    * from the transfer secret per fresh coin.
    292    */
    293   struct TALER_PlanchetMasterSecretP *psa;
    294 
    295   /**
    296    * Interpreter state.
    297    */
    298   struct TALER_TESTING_Interpreter *is;
    299 
    300   /**
    301    * Task scheduled to try later.
    302    */
    303   struct GNUNET_SCHEDULER_Task *retry_task;
    304 
    305   /**
    306    * How long do we wait until we retry?
    307    */
    308   struct GNUNET_TIME_Relative backoff;
    309 
    310   /**
    311    * How long did we wait in total for retries?
    312    */
    313   struct GNUNET_TIME_Relative total_backoff;
    314 
    315   /**
    316    * Number of fresh coins withdrawn, set by the
    317    * reveal callback as it comes from the exchange,
    318    * it is the length of the @e fresh_coins array.
    319    */
    320   unsigned int num_fresh_coins;
    321 
    322   /**
    323    * Expected HTTP response code.
    324    */
    325   unsigned int expected_response_code;
    326 
    327   /**
    328    * How often should we retry on (transient) failures?
    329    */
    330   unsigned int do_retry;
    331 
    332 };
    333 
    334 
    335 /**
    336  * State for a "refresh link" CMD.
    337  */
    338 struct RefreshLinkState
    339 {
    340   /**
    341    * Link to a "refresh reveal" command.
    342    */
    343   const char *reveal_reference;
    344 
    345   /**
    346    * Our command.
    347    */
    348   const struct TALER_TESTING_Command *cmd;
    349 
    350   /**
    351    * Handle to the ongoing operation.
    352    */
    353   struct TALER_EXCHANGE_LinkHandle *rlh;
    354 
    355   /**
    356    * Interpreter state.
    357    */
    358   struct TALER_TESTING_Interpreter *is;
    359 
    360   /**
    361    * Task scheduled to try later.
    362    */
    363   struct GNUNET_SCHEDULER_Task *retry_task;
    364 
    365   /**
    366    * How long do we wait until we retry?
    367    */
    368   struct GNUNET_TIME_Relative backoff;
    369 
    370   /**
    371    * How long did we wait in total for retries?
    372    */
    373   struct GNUNET_TIME_Relative total_backoff;
    374 
    375   /**
    376    * Expected HTTP response code.
    377    */
    378   unsigned int expected_response_code;
    379 
    380   /**
    381    * How often should we retry on (transient) failures?
    382    */
    383   unsigned int do_retry;
    384 
    385 };
    386 
    387 
    388 /**
    389  * Run the command.
    390  *
    391  * @param cls closure.
    392  * @param cmd the command to execute.
    393  * @param is the interpreter state.
    394  */
    395 static void
    396 melt_reveal_run (void *cls,
    397                  const struct TALER_TESTING_Command *cmd,
    398                  struct TALER_TESTING_Interpreter *is);
    399 
    400 
    401 /**
    402  * Task scheduled to re-try #melt_reveal_run.
    403  *
    404  * @param cls a `struct RefreshRevealState`
    405  */
    406 static void
    407 do_reveal_retry (void *cls)
    408 {
    409   struct RevealMeltState *rrs = cls;
    410 
    411   rrs->retry_task = NULL;
    412   TALER_TESTING_touch_cmd (rrs->is);
    413   melt_reveal_run (rrs,
    414                    NULL,
    415                    rrs->is);
    416 }
    417 
    418 
    419 /**
    420  * "refresh reveal" request callback; it checks that the response
    421  * code is expected and copies into its command's state the data
    422  * coming from the exchange, namely the fresh coins.
    423  *
    424  * @param rrs closure, a `struct RevealMeltState`
    425  * @param rmr HTTP response details
    426  */
    427 static void
    428 reveal_cb (struct RevealMeltState *rrs,
    429            const struct TALER_EXCHANGE_PostRevealMeltResponse *rmr)
    430 {
    431   const struct TALER_EXCHANGE_HttpResponse *hr = &rmr->hr;
    432   const struct TALER_TESTING_Command *melt_cmd;
    433 
    434   rrs->rmh = NULL;
    435   if (rrs->expected_response_code != hr->http_status)
    436   {
    437     if (0 != rrs->do_retry)
    438     {
    439       rrs->do_retry--;
    440       if ( (0 == hr->http_status) ||
    441            (TALER_EC_GENERIC_DB_SOFT_FAILURE == hr->ec) ||
    442            (MHD_HTTP_INTERNAL_SERVER_ERROR == hr->http_status) )
    443       {
    444         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    445                     "Retrying refresh reveal failed with %u/%d\n",
    446                     hr->http_status,
    447                     (int) hr->ec);
    448         /* on DB conflicts, do not use backoff */
    449         if (TALER_EC_GENERIC_DB_SOFT_FAILURE == hr->ec)
    450           rrs->backoff = GNUNET_TIME_UNIT_ZERO;
    451         else
    452           rrs->backoff = GNUNET_TIME_randomized_backoff (rrs->backoff,
    453                                                          MAX_BACKOFF);
    454         rrs->total_backoff = GNUNET_TIME_relative_add (rrs->total_backoff,
    455                                                        rrs->backoff);
    456         TALER_TESTING_inc_tries (rrs->is);
    457         rrs->retry_task = GNUNET_SCHEDULER_add_delayed (rrs->backoff,
    458                                                         &do_reveal_retry,
    459                                                         rrs);
    460         return;
    461       }
    462     }
    463     TALER_TESTING_unexpected_status_with_body (rrs->is,
    464                                                hr->http_status,
    465                                                rrs->expected_response_code,
    466                                                hr->reply);
    467     return;
    468   }
    469   melt_cmd = TALER_TESTING_interpreter_lookup_command (rrs->is,
    470                                                        rrs->melt_reference);
    471   if (NULL == melt_cmd)
    472   {
    473     GNUNET_break (0);
    474     TALER_TESTING_interpreter_fail (rrs->is);
    475     return;
    476   }
    477   switch (hr->http_status)
    478   {
    479   case MHD_HTTP_OK:
    480     rrs->num_fresh_coins = rmr->details.ok.num_coins;
    481     rrs->psa = GNUNET_new_array (rrs->num_fresh_coins,
    482                                  struct TALER_PlanchetMasterSecretP);
    483     rrs->fresh_coins = GNUNET_new_array (rrs->num_fresh_coins,
    484                                          struct TALER_TESTING_FreshCoinData);
    485     for (unsigned int i = 0; i<rrs->num_fresh_coins; i++)
    486     {
    487       const struct TALER_EXCHANGE_RevealedCoinInfo *coin
    488         = &rmr->details.ok.coins[i];
    489       struct TALER_TESTING_FreshCoinData *fc = &rrs->fresh_coins[i];
    490 
    491       rrs->psa[i] = coin->ps;
    492       fc->blinding_key = coin->bks;
    493       if (GNUNET_OK !=
    494           TALER_TESTING_get_trait_denom_pub (melt_cmd,
    495                                              i,
    496                                              &fc->pk))
    497       {
    498         GNUNET_break (0);
    499         TALER_TESTING_interpreter_fail (rrs->is);
    500         return;
    501       }
    502       fc->coin_priv = coin->coin_priv;
    503       GNUNET_CRYPTO_eddsa_key_get_public (&fc->coin_priv.eddsa_priv,
    504                                           &fc->coin_pub.eddsa_pub);
    505 
    506       if (NULL != coin->age_commitment_proof)
    507       {
    508         fc->age_commitment_proof =
    509           TALER_age_commitment_proof_duplicate (coin->age_commitment_proof);
    510         fc->h_age_commitment = coin->h_age_commitment;
    511       }
    512 
    513       TALER_denom_sig_copy (&fc->sig,
    514                             &coin->sig);
    515     }
    516     if (0 != rrs->total_backoff.rel_value_us)
    517     {
    518       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    519                   "Total reveal backoff for %s was %s\n",
    520                   rrs->cmd->label,
    521                   GNUNET_STRINGS_relative_time_to_string (rrs->total_backoff,
    522                                                           true));
    523     }
    524     break;
    525   default:
    526     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    527                 "Unknown HTTP status %u/%d\n",
    528                 hr->http_status,
    529                 (int) hr->ec);
    530   }
    531   TALER_TESTING_interpreter_next (rrs->is);
    532 }
    533 
    534 
    535 /**
    536  * Run the command.
    537  *
    538  * @param cls closure.
    539  * @param cmd the command to execute.
    540  * @param is the interpreter state.
    541  */
    542 static void
    543 melt_run (void *cls,
    544           const struct TALER_TESTING_Command *cmd,
    545           struct TALER_TESTING_Interpreter *is);
    546 
    547 
    548 /**
    549  * Run the command.
    550  *
    551  * @param cls closure.
    552  * @param cmd the command to execute.
    553  * @param is the interpreter state.
    554  */
    555 static void
    556 melt_reveal_run (void *cls,
    557                  const struct TALER_TESTING_Command *cmd,
    558                  struct TALER_TESTING_Interpreter *is)
    559 {
    560   struct RevealMeltState *rrs = cls;
    561   struct MeltState *ms;
    562   const struct TALER_TESTING_Command *melt_cmd;
    563 
    564   rrs->cmd = cmd;
    565   rrs->is = is;
    566   melt_cmd = TALER_TESTING_interpreter_lookup_command (is,
    567                                                        rrs->melt_reference);
    568   if (NULL == melt_cmd)
    569   {
    570     GNUNET_break (0);
    571     TALER_TESTING_interpreter_fail (rrs->is);
    572     return;
    573   }
    574   GNUNET_assert (melt_cmd->run == &melt_run);
    575   ms = melt_cmd->cls;
    576   ms->reveal_melt_input.rms = &ms->rms;
    577   ms->reveal_melt_input.melt_input = &ms->melt_input;
    578   ms->reveal_melt_input.blinding_seed = ms->no_blinding_seed
    579     ? NULL
    580     : &ms->blinding_seed;
    581   ms->reveal_melt_input.num_blinding_values = ms->num_blinding_values;
    582   ms->reveal_melt_input.blinding_values = ms->blinding_values;
    583   ms->reveal_melt_input.noreveal_index = ms->noreveal_index;
    584   rrs->rmh = TALER_EXCHANGE_post_reveal_melt_create (
    585     TALER_TESTING_interpreter_get_context (is),
    586     TALER_TESTING_get_exchange_url (is),
    587     &ms->reveal_melt_input);
    588   if (NULL == rrs->rmh)
    589   {
    590     GNUNET_break (0);
    591     TALER_TESTING_interpreter_fail (is);
    592     return;
    593   }
    594   GNUNET_assert (TALER_EC_NONE ==
    595                  TALER_EXCHANGE_post_reveal_melt_start (rrs->rmh,
    596                                                         &reveal_cb,
    597                                                         rrs));
    598 }
    599 
    600 
    601 /**
    602  * Free the state from a "refresh reveal" CMD, and possibly
    603  * cancel a pending operation thereof.
    604  *
    605  * @param cls closure.
    606  * @param cmd the command which is being cleaned up.
    607  */
    608 static void
    609 melt_reveal_cleanup (void *cls,
    610                      const struct TALER_TESTING_Command *cmd)
    611 {
    612   struct RevealMeltState *rrs = cls;
    613 
    614   (void) cmd;
    615   if (NULL != rrs->rmh)
    616   {
    617     TALER_TESTING_command_incomplete (rrs->is,
    618                                       cmd->label);
    619     TALER_EXCHANGE_post_reveal_melt_cancel (rrs->rmh);
    620     rrs->rmh = NULL;
    621   }
    622   if (NULL != rrs->retry_task)
    623   {
    624     GNUNET_SCHEDULER_cancel (rrs->retry_task);
    625     rrs->retry_task = NULL;
    626   }
    627 
    628   for (unsigned int j = 0; j < rrs->num_fresh_coins; j++)
    629   {
    630     TALER_denom_sig_free (&rrs->fresh_coins[j].sig);
    631     TALER_age_commitment_proof_free (rrs->fresh_coins[j].age_commitment_proof);
    632     GNUNET_free (rrs->fresh_coins[j].age_commitment_proof);
    633   }
    634   GNUNET_free (rrs->fresh_coins);
    635   GNUNET_free (rrs->psa);
    636   rrs->num_fresh_coins = 0;
    637   GNUNET_free (rrs);
    638 }
    639 
    640 
    641 /**
    642  * Task scheduled to re-try #melt_run.
    643  *
    644  * @param cls a `struct RefreshMeltState`
    645  */
    646 static void
    647 do_melt_retry (void *cls)
    648 {
    649   struct MeltState *rms = cls;
    650 
    651   rms->retry_task = NULL;
    652   TALER_TESTING_touch_cmd (rms->is);
    653   melt_run (rms,
    654             NULL,
    655             rms->is);
    656 }
    657 
    658 
    659 /**
    660  * Callback for a " /melt" operation; checks if the HTTP
    661  * response code is okay and re-run the melt operation if the
    662  * CMD was set to do so.
    663  *
    664  * @param ms closure.
    665  * @param mr melt response details
    666  */
    667 static void
    668 melt_cb (struct MeltState *ms,
    669          const struct TALER_EXCHANGE_PostMeltResponse *mr)
    670 {
    671   const struct TALER_EXCHANGE_HttpResponse *hr = &mr->hr;
    672 
    673   ms->mh = NULL;
    674   if (ms->expected_response_code != hr->http_status)
    675   {
    676     if (0 != ms->do_retry)
    677     {
    678       ms->do_retry--;
    679       if ( (0 == hr->http_status) ||
    680            (TALER_EC_GENERIC_DB_SOFT_FAILURE == hr->ec) ||
    681            (MHD_HTTP_INTERNAL_SERVER_ERROR == hr->http_status) )
    682       {
    683         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    684                     "Retrying refresh melt failed with %u/%d\n",
    685                     hr->http_status,
    686                     (int) hr->ec);
    687         /* on DB conflicts, do not use backoff */
    688         if (TALER_EC_GENERIC_DB_SOFT_FAILURE == hr->ec)
    689           ms->backoff = GNUNET_TIME_UNIT_ZERO;
    690         else
    691           ms->backoff = GNUNET_TIME_randomized_backoff (ms->backoff,
    692                                                         MAX_BACKOFF);
    693         ms->total_backoff = GNUNET_TIME_relative_add (ms->total_backoff,
    694                                                       ms->backoff);
    695         TALER_TESTING_inc_tries (ms->is);
    696         ms->retry_task = GNUNET_SCHEDULER_add_delayed (ms->backoff,
    697                                                        &do_melt_retry,
    698                                                        ms);
    699         return;
    700       }
    701     }
    702     TALER_TESTING_unexpected_status_with_body (ms->is,
    703                                                hr->http_status,
    704                                                ms->expected_response_code,
    705                                                hr->reply);
    706     return;
    707   }
    708   if (MHD_HTTP_OK == hr->http_status)
    709   {
    710     ms->noreveal_index = mr->details.ok.noreveal_index;
    711     if (mr->details.ok.num_melt_blinding_values != ms->num_fresh_coins)
    712     {
    713       GNUNET_break (0);
    714       TALER_TESTING_interpreter_fail (ms->is);
    715       return;
    716     }
    717     ms->no_blinding_seed = (NULL == mr->details.ok.blinding_seed);
    718     if (NULL != mr->details.ok.blinding_seed)
    719       ms->blinding_seed = *mr->details.ok.blinding_seed;
    720     ms->num_blinding_values = mr->details.ok.num_melt_blinding_values;
    721     if (NULL != ms->blinding_values)
    722     {
    723       for (unsigned int i = 0; i < ms->num_blinding_values; i++)
    724         TALER_denom_ewv_free (&ms->blinding_values[i]);
    725       GNUNET_free (ms->blinding_values);
    726     }
    727     ms->blinding_values = GNUNET_new_array (
    728       ms->num_blinding_values,
    729       struct TALER_ExchangeBlindingValues);
    730     for (unsigned int i = 0; i<ms->num_blinding_values; i++)
    731     {
    732       TALER_denom_ewv_copy (&ms->blinding_values[i],
    733                             &mr->details.ok.melt_blinding_values[i]);
    734     }
    735   }
    736   if (0 != ms->total_backoff.rel_value_us)
    737   {
    738     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    739                 "Total melt backoff for %s was %s\n",
    740                 ms->cmd->label,
    741                 GNUNET_STRINGS_relative_time_to_string (ms->total_backoff,
    742                                                         true));
    743   }
    744   if (ms->double_melt)
    745   {
    746     TALER_LOG_DEBUG ("Doubling the melt (%s)\n",
    747                      ms->cmd->label);
    748     ms->mh = TALER_EXCHANGE_post_melt_create (
    749       TALER_TESTING_interpreter_get_context (ms->is),
    750       TALER_TESTING_get_exchange_url (ms->is),
    751       TALER_TESTING_get_keys (ms->is),
    752       &ms->rms,
    753       &ms->melt_input);
    754     GNUNET_assert (NULL != ms->mh);
    755     GNUNET_assert (TALER_EC_NONE ==
    756                    TALER_EXCHANGE_post_melt_start (ms->mh,
    757                                                    &melt_cb,
    758                                                    ms));
    759     ms->double_melt = false;
    760     return;
    761   }
    762   TALER_TESTING_interpreter_next (ms->is);
    763 }
    764 
    765 
    766 /**
    767  * Run the command.
    768  *
    769  * @param cls closure.
    770  * @param cmd the command to execute.
    771  * @param is the interpreter state.
    772  */
    773 static void
    774 melt_run (void *cls,
    775           const struct TALER_TESTING_Command *cmd,
    776           struct TALER_TESTING_Interpreter *is)
    777 {
    778   static const char *default_melt_fresh_amounts[] = {
    779     "EUR:1", "EUR:1", "EUR:1", "EUR:0.1",
    780     NULL
    781   };
    782   struct MeltState *rms = cls;
    783   unsigned int num_fresh_coins;
    784   const char **melt_fresh_amounts;
    785 
    786   rms->cmd = cmd;
    787   if (NULL == (melt_fresh_amounts = rms->melt_fresh_amounts))
    788     melt_fresh_amounts = default_melt_fresh_amounts;
    789   rms->is = is;
    790   rms->noreveal_index = UINT16_MAX;
    791   TALER_refresh_master_setup_random (&rms->rms);
    792   for (num_fresh_coins = 0;
    793        NULL != melt_fresh_amounts[num_fresh_coins];
    794        num_fresh_coins++)
    795     ;
    796   rms->num_fresh_coins = num_fresh_coins;
    797   /* Free old data structure in case this is a retry! */
    798   if (NULL != rms->fresh_pks)
    799   {
    800     for (unsigned int i = 0; i < rms->num_fresh_coins; i++)
    801       TALER_denom_pub_free (&rms->fresh_pks[i].key);
    802     GNUNET_free (rms->fresh_pks);
    803   }
    804   rms->fresh_pks = GNUNET_new_array (
    805     num_fresh_coins,
    806     struct TALER_EXCHANGE_DenomPublicKey);
    807   {
    808     struct TALER_Amount melt_amount;
    809     struct TALER_Amount fresh_amount;
    810     const struct TALER_AgeCommitmentProof *age_commitment_proof = NULL;
    811     const struct TALER_AgeCommitmentHashP *h_age_commitment = NULL;
    812     const struct TALER_DenominationSignature *melt_sig;
    813     const struct TALER_EXCHANGE_DenomPublicKey *melt_denom_pub;
    814     const struct TALER_TESTING_Command *coin_command;
    815     bool age_restricted_denom;
    816 
    817     if (NULL == (coin_command
    818                    = TALER_TESTING_interpreter_lookup_command (
    819                        is,
    820                        rms->coin_reference)))
    821     {
    822       GNUNET_break (0);
    823       TALER_TESTING_interpreter_fail (rms->is);
    824       return;
    825     }
    826 
    827     if (GNUNET_OK !=
    828         TALER_TESTING_get_trait_coin_priv (coin_command,
    829                                            0,
    830                                            &rms->melt_priv))
    831     {
    832       GNUNET_break (0);
    833       TALER_TESTING_interpreter_fail (rms->is);
    834       return;
    835     }
    836     if (GNUNET_OK !=
    837         TALER_TESTING_get_trait_age_commitment_proof (coin_command,
    838                                                       0,
    839                                                       &age_commitment_proof))
    840     {
    841       GNUNET_break (0);
    842       TALER_TESTING_interpreter_fail (rms->is);
    843       return;
    844     }
    845 
    846     if (GNUNET_OK !=
    847         TALER_TESTING_get_trait_h_age_commitment (coin_command,
    848                                                   0,
    849                                                   &h_age_commitment))
    850     {
    851       GNUNET_break (0);
    852       TALER_TESTING_interpreter_fail (rms->is);
    853       return;
    854     }
    855     if (GNUNET_OK !=
    856         TALER_TESTING_get_trait_denom_sig (coin_command,
    857                                            0,
    858                                            &melt_sig))
    859     {
    860       GNUNET_break (0);
    861       TALER_TESTING_interpreter_fail (rms->is);
    862       return;
    863     }
    864     if (GNUNET_OK !=
    865         TALER_TESTING_get_trait_denom_pub (coin_command,
    866                                            0,
    867                                            &melt_denom_pub))
    868     {
    869       GNUNET_break (0);
    870       TALER_TESTING_interpreter_fail (rms->is);
    871       return;
    872     }
    873 
    874     /* Melt amount starts with the melt fee of the old coin; we'll add the
    875        values and withdraw fees of the fresh coins next */
    876     melt_amount = melt_denom_pub->fees.refresh;
    877     age_restricted_denom = melt_denom_pub->key.age_mask.bits != 0;
    878     GNUNET_assert (age_restricted_denom == (NULL != age_commitment_proof));
    879     GNUNET_assert ((NULL == age_commitment_proof) ||
    880                    (0 < age_commitment_proof->commitment.num));
    881     for (unsigned int i = 0; i<num_fresh_coins; i++)
    882     {
    883       const struct TALER_EXCHANGE_DenomPublicKey *fresh_pk;
    884 
    885       if (GNUNET_OK !=
    886           TALER_string_to_amount (melt_fresh_amounts[i],
    887                                   &fresh_amount))
    888       {
    889         GNUNET_break (0);
    890         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    891                     "Failed to parse amount `%s' at index %u\n",
    892                     melt_fresh_amounts[i],
    893                     i);
    894         TALER_TESTING_interpreter_fail (rms->is);
    895         return;
    896       }
    897       fresh_pk = TALER_TESTING_find_pk (TALER_TESTING_get_keys (rms->is),
    898                                         &fresh_amount,
    899                                         age_restricted_denom);
    900       if (NULL == fresh_pk)
    901       {
    902         GNUNET_break (0);
    903         /* Subroutine logs specific error */
    904         TALER_TESTING_interpreter_fail (rms->is);
    905         return;
    906       }
    907       GNUNET_assert (0 <=
    908                      TALER_amount_add (&melt_amount,
    909                                        &melt_amount,
    910                                        &fresh_amount));
    911       GNUNET_assert (0 <=
    912                      TALER_amount_add (&melt_amount,
    913                                        &melt_amount,
    914                                        &fresh_pk->fees.withdraw));
    915       rms->fresh_pks[i] = *fresh_pk;
    916       /* Make a deep copy of the RSA key */
    917       TALER_denom_pub_copy (&rms->fresh_pks[i].key,
    918                             &fresh_pk->key);
    919     } /* end for */
    920 
    921     rms->melt_input.melt_priv = *rms->melt_priv;
    922     GNUNET_CRYPTO_eddsa_key_get_public (&rms->melt_priv->eddsa_priv,
    923                                         &rms->melt_pub.eddsa_pub);
    924     rms->melt_input.melt_amount = melt_amount;
    925     rms->melt_input.melt_sig = *melt_sig;
    926     rms->melt_input.melt_pk = *melt_denom_pub;
    927 
    928     if (NULL != age_commitment_proof)
    929     {
    930       GNUNET_assert (NULL != h_age_commitment);
    931       rms->melt_input.melt_age_commitment_proof = age_commitment_proof;
    932       rms->melt_input.melt_h_age_commitment = h_age_commitment;
    933     }
    934     rms->melt_input.fresh_denom_pubs = rms->fresh_pks;
    935     rms->melt_input.num_fresh_denom_pubs = num_fresh_coins;
    936 
    937     GNUNET_assert (age_restricted_denom ==
    938                    (NULL != age_commitment_proof));
    939     GNUNET_assert ((NULL == age_commitment_proof) ||
    940                    (0 < age_commitment_proof->commitment.num));
    941 
    942     rms->che.type = TALER_EXCHANGE_CTT_MELT;
    943     rms->che.amount = melt_amount;
    944     if (NULL != age_commitment_proof)
    945       rms->che.details.melt.h_age_commitment = *h_age_commitment;
    946     else
    947       rms->che.details.melt.no_hac = true;
    948 
    949     rms->mh = TALER_EXCHANGE_post_melt_create (
    950       TALER_TESTING_interpreter_get_context (is),
    951       TALER_TESTING_get_exchange_url (is),
    952       TALER_TESTING_get_keys (is),
    953       &rms->rms,
    954       &rms->melt_input);
    955 
    956     if (NULL == rms->mh)
    957     {
    958       GNUNET_break (0);
    959       TALER_TESTING_interpreter_fail (rms->is);
    960       return;
    961     }
    962     GNUNET_assert (TALER_EC_NONE ==
    963                    TALER_EXCHANGE_post_melt_start (rms->mh,
    964                                                    &melt_cb,
    965                                                    rms));
    966   }
    967 }
    968 
    969 
    970 /**
    971  * Free the "refresh melt" CMD state, and possibly cancel a
    972  * pending operation thereof.
    973  *
    974  * @param cls closure, must be a `struct RefreshMeltState`.
    975  * @param cmd the command which is being cleaned up.
    976  */
    977 static void
    978 melt_cleanup (void *cls,
    979               const struct TALER_TESTING_Command *cmd)
    980 {
    981   struct MeltState *rms = cls;
    982 
    983   (void) cmd;
    984   if (NULL != rms->mh)
    985   {
    986     TALER_TESTING_command_incomplete (rms->is,
    987                                       cmd->label);
    988     TALER_EXCHANGE_post_melt_cancel (rms->mh);
    989     rms->mh = NULL;
    990   }
    991   if (NULL != rms->retry_task)
    992   {
    993     GNUNET_SCHEDULER_cancel (rms->retry_task);
    994     rms->retry_task = NULL;
    995   }
    996   if (NULL != rms->fresh_pks)
    997   {
    998     for (unsigned int i = 0; i < rms->num_fresh_coins; i++)
    999       TALER_denom_pub_free (&rms->fresh_pks[i].key);
   1000     GNUNET_free (rms->fresh_pks);
   1001   }
   1002   if (NULL != rms->blinding_values)
   1003   {
   1004     for (unsigned int i = 0; i < rms->num_blinding_values; i++)
   1005       TALER_denom_ewv_free (&rms->blinding_values[i]);
   1006     GNUNET_free (rms->blinding_values);
   1007   }
   1008   GNUNET_free (rms->melt_fresh_amounts);
   1009   GNUNET_free (rms);
   1010 }
   1011 
   1012 
   1013 /**
   1014  * Offer internal data to the "refresh melt" CMD.
   1015  *
   1016  * @param cls closure.
   1017  * @param[out] ret result (could be anything).
   1018  * @param trait name of the trait.
   1019  * @param index index number of the object to offer.
   1020  * @return #GNUNET_OK on success.
   1021  */
   1022 static enum GNUNET_GenericReturnValue
   1023 melt_traits (void *cls,
   1024              const void **ret,
   1025              const char *trait,
   1026              unsigned int index)
   1027 {
   1028   struct MeltState *rms = cls;
   1029 
   1030   if (index >= rms->num_fresh_coins)
   1031   {
   1032     GNUNET_break (0);
   1033     return GNUNET_SYSERR;
   1034   }
   1035   {
   1036     struct TALER_TESTING_Trait traits[] = {
   1037       TALER_TESTING_make_trait_denom_pub (index,
   1038                                           &rms->fresh_pks[index]),
   1039       TALER_TESTING_make_trait_coin_priv (0,
   1040                                           rms->melt_priv),
   1041       TALER_TESTING_make_trait_coin_pub (0,
   1042                                          &rms->melt_pub),
   1043       TALER_TESTING_make_trait_coin_history (0,
   1044                                              &rms->che),
   1045       TALER_TESTING_make_trait_age_commitment_proof (
   1046         index,
   1047         rms->melt_input.melt_age_commitment_proof),
   1048       TALER_TESTING_make_trait_h_age_commitment (
   1049         index,
   1050         rms->melt_input.melt_h_age_commitment),
   1051       TALER_TESTING_make_trait_refresh_seed (&rms->rms),
   1052       (NULL != rms->reveal_melt_input.blinding_values)
   1053       ? TALER_TESTING_make_trait_exchange_blinding_values (
   1054         index,
   1055         &rms->reveal_melt_input.blinding_values[index])
   1056       : TALER_TESTING_trait_end (),
   1057       TALER_TESTING_trait_end ()
   1058     };
   1059 
   1060     return TALER_TESTING_get_trait (traits,
   1061                                     ret,
   1062                                     trait,
   1063                                     index);
   1064   }
   1065 }
   1066 
   1067 
   1068 /**
   1069  * Parse list of amounts for melt operation.
   1070  *
   1071  * @param[in,out] rms where to store the list
   1072  * @param ap NULL-termianted list of amounts to be melted (one per fresh coin)
   1073  * @return #GNUNET_OK on success
   1074  */
   1075 static enum GNUNET_GenericReturnValue
   1076 parse_amounts (struct MeltState *rms,
   1077                va_list ap)
   1078 {
   1079   unsigned int len;
   1080   unsigned int off;
   1081   const char *amount;
   1082 
   1083   len = 0;
   1084   off = 0;
   1085   while (NULL != (amount = va_arg (ap, const char *)))
   1086   {
   1087     if (len == off)
   1088     {
   1089       struct TALER_Amount a;
   1090 
   1091       GNUNET_array_grow (rms->melt_fresh_amounts,
   1092                          len,
   1093                          off + 16);
   1094       if (GNUNET_OK !=
   1095           TALER_string_to_amount (amount, &a))
   1096       {
   1097         GNUNET_break (0);
   1098         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1099                     "Failed to parse amount `%s' at index %u\n",
   1100                     amount, off);
   1101         GNUNET_free (rms->melt_fresh_amounts);
   1102         rms->melt_fresh_amounts = NULL;
   1103         return GNUNET_SYSERR;
   1104       }
   1105       rms->melt_fresh_amounts[off++] = amount;
   1106     }
   1107   }
   1108   if (0 == off)
   1109     return GNUNET_OK; /* no amounts given == use defaults! */
   1110   /* ensure NULL-termination */
   1111   GNUNET_array_grow (rms->melt_fresh_amounts,
   1112                      len,
   1113                      off + 1);
   1114   return GNUNET_OK;
   1115 }
   1116 
   1117 
   1118 struct TALER_TESTING_Command
   1119 TALER_TESTING_cmd_melt (const char *label,
   1120                         const char *coin_reference,
   1121                         unsigned int expected_response_code,
   1122                         ...)
   1123 {
   1124   struct MeltState *rms;
   1125   va_list ap;
   1126 
   1127   rms = GNUNET_new (struct MeltState);
   1128   rms->coin_reference = coin_reference;
   1129   rms->expected_response_code = expected_response_code;
   1130   va_start (ap,
   1131             expected_response_code);
   1132   GNUNET_assert (GNUNET_OK ==
   1133                  parse_amounts (rms, ap));
   1134   va_end (ap);
   1135   {
   1136     struct TALER_TESTING_Command cmd = {
   1137       .label = label,
   1138       .cls = rms,
   1139       .run = &melt_run,
   1140       .cleanup = &melt_cleanup,
   1141       .traits = &melt_traits
   1142     };
   1143 
   1144     return cmd;
   1145   }
   1146 }
   1147 
   1148 
   1149 struct TALER_TESTING_Command
   1150 TALER_TESTING_cmd_melt_double (const char *label,
   1151                                const char *coin_reference,
   1152                                unsigned int expected_response_code,
   1153                                ...)
   1154 {
   1155   struct MeltState *rms;
   1156   va_list ap;
   1157 
   1158   rms = GNUNET_new (struct MeltState);
   1159   rms->coin_reference = coin_reference;
   1160   rms->expected_response_code = expected_response_code;
   1161   rms->double_melt = true;
   1162   va_start (ap,
   1163             expected_response_code);
   1164   GNUNET_assert (GNUNET_OK ==
   1165                  parse_amounts (rms, ap));
   1166   va_end (ap);
   1167   {
   1168     struct TALER_TESTING_Command cmd = {
   1169       .label = label,
   1170       .cls = rms,
   1171       .run = &melt_run,
   1172       .cleanup = &melt_cleanup,
   1173       .traits = &melt_traits
   1174     };
   1175 
   1176     return cmd;
   1177   }
   1178 }
   1179 
   1180 
   1181 struct TALER_TESTING_Command
   1182 TALER_TESTING_cmd_melt_with_retry (struct TALER_TESTING_Command cmd)
   1183 {
   1184   struct MeltState *rms;
   1185 
   1186   GNUNET_assert (&melt_run == cmd.run);
   1187   rms = cmd.cls;
   1188   rms->do_retry = NUM_RETRIES;
   1189   return cmd;
   1190 }
   1191 
   1192 
   1193 /**
   1194  * Offer internal data from a "refresh reveal" CMD.
   1195  *
   1196  * @param cls closure.
   1197  * @param[out] ret result (could be anything).
   1198  * @param trait name of the trait.
   1199  * @param index index number of the object to offer.
   1200  * @return #GNUNET_OK on success.
   1201  */
   1202 static enum GNUNET_GenericReturnValue
   1203 melt_reveal_traits (void *cls,
   1204                     const void **ret,
   1205                     const char *trait,
   1206                     unsigned int index)
   1207 {
   1208   struct RevealMeltState *rrs = cls;
   1209 
   1210   if (index >= rrs->num_fresh_coins)
   1211     return GNUNET_SYSERR;
   1212 
   1213   {
   1214     struct TALER_TESTING_Trait traits[] = {
   1215       TALER_TESTING_make_trait_coin_priv (
   1216         index,
   1217         &rrs->fresh_coins[index].coin_priv),
   1218       TALER_TESTING_make_trait_coin_pub (
   1219         index,
   1220         &rrs->fresh_coins[index].coin_pub),
   1221       TALER_TESTING_make_trait_age_commitment_proof (
   1222         index,
   1223         rrs->fresh_coins[index].age_commitment_proof),
   1224       TALER_TESTING_make_trait_h_age_commitment (
   1225         index,
   1226         &rrs->fresh_coins[index].h_age_commitment),
   1227       TALER_TESTING_make_trait_denom_pub (
   1228         index,
   1229         rrs->fresh_coins[index].pk),
   1230       TALER_TESTING_make_trait_denom_sig (
   1231         index,
   1232         &rrs->fresh_coins[index].sig),
   1233       TALER_TESTING_make_trait_blinding_key (
   1234         index,
   1235         &rrs->fresh_coins[index].blinding_key),
   1236       TALER_TESTING_make_trait_array_length (
   1237         &rrs->num_fresh_coins),
   1238       TALER_TESTING_make_trait_fresh_coins (
   1239         (const struct TALER_TESTING_FreshCoinData **) &rrs->fresh_coins),
   1240       TALER_TESTING_make_trait_planchet_secrets (index,
   1241                                                  &rrs->psa[index]),
   1242       TALER_TESTING_trait_end ()
   1243     };
   1244 
   1245     return TALER_TESTING_get_trait (traits,
   1246                                     ret,
   1247                                     trait,
   1248                                     index);
   1249   }
   1250 }
   1251 
   1252 
   1253 struct TALER_TESTING_Command
   1254 TALER_TESTING_cmd_melt_reveal (const char *label,
   1255                                const char *melt_reference,
   1256                                unsigned int expected_response_code)
   1257 {
   1258   struct RevealMeltState *rrs;
   1259 
   1260   rrs = GNUNET_new (struct RevealMeltState);
   1261   rrs->melt_reference = melt_reference;
   1262   rrs->expected_response_code = expected_response_code;
   1263   {
   1264     struct TALER_TESTING_Command cmd = {
   1265       .cls = rrs,
   1266       .label = label,
   1267       .run = &melt_reveal_run,
   1268       .cleanup = &melt_reveal_cleanup,
   1269       .traits = &melt_reveal_traits
   1270     };
   1271 
   1272     return cmd;
   1273   }
   1274 }
   1275 
   1276 
   1277 struct TALER_TESTING_Command
   1278 TALER_TESTING_cmd_melt_reveal_with_retry (struct TALER_TESTING_Command cmd)
   1279 {
   1280   struct RevealMeltState *rrs;
   1281 
   1282   GNUNET_assert (&melt_reveal_run == cmd.run);
   1283   rrs = cmd.cls;
   1284   rrs->do_retry = NUM_RETRIES;
   1285   return cmd;
   1286 }