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 (36223B)


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