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


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