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


      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    * Fresh age commitment for the coin with proof and its hash, NULL if not
     76    * applicable.
     77    */
     78   struct TALER_AgeCommitmentProof *age_commitment_proof;
     79   struct TALER_AgeCommitmentHashP h_age_commitment;
     80 
     81   /**
     82    * The blinding key (needed for recoup operations).
     83    */
     84   union GNUNET_CRYPTO_BlindingSecretP blinding_key;
     85 
     86 };
     87 
     88 
     89 /**
     90  * State for a "refresh melt" command.
     91  */
     92 struct MeltState
     93 {
     94 
     95   /**
     96    * Reference to reserve_withdraw operations for coin to
     97    * be used for the /refresh/melt operation.
     98    */
     99   const char *coin_reference;
    100 
    101   /**
    102    * Our command.
    103    */
    104   const struct TALER_TESTING_Command *cmd;
    105 
    106   /**
    107    * Reference to a previous melt command.
    108    */
    109   const char *melt_reference;
    110 
    111   /**
    112    * Melt handle while operation is running.
    113    */
    114   struct TALER_EXCHANGE_MeltHandle_v27 *mh;
    115 
    116   /**
    117    * Expected entry in the coin history created by this
    118    * operation.
    119    */
    120   struct TALER_EXCHANGE_CoinHistoryEntry che;
    121 
    122   /**
    123    * Interpreter state.
    124    */
    125   struct TALER_TESTING_Interpreter *is;
    126 
    127   /**
    128    * The input for the call to /melt
    129    */
    130   struct TALER_EXCHANGE_MeltInput melt_input;
    131 
    132   /**
    133    * Length of the @a blinding_values array with the exchange values
    134    * and blinding keys we are using.
    135    */
    136   unsigned int num_blinding_values;
    137 
    138   /**
    139    * Blinding values returned per coin.
    140    */
    141   struct TALER_ExchangeBlindingValues *blinding_values;
    142 
    143   /**
    144    * The input for the call to /reveal-melt
    145    */
    146   struct TALER_EXCHANGE_RevealMeltInput reveal_melt_input;
    147 
    148   /**
    149    * Array of the denomination public keys
    150    * corresponding to the @e num_fresh_coins;
    151    */
    152   struct TALER_EXCHANGE_DenomPublicKey *fresh_pks;
    153 
    154   /**
    155    * Private key of the dirty coin being melted.
    156    */
    157   const struct TALER_CoinSpendPrivateKeyP *melt_priv;
    158 
    159   /**
    160    * Public key of the dirty coin being melted.
    161    */
    162   struct TALER_CoinSpendPublicKeyP melt_pub;
    163 
    164   /**
    165    * Entropy seed for the refresh-melt operation.
    166    */
    167   struct TALER_RefreshMasterSecretP rms;
    168 
    169   /**
    170    * The master seed to derive the nonces from
    171    */
    172   struct TALER_PublicRefreshMasterSeedP refresh_seed;
    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_RevealMeltHandle *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_RevealMeltResponse *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 (rrs->is,
    460                                      hr->http_status,
    461                                      rrs->expected_response_code);
    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 
    499       if (NULL != coin->age_commitment_proof)
    500       {
    501         fc->age_commitment_proof =
    502           TALER_age_commitment_proof_duplicate (coin->age_commitment_proof);
    503         fc->h_age_commitment = coin->h_age_commitment;
    504       }
    505 
    506       TALER_denom_sig_copy (&fc->sig,
    507                             &coin->sig);
    508     }
    509     if (0 != rrs->total_backoff.rel_value_us)
    510     {
    511       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    512                   "Total reveal backoff for %s was %s\n",
    513                   rrs->cmd->label,
    514                   GNUNET_STRINGS_relative_time_to_string (rrs->total_backoff,
    515                                                           true));
    516     }
    517     break;
    518   default:
    519     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    520                 "Unknown HTTP status %u/%d\n",
    521                 hr->http_status,
    522                 (int) hr->ec);
    523   }
    524   TALER_TESTING_interpreter_next (rrs->is);
    525 }
    526 
    527 
    528 /**
    529  * Run the command.
    530  *
    531  * @param cls closure.
    532  * @param cmd the command to execute.
    533  * @param is the interpreter state.
    534  */
    535 static void
    536 melt_run (void *cls,
    537           const struct TALER_TESTING_Command *cmd,
    538           struct TALER_TESTING_Interpreter *is);
    539 
    540 
    541 /**
    542  * Run the command.
    543  *
    544  * @param cls closure.
    545  * @param cmd the command to execute.
    546  * @param is the interpreter state.
    547  */
    548 static void
    549 melt_reveal_run (void *cls,
    550                  const struct TALER_TESTING_Command *cmd,
    551                  struct TALER_TESTING_Interpreter *is)
    552 {
    553   struct RevealMeltState *rrs = cls;
    554   struct MeltState *rms;
    555   const struct TALER_TESTING_Command *melt_cmd;
    556 
    557   rrs->cmd = cmd;
    558   rrs->is = is;
    559   melt_cmd = TALER_TESTING_interpreter_lookup_command (is,
    560                                                        rrs->melt_reference);
    561   if (NULL == melt_cmd)
    562   {
    563     GNUNET_break (0);
    564     TALER_TESTING_interpreter_fail (rrs->is);
    565     return;
    566   }
    567   GNUNET_assert (melt_cmd->run == &melt_run);
    568   rms = melt_cmd->cls;
    569   rms->reveal_melt_input.rms = &rms->rms;
    570   rms->reveal_melt_input.melt_input = &rms->melt_input;
    571   rms->reveal_melt_input.blinding_seed = rms->no_blinding_seed
    572     ? NULL
    573     : &rms->blinding_seed;
    574   rms->reveal_melt_input.num_blinding_values = rms->num_blinding_values;
    575   rms->reveal_melt_input.blinding_values = rms->blinding_values;
    576   rms->reveal_melt_input.noreveal_index = rms->noreveal_index;
    577   rrs->rmh = TALER_EXCHANGE_reveal_melt (
    578     TALER_TESTING_interpreter_get_context (is),
    579     TALER_TESTING_get_exchange_url (is),
    580     &rms->reveal_melt_input,
    581     &reveal_cb,
    582     rrs);
    583   if (NULL == rrs->rmh)
    584   {
    585     GNUNET_break (0);
    586     TALER_TESTING_interpreter_fail (is);
    587     return;
    588   }
    589 }
    590 
    591 
    592 /**
    593  * Free the state from a "refresh reveal" CMD, and possibly
    594  * cancel a pending operation thereof.
    595  *
    596  * @param cls closure.
    597  * @param cmd the command which is being cleaned up.
    598  */
    599 static void
    600 melt_reveal_cleanup (void *cls,
    601                      const struct TALER_TESTING_Command *cmd)
    602 {
    603   struct RevealMeltState *rrs = cls;
    604 
    605   (void) cmd;
    606   if (NULL != rrs->rmh)
    607   {
    608     TALER_TESTING_command_incomplete (rrs->is,
    609                                       cmd->label);
    610     TALER_EXCHANGE_reveal_melt_cancel (rrs->rmh);
    611     rrs->rmh = NULL;
    612   }
    613   if (NULL != rrs->retry_task)
    614   {
    615     GNUNET_SCHEDULER_cancel (rrs->retry_task);
    616     rrs->retry_task = NULL;
    617   }
    618 
    619   for (unsigned int j = 0; j < rrs->num_fresh_coins; j++)
    620   {
    621     TALER_denom_sig_free (&rrs->fresh_coins[j].sig);
    622     TALER_age_commitment_proof_free (rrs->fresh_coins[j].age_commitment_proof);
    623     GNUNET_free (rrs->fresh_coins[j].age_commitment_proof);
    624   }
    625   GNUNET_free (rrs->fresh_coins);
    626   GNUNET_free (rrs->psa);
    627   rrs->num_fresh_coins = 0;
    628   GNUNET_free (rrs);
    629 }
    630 
    631 
    632 /**
    633  * Task scheduled to re-try #melt_run.
    634  *
    635  * @param cls a `struct RefreshMeltState`
    636  */
    637 static void
    638 do_melt_retry (void *cls)
    639 {
    640   struct MeltState *rms = cls;
    641 
    642   rms->retry_task = NULL;
    643   TALER_TESTING_touch_cmd (rms->is);
    644   melt_run (rms,
    645             NULL,
    646             rms->is);
    647 }
    648 
    649 
    650 /**
    651  * Callback for a " /melt" operation; checks if the HTTP
    652  * response code is okay and re-run the melt operation if the
    653  * CMD was set to do so.
    654  *
    655  * @param cls closure.
    656  * @param mr melt response details
    657  */
    658 static void
    659 melt_cb (void *cls,
    660          const struct TALER_EXCHANGE_MeltResponse_v27 *mr)
    661 {
    662   struct MeltState *rms = cls;
    663   const struct TALER_EXCHANGE_HttpResponse *hr = &mr->hr;
    664 
    665   rms->mh = NULL;
    666   if (rms->expected_response_code != hr->http_status)
    667   {
    668     if (0 != rms->do_retry)
    669     {
    670       rms->do_retry--;
    671       if ( (0 == hr->http_status) ||
    672            (TALER_EC_GENERIC_DB_SOFT_FAILURE == hr->ec) ||
    673            (MHD_HTTP_INTERNAL_SERVER_ERROR == hr->http_status) )
    674       {
    675         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    676                     "Retrying refresh melt failed with %u/%d\n",
    677                     hr->http_status,
    678                     (int) hr->ec);
    679         /* on DB conflicts, do not use backoff */
    680         if (TALER_EC_GENERIC_DB_SOFT_FAILURE == hr->ec)
    681           rms->backoff = GNUNET_TIME_UNIT_ZERO;
    682         else
    683           rms->backoff = GNUNET_TIME_randomized_backoff (rms->backoff,
    684                                                          MAX_BACKOFF);
    685         rms->total_backoff = GNUNET_TIME_relative_add (rms->total_backoff,
    686                                                        rms->backoff);
    687         TALER_TESTING_inc_tries (rms->is);
    688         rms->retry_task = GNUNET_SCHEDULER_add_delayed (rms->backoff,
    689                                                         &do_melt_retry,
    690                                                         rms);
    691         return;
    692       }
    693     }
    694     TALER_TESTING_unexpected_status_with_body (rms->is,
    695                                                hr->http_status,
    696                                                rms->expected_response_code,
    697                                                hr->reply);
    698     return;
    699   }
    700   if (MHD_HTTP_OK == hr->http_status)
    701   {
    702     rms->noreveal_index = mr->details.ok.noreveal_index;
    703     if (mr->details.ok.num_melt_blinding_values != rms->num_fresh_coins)
    704     {
    705       GNUNET_break (0);
    706       TALER_TESTING_interpreter_fail (rms->is);
    707       return;
    708     }
    709     rms->no_blinding_seed = (NULL == mr->details.ok.blinding_seed);
    710     if (NULL != mr->details.ok.blinding_seed)
    711       rms->blinding_seed = *mr->details.ok.blinding_seed;
    712     rms->num_blinding_values = mr->details.ok.num_melt_blinding_values;
    713     if (NULL != rms->blinding_values)
    714     {
    715       GNUNET_break (0); /* can this this happen? Check! */
    716       for (unsigned int i = 0; i < rms->num_blinding_values; i++)
    717         TALER_denom_ewv_free (&rms->blinding_values[i]);
    718       GNUNET_free (rms->blinding_values);
    719     }
    720     rms->blinding_values = GNUNET_new_array (
    721       rms->num_blinding_values,
    722       struct TALER_ExchangeBlindingValues);
    723     for (unsigned int i = 0; i<rms->num_blinding_values; i++)
    724     {
    725       TALER_denom_ewv_copy (&rms->blinding_values[i],
    726                             &mr->details.ok.melt_blinding_values[i]);
    727     }
    728   }
    729   if (0 != rms->total_backoff.rel_value_us)
    730   {
    731     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    732                 "Total melt backoff for %s was %s\n",
    733                 rms->cmd->label,
    734                 GNUNET_STRINGS_relative_time_to_string (rms->total_backoff,
    735                                                         true));
    736   }
    737   if (rms->double_melt)
    738   {
    739     TALER_LOG_DEBUG ("Doubling the melt (%s)\n",
    740                      rms->cmd->label);
    741     rms->mh = TALER_EXCHANGE_melt_v27 (
    742       TALER_TESTING_interpreter_get_context (rms->is),
    743       TALER_TESTING_get_exchange_url (rms->is),
    744       TALER_TESTING_get_keys (rms->is),
    745       &rms->rms,
    746       &rms->melt_input,
    747       &melt_cb,
    748       rms);
    749     rms->double_melt = false;
    750     return;
    751   }
    752   TALER_TESTING_interpreter_next (rms->is);
    753 }
    754 
    755 
    756 /**
    757  * Run the command.
    758  *
    759  * @param cls closure.
    760  * @param cmd the command to execute.
    761  * @param is the interpreter state.
    762  */
    763 static void
    764 melt_run (void *cls,
    765           const struct TALER_TESTING_Command *cmd,
    766           struct TALER_TESTING_Interpreter *is)
    767 {
    768   static const char *default_melt_fresh_amounts[] = {
    769     "EUR:1", "EUR:1", "EUR:1", "EUR:0.1",
    770     NULL
    771   };
    772   struct MeltState *rms = cls;
    773   unsigned int num_fresh_coins;
    774   const char **melt_fresh_amounts;
    775 
    776   rms->cmd = cmd;
    777   if (NULL == (melt_fresh_amounts = rms->melt_fresh_amounts))
    778     melt_fresh_amounts = default_melt_fresh_amounts;
    779   rms->is = is;
    780   rms->noreveal_index = UINT16_MAX;
    781   TALER_refresh_master_setup_random (&rms->rms);
    782   for (num_fresh_coins = 0;
    783        NULL != melt_fresh_amounts[num_fresh_coins];
    784        num_fresh_coins++)
    785     ;
    786   rms->num_fresh_coins = num_fresh_coins;
    787   /* Free old data structure in case this is a retry! */
    788   if (NULL != rms->fresh_pks)
    789   {
    790     for (unsigned int i = 0; i < rms->num_fresh_coins; i++)
    791       TALER_denom_pub_free (&rms->fresh_pks[i].key);
    792     GNUNET_free (rms->fresh_pks);
    793   }
    794   rms->fresh_pks = GNUNET_new_array (
    795     num_fresh_coins,
    796     struct TALER_EXCHANGE_DenomPublicKey);
    797   {
    798     struct TALER_Amount melt_amount;
    799     struct TALER_Amount fresh_amount;
    800     const struct TALER_AgeCommitmentProof *age_commitment_proof = NULL;
    801     const struct TALER_AgeCommitmentHashP *h_age_commitment = NULL;
    802     const struct TALER_DenominationSignature *melt_sig;
    803     const struct TALER_EXCHANGE_DenomPublicKey *melt_denom_pub;
    804     const struct TALER_TESTING_Command *coin_command;
    805     bool age_restricted_denom;
    806 
    807     if (NULL == (coin_command
    808                    = TALER_TESTING_interpreter_lookup_command (
    809                        is,
    810                        rms->coin_reference)))
    811     {
    812       GNUNET_break (0);
    813       TALER_TESTING_interpreter_fail (rms->is);
    814       return;
    815     }
    816 
    817     if (GNUNET_OK !=
    818         TALER_TESTING_get_trait_coin_priv (coin_command,
    819                                            0,
    820                                            &rms->melt_priv))
    821     {
    822       GNUNET_break (0);
    823       TALER_TESTING_interpreter_fail (rms->is);
    824       return;
    825     }
    826     if (GNUNET_OK !=
    827         TALER_TESTING_get_trait_age_commitment_proof (coin_command,
    828                                                       0,
    829                                                       &age_commitment_proof))
    830     {
    831       GNUNET_break (0);
    832       TALER_TESTING_interpreter_fail (rms->is);
    833       return;
    834     }
    835 
    836     if (GNUNET_OK !=
    837         TALER_TESTING_get_trait_h_age_commitment (coin_command,
    838                                                   0,
    839                                                   &h_age_commitment))
    840     {
    841       GNUNET_break (0);
    842       TALER_TESTING_interpreter_fail (rms->is);
    843       return;
    844     }
    845     if (GNUNET_OK !=
    846         TALER_TESTING_get_trait_denom_sig (coin_command,
    847                                            0,
    848                                            &melt_sig))
    849     {
    850       GNUNET_break (0);
    851       TALER_TESTING_interpreter_fail (rms->is);
    852       return;
    853     }
    854     if (GNUNET_OK !=
    855         TALER_TESTING_get_trait_denom_pub (coin_command,
    856                                            0,
    857                                            &melt_denom_pub))
    858     {
    859       GNUNET_break (0);
    860       TALER_TESTING_interpreter_fail (rms->is);
    861       return;
    862     }
    863 
    864     /* Melt amount starts with the melt fee of the old coin; we'll add the
    865        values and withdraw fees of the fresh coins next */
    866     melt_amount = melt_denom_pub->fees.refresh;
    867     age_restricted_denom = melt_denom_pub->key.age_mask.bits != 0;
    868     GNUNET_assert (age_restricted_denom == (NULL != age_commitment_proof));
    869     GNUNET_assert ((NULL == age_commitment_proof) ||
    870                    (0 < age_commitment_proof->commitment.num));
    871     for (unsigned int i = 0; i<num_fresh_coins; i++)
    872     {
    873       const struct TALER_EXCHANGE_DenomPublicKey *fresh_pk;
    874 
    875       if (GNUNET_OK !=
    876           TALER_string_to_amount (melt_fresh_amounts[i],
    877                                   &fresh_amount))
    878       {
    879         GNUNET_break (0);
    880         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    881                     "Failed to parse amount `%s' at index %u\n",
    882                     melt_fresh_amounts[i],
    883                     i);
    884         TALER_TESTING_interpreter_fail (rms->is);
    885         return;
    886       }
    887       fresh_pk = TALER_TESTING_find_pk (TALER_TESTING_get_keys (rms->is),
    888                                         &fresh_amount,
    889                                         age_restricted_denom);
    890       if (NULL == fresh_pk)
    891       {
    892         GNUNET_break (0);
    893         /* Subroutine logs specific error */
    894         TALER_TESTING_interpreter_fail (rms->is);
    895         return;
    896       }
    897       GNUNET_assert (0 <=
    898                      TALER_amount_add (&melt_amount,
    899                                        &melt_amount,
    900                                        &fresh_amount));
    901       GNUNET_assert (0 <=
    902                      TALER_amount_add (&melt_amount,
    903                                        &melt_amount,
    904                                        &fresh_pk->fees.withdraw));
    905       rms->fresh_pks[i] = *fresh_pk;
    906       /* Make a deep copy of the RSA key */
    907       TALER_denom_pub_copy (&rms->fresh_pks[i].key,
    908                             &fresh_pk->key);
    909     } /* end for */
    910 
    911     rms->melt_input.melt_priv = *rms->melt_priv;
    912     GNUNET_CRYPTO_eddsa_key_get_public (&rms->melt_priv->eddsa_priv,
    913                                         &rms->melt_pub.eddsa_pub);
    914     rms->melt_input.melt_amount = melt_amount;
    915     rms->melt_input.melt_sig = *melt_sig;
    916     rms->melt_input.melt_pk = *melt_denom_pub;
    917 
    918     if (NULL != age_commitment_proof)
    919     {
    920       GNUNET_assert (NULL != h_age_commitment);
    921       rms->melt_input.melt_age_commitment_proof = age_commitment_proof;
    922       rms->melt_input.melt_h_age_commitment = h_age_commitment;
    923     }
    924     rms->melt_input.fresh_denom_pubs = rms->fresh_pks;
    925     rms->melt_input.num_fresh_denom_pubs = num_fresh_coins;
    926 
    927     GNUNET_assert (age_restricted_denom ==
    928                    (NULL != age_commitment_proof));
    929     GNUNET_assert ((NULL == age_commitment_proof) ||
    930                    (0 < age_commitment_proof->commitment.num));
    931 
    932     rms->che.type = TALER_EXCHANGE_CTT_MELT;
    933     rms->che.amount = melt_amount;
    934     if (NULL != age_commitment_proof)
    935       rms->che.details.melt.h_age_commitment = *h_age_commitment;
    936     else
    937       rms->che.details.melt.no_hac = true;
    938 
    939     rms->mh = TALER_EXCHANGE_melt_v27 (
    940       TALER_TESTING_interpreter_get_context (is),
    941       TALER_TESTING_get_exchange_url (is),
    942       TALER_TESTING_get_keys (is),
    943       &rms->rms,
    944       &rms->melt_input,
    945       &melt_cb,
    946       rms);
    947 
    948     if (NULL == rms->mh)
    949     {
    950       GNUNET_break (0);
    951       TALER_TESTING_interpreter_fail (rms->is);
    952       return;
    953     }
    954   }
    955 }
    956 
    957 
    958 /**
    959  * Free the "refresh melt" CMD state, and possibly cancel a
    960  * pending operation thereof.
    961  *
    962  * @param cls closure, must be a `struct RefreshMeltState`.
    963  * @param cmd the command which is being cleaned up.
    964  */
    965 static void
    966 melt_cleanup (void *cls,
    967               const struct TALER_TESTING_Command *cmd)
    968 {
    969   struct MeltState *rms = cls;
    970 
    971   (void) cmd;
    972   if (NULL != rms->mh)
    973   {
    974     TALER_TESTING_command_incomplete (rms->is,
    975                                       cmd->label);
    976     TALER_EXCHANGE_melt_v27_cancel (rms->mh);
    977     rms->mh = NULL;
    978   }
    979   if (NULL != rms->retry_task)
    980   {
    981     GNUNET_SCHEDULER_cancel (rms->retry_task);
    982     rms->retry_task = NULL;
    983   }
    984   if (NULL != rms->fresh_pks)
    985   {
    986     for (unsigned int i = 0; i < rms->num_fresh_coins; i++)
    987       TALER_denom_pub_free (&rms->fresh_pks[i].key);
    988     GNUNET_free (rms->fresh_pks);
    989   }
    990   if (NULL != rms->blinding_values)
    991   {
    992     for (unsigned int i = 0; i < rms->num_blinding_values; i++)
    993       TALER_denom_ewv_free (&rms->blinding_values[i]);
    994     GNUNET_free (rms->blinding_values);
    995   }
    996   GNUNET_free (rms->melt_fresh_amounts);
    997   GNUNET_free (rms);
    998 }
    999 
   1000 
   1001 /**
   1002  * Offer internal data to the "refresh melt" CMD.
   1003  *
   1004  * @param cls closure.
   1005  * @param[out] ret result (could be anything).
   1006  * @param trait name of the trait.
   1007  * @param index index number of the object to offer.
   1008  * @return #GNUNET_OK on success.
   1009  */
   1010 static enum GNUNET_GenericReturnValue
   1011 melt_traits (void *cls,
   1012              const void **ret,
   1013              const char *trait,
   1014              unsigned int index)
   1015 {
   1016   struct MeltState *rms = cls;
   1017 
   1018   if (index >= rms->num_fresh_coins)
   1019   {
   1020     GNUNET_break (0);
   1021     return GNUNET_SYSERR;
   1022   }
   1023   {
   1024     struct TALER_TESTING_Trait traits[] = {
   1025       TALER_TESTING_make_trait_denom_pub (index,
   1026                                           &rms->fresh_pks[index]),
   1027       TALER_TESTING_make_trait_coin_priv (0,
   1028                                           rms->melt_priv),
   1029       TALER_TESTING_make_trait_coin_pub (0,
   1030                                          &rms->melt_pub),
   1031       TALER_TESTING_make_trait_coin_history (0,
   1032                                              &rms->che),
   1033       TALER_TESTING_make_trait_age_commitment_proof (
   1034         index,
   1035         rms->melt_input.melt_age_commitment_proof),
   1036       TALER_TESTING_make_trait_h_age_commitment (
   1037         index,
   1038         rms->melt_input.melt_h_age_commitment),
   1039       TALER_TESTING_make_trait_refresh_secret (&rms->rms),
   1040       (NULL != rms->reveal_melt_input.blinding_values)
   1041       ? TALER_TESTING_make_trait_exchange_blinding_values (
   1042         index,
   1043         &rms->reveal_melt_input.blinding_values[index])
   1044       : TALER_TESTING_trait_end (),
   1045       TALER_TESTING_trait_end ()
   1046     };
   1047 
   1048     return TALER_TESTING_get_trait (traits,
   1049                                     ret,
   1050                                     trait,
   1051                                     index);
   1052   }
   1053 }
   1054 
   1055 
   1056 /**
   1057  * Parse list of amounts for melt operation.
   1058  *
   1059  * @param[in,out] rms where to store the list
   1060  * @param ap NULL-termianted list of amounts to be melted (one per fresh coin)
   1061  * @return #GNUNET_OK on success
   1062  */
   1063 static enum GNUNET_GenericReturnValue
   1064 parse_amounts (struct MeltState *rms,
   1065                va_list ap)
   1066 {
   1067   unsigned int len;
   1068   unsigned int off;
   1069   const char *amount;
   1070 
   1071   len = 0;
   1072   off = 0;
   1073   while (NULL != (amount = va_arg (ap, const char *)))
   1074   {
   1075     if (len == off)
   1076     {
   1077       struct TALER_Amount a;
   1078 
   1079       GNUNET_array_grow (rms->melt_fresh_amounts,
   1080                          len,
   1081                          off + 16);
   1082       if (GNUNET_OK !=
   1083           TALER_string_to_amount (amount, &a))
   1084       {
   1085         GNUNET_break (0);
   1086         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1087                     "Failed to parse amount `%s' at index %u\n",
   1088                     amount, off);
   1089         GNUNET_free (rms->melt_fresh_amounts);
   1090         rms->melt_fresh_amounts = NULL;
   1091         return GNUNET_SYSERR;
   1092       }
   1093       rms->melt_fresh_amounts[off++] = amount;
   1094     }
   1095   }
   1096   if (0 == off)
   1097     return GNUNET_OK; /* no amounts given == use defaults! */
   1098   /* ensure NULL-termination */
   1099   GNUNET_array_grow (rms->melt_fresh_amounts,
   1100                      len,
   1101                      off + 1);
   1102   return GNUNET_OK;
   1103 }
   1104 
   1105 
   1106 struct TALER_TESTING_Command
   1107 TALER_TESTING_cmd_melt (const char *label,
   1108                         const char *coin_reference,
   1109                         unsigned int expected_response_code,
   1110                         ...)
   1111 {
   1112   struct MeltState *rms;
   1113   va_list ap;
   1114 
   1115   rms = GNUNET_new (struct MeltState);
   1116   rms->coin_reference = coin_reference;
   1117   rms->expected_response_code = expected_response_code;
   1118   va_start (ap,
   1119             expected_response_code);
   1120   GNUNET_assert (GNUNET_OK ==
   1121                  parse_amounts (rms, ap));
   1122   va_end (ap);
   1123   {
   1124     struct TALER_TESTING_Command cmd = {
   1125       .label = label,
   1126       .cls = rms,
   1127       .run = &melt_run,
   1128       .cleanup = &melt_cleanup,
   1129       .traits = &melt_traits
   1130     };
   1131 
   1132     return cmd;
   1133   }
   1134 }
   1135 
   1136 
   1137 struct TALER_TESTING_Command
   1138 TALER_TESTING_cmd_melt_double (const char *label,
   1139                                const char *coin_reference,
   1140                                unsigned int expected_response_code,
   1141                                ...)
   1142 {
   1143   struct MeltState *rms;
   1144   va_list ap;
   1145 
   1146   rms = GNUNET_new (struct MeltState);
   1147   rms->coin_reference = coin_reference;
   1148   rms->expected_response_code = expected_response_code;
   1149   rms->double_melt = true;
   1150   va_start (ap,
   1151             expected_response_code);
   1152   GNUNET_assert (GNUNET_OK ==
   1153                  parse_amounts (rms, ap));
   1154   va_end (ap);
   1155   {
   1156     struct TALER_TESTING_Command cmd = {
   1157       .label = label,
   1158       .cls = rms,
   1159       .run = &melt_run,
   1160       .cleanup = &melt_cleanup,
   1161       .traits = &melt_traits
   1162     };
   1163 
   1164     return cmd;
   1165   }
   1166 }
   1167 
   1168 
   1169 struct TALER_TESTING_Command
   1170 TALER_TESTING_cmd_melt_with_retry (struct TALER_TESTING_Command cmd)
   1171 {
   1172   struct MeltState *rms;
   1173 
   1174   GNUNET_assert (&melt_run == cmd.run);
   1175   rms = cmd.cls;
   1176   rms->do_retry = NUM_RETRIES;
   1177   return cmd;
   1178 }
   1179 
   1180 
   1181 /**
   1182  * Offer internal data from a "refresh reveal" CMD.
   1183  *
   1184  * @param cls closure.
   1185  * @param[out] ret result (could be anything).
   1186  * @param trait name of the trait.
   1187  * @param index index number of the object to offer.
   1188  * @return #GNUNET_OK on success.
   1189  */
   1190 static enum GNUNET_GenericReturnValue
   1191 melt_reveal_traits (void *cls,
   1192                     const void **ret,
   1193                     const char *trait,
   1194                     unsigned int index)
   1195 {
   1196   struct RevealMeltState *rrs = cls;
   1197 
   1198   if (index >= rrs->num_fresh_coins)
   1199     return GNUNET_SYSERR;
   1200 
   1201   {
   1202     struct TALER_TESTING_Trait traits[] = {
   1203       TALER_TESTING_make_trait_coin_priv (
   1204         index,
   1205         &rrs->fresh_coins[index].coin_priv),
   1206       TALER_TESTING_make_trait_age_commitment_proof (
   1207         index,
   1208         rrs->fresh_coins[index].age_commitment_proof),
   1209       TALER_TESTING_make_trait_h_age_commitment (
   1210         index,
   1211         &rrs->fresh_coins[index].h_age_commitment),
   1212       TALER_TESTING_make_trait_denom_pub (
   1213         index,
   1214         rrs->fresh_coins[index].pk),
   1215       TALER_TESTING_make_trait_denom_sig (
   1216         index,
   1217         &rrs->fresh_coins[index].sig),
   1218       TALER_TESTING_make_trait_blinding_key (
   1219         index,
   1220         &rrs->fresh_coins[index].blinding_key),
   1221       TALER_TESTING_make_trait_array_length (
   1222         &rrs->num_fresh_coins),
   1223       TALER_TESTING_make_trait_fresh_coins (
   1224         (const struct TALER_TESTING_FreshCoinData **) &rrs->fresh_coins),
   1225       TALER_TESTING_make_trait_planchet_secrets (index,
   1226                                                  &rrs->psa[index]),
   1227       TALER_TESTING_trait_end ()
   1228     };
   1229 
   1230     return TALER_TESTING_get_trait (traits,
   1231                                     ret,
   1232                                     trait,
   1233                                     index);
   1234   }
   1235 }
   1236 
   1237 
   1238 struct TALER_TESTING_Command
   1239 TALER_TESTING_cmd_melt_reveal (const char *label,
   1240                                const char *melt_reference,
   1241                                unsigned int expected_response_code)
   1242 {
   1243   struct RevealMeltState *rrs;
   1244 
   1245   rrs = GNUNET_new (struct RevealMeltState);
   1246   rrs->melt_reference = melt_reference;
   1247   rrs->expected_response_code = expected_response_code;
   1248   {
   1249     struct TALER_TESTING_Command cmd = {
   1250       .cls = rrs,
   1251       .label = label,
   1252       .run = &melt_reveal_run,
   1253       .cleanup = &melt_reveal_cleanup,
   1254       .traits = &melt_reveal_traits
   1255     };
   1256 
   1257     return cmd;
   1258   }
   1259 }
   1260 
   1261 
   1262 struct TALER_TESTING_Command
   1263 TALER_TESTING_cmd_melt_reveal_with_retry (struct TALER_TESTING_Command cmd)
   1264 {
   1265   struct RevealMeltState *rrs;
   1266 
   1267   GNUNET_assert (&melt_reveal_run == cmd.run);
   1268   rrs = cmd.cls;
   1269   rrs->do_retry = NUM_RETRIES;
   1270   return cmd;
   1271 }