exchange

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

taler-exchange-httpd_melt.c (57366B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2025 Taler Systems SA
      4 
      5   TALER is free software; you can redistribute it and/or modify it under the
      6   terms of the GNU Affero General Public License as published by the Free Software
      7   Foundation; either version 3, or (at your option) any later version.
      8 
      9   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
     10   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
     11   A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more details.
     12 
     13   You should have received a copy of the GNU Affero General Public License along with
     14   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
     15 */
     16 /**
     17  * @file taler-exchange-httpd_melt.c
     18  * @brief Handle /melt requests
     19  * @note This endpoint is active since vDOLDPLUS of the protocol API
     20  * @author Özgür Kesim
     21  */
     22 
     23 #include "taler/platform.h"
     24 #include <gnunet/gnunet_util_lib.h>
     25 #include <jansson.h>
     26 #include "taler-exchange-httpd.h"
     27 #include "taler/taler_json_lib.h"
     28 #include "taler/taler_mhd_lib.h"
     29 #include "taler-exchange-httpd_melt.h"
     30 #include "taler-exchange-httpd_responses.h"
     31 #include "taler-exchange-httpd_keys.h"
     32 #include "taler/taler_util.h"
     33 
     34 /**
     35  * The different type of errors that might occur, sorted by name.
     36  * Some of them require idempotency checks, which are marked
     37  * in @e idempotency_check_required below.
     38  */
     39 enum MeltError
     40 {
     41   MELT_ERROR_NONE = 0,
     42   MELT_ERROR_AGE_RESTRICTION_COMMITMENT_INVALID,
     43   MELT_ERROR_AGE_RESTRICTION_NOT_SUPPORTED_BY_DENOMINATION,
     44   MELT_ERROR_AMOUNT_OVERFLOW,
     45   MELT_ERROR_AMOUNT_PLUS_FEE_OVERFLOW,
     46   MELT_ERROR_AMOUNT_WITH_FEE_INCORRECT,
     47   MELT_ERROR_BLINDING_SEED_REQUIRED,
     48   MELT_ERROR_COIN_CIPHER_MISMATCH,
     49   MELT_COIN_CONFLICTING_DENOMINATION_KEY,
     50   MELT_ERROR_COIN_EXPIRED_NO_ZOMBIE,
     51   MELT_ERROR_COIN_SIGNATURE_INVALID,
     52   MELT_ERROR_COIN_UNKNOWN,
     53   MELT_ERROR_CONFIRMATION_SIGN,
     54   MELT_ERROR_CRYPTO_HELPER,
     55   MELT_ERROR_DB_FETCH_FAILED,
     56   MELT_ERROR_DB_INVARIANT_FAILURE,
     57   MELT_ERROR_DB_MAKE_COIN_KNOW_FAILURE,
     58   MELT_ERROR_DB_PREFLIGHT_FAILURE,
     59   MELT_ERROR_DENOMINATION_EXPIRED,
     60   MELT_ERROR_DENOMINATION_KEY_UNKNOWN,
     61   MELT_ERROR_DENOMINATION_REVOKED,
     62   MELT_ERROR_DENOMINATION_SIGN,
     63   MELT_ERROR_DENOMINATION_SIGNATURE_INVALID,
     64   MELT_ERROR_DENOMINATION_VALIDITY_IN_FUTURE,
     65   MELT_ERROR_DUPLICATE_PLANCHET,
     66   MELT_ERROR_DUPLICATE_TRANSFER_PUB,
     67   MELT_ERROR_INSUFFICIENT_FUNDS,
     68   MELT_ERROR_KEYS_MISSING,
     69   MELT_ERROR_FEES_EXCEED_CONTRIBUTION,
     70   MELT_ERROR_NONCE_RESUSE,
     71   MELT_ERROR_REQUEST_PARAMETER_MALFORMED,
     72 };
     73 
     74 /**
     75  * With the bits set in this value will be mark the errors
     76  * that require a check for idempotency before actually
     77  * returning an error.
     78  */
     79 static const uint64_t idempotency_check_required =
     80   0
     81   | (1 << MELT_ERROR_DENOMINATION_EXPIRED)
     82   | (1 << MELT_ERROR_DENOMINATION_KEY_UNKNOWN)
     83   | (1 << MELT_ERROR_DENOMINATION_REVOKED)
     84   | (1 << MELT_ERROR_INSUFFICIENT_FUNDS) /* TODO: is this still correct? Compare exchange_do_refresh.sql */
     85   | (1 << MELT_ERROR_KEYS_MISSING);
     86 
     87 #define IDEMPOTENCY_CHECK_REQUIRED(error) \
     88         (0 != (idempotency_check_required & (1 << (error))))
     89 
     90 /**
     91  * Context for a /melt request
     92  */
     93 struct MeltContext
     94 {
     95 
     96   /**
     97    * This struct is kept in a DLL.
     98    */
     99   struct MeltContext *prev;
    100   struct MeltContext *next;
    101 
    102   /**
    103      * Processing phase we are in.
    104      * The ordering here partially matters, as we progress through
    105      * them by incrementing the phase in the happy path.
    106      */
    107   enum MeltPhase
    108   {
    109     MELT_PHASE_PARSE,
    110     MELT_PHASE_CHECK_MELT_VALID,
    111     MELT_PHASE_CHECK_KEYS,
    112     MELT_PHASE_CHECK_COIN_SIGNATURE,
    113     MELT_PHASE_PREPARE_TRANSACTION,
    114     MELT_PHASE_RUN_TRANSACTION,
    115     MELT_PHASE_GENERATE_REPLY_SUCCESS,
    116     MELT_PHASE_GENERATE_REPLY_ERROR,
    117     MELT_PHASE_RETURN_NO,
    118     MELT_PHASE_RETURN_YES,
    119   } phase;
    120 
    121 
    122   /**
    123    * Request context
    124    */
    125   const struct TEH_RequestContext *rc;
    126 
    127   /**
    128    * Current time for the DB transaction.
    129    */
    130   struct GNUNET_TIME_Timestamp now;
    131 
    132   /**
    133    * The current key state
    134    */
    135   struct TEH_KeyStateHandle *ksh;
    136 
    137   /**
    138    * The melted coin's denomination key
    139    */
    140   struct TEH_DenominationKey *melted_coin_denom;
    141 
    142   /**
    143    * Set to true if this coin's denomination was revoked and the operation
    144    * is thus only allowed for zombie coins where the transaction
    145    * history includes a #TALER_EXCHANGEDB_TT_OLD_COIN_RECOUP.
    146    *
    147    * TODO: find a better terminology.  The sentences in the comments containing
    148    * "zombie" make semantically _no sense_!
    149    */
    150   bool zombie_required;
    151 
    152   /**
    153    * We already checked and noticed that the coin is known. Hence we
    154    * can skip the "ensure_coin_known" step of the transaction.
    155    */
    156   bool coin_is_known;
    157 
    158   /**
    159    * UUID of the coin in the known_coins table.
    160    */
    161   uint64_t known_coin_id;
    162 
    163   /**
    164    * Captures all parameters provided in the JSON request
    165    */
    166   struct
    167   {
    168 
    169     /**
    170      * All fields (from the request or computed)
    171      * that we persist in the database.
    172      */
    173     struct TALER_EXCHANGEDB_Refresh_vDOLDPLUS refresh;
    174 
    175     /**
    176      * In some error cases we check for idempotency.
    177      * If we find an entry in the database, we mark this here.
    178      */
    179     bool is_idempotent;
    180 
    181     /**
    182      * In some error conditions the request is checked
    183      * for idempotency and the result from the database
    184      * is stored here.
    185      */
    186     struct TALER_EXCHANGEDB_Refresh_vDOLDPLUS refresh_idem;
    187 
    188     /**
    189      * True if @e blinding_seed is missing in the request
    190      */
    191     bool no_blinding_seed;
    192 
    193     /**
    194      * Array @e persis.num_coins of hashes of the public keys
    195      * of the denominations to refresh.
    196      */
    197     struct TALER_DenominationHashP *denoms_h;
    198 
    199     /**
    200      * Array of @e refresh.num_coins blinded coin planchets, arranged
    201      * in runs of @e refresh.num_coins coins, [0..num_coins)..[0..num_coins),
    202      * one for each kappa value.
    203      */
    204     struct TALER_BlindedPlanchet *planchets[TALER_CNC_KAPPA];
    205 
    206     /**
    207      * @since vDOLDPLUS
    208      * #TALER_CNC_KAPPA arrays of @e refresh.num_coins transfer public keys
    209      * in runs of @e num_coins coins, [0..num_coins)..[0..num_coins),
    210      * one for each kappa value.
    211      *
    212      * MAYBE null.  If the client has NOT provided the transfer_pubs in the request,
    213      * @e refresh.is_v27_refresh will be true.
    214      *
    215      * TODO: Once v27 clients are gone, this MUST NOT be nulls.
    216      */
    217     struct TALER_TransferPublicKeyP *transfer_pubs[TALER_CNC_KAPPA];
    218 
    219     /**
    220      * #TALER_CNC_KAPPA hashes of the batches of @e num_coins coins.
    221      */
    222     struct TALER_KappaHashBlindedPlanchetsP kappa_planchets_h;
    223 
    224     /**
    225      * Array @e withdraw.num_r_pubs of indices into @e denoms_h
    226      * of CS denominations.
    227      */
    228     uint32_t *cs_indices;
    229 
    230     /**
    231      * Total (over all coins) amount (excluding fee) committed for the refresh
    232      */
    233     struct TALER_Amount amount;
    234 
    235   } request;
    236 
    237   /**
    238    * Errors occurring during evaluation of the request are captured in this
    239    * struct.  In phase WITHDRAW_PHASE_GENERATE_REPLY_ERROR an appropriate error
    240    * message is prepared and sent to the client.
    241    */
    242   struct
    243   {
    244     /* The (internal) error code */
    245     enum MeltError code;
    246 
    247     /**
    248      * Some errors require details to be sent to the client.
    249      * These are captured in this union.
    250      * Each field is named according to the error that is using it, except
    251      * commented otherwise.
    252      */
    253     union
    254     {
    255       const char *request_parameter_malformed;
    256 
    257       /**
    258        * For all errors related to a particular denomination, i.e.
    259        *  #MELT_ERROR_DENOMINATION_KEY_UNKNOWN,
    260        *  #MELT_ERROR_DENOMINATION_EXPIRED,
    261        *  #MELT_ERROR_DENOMINATION_VALIDITY_IN_FUTURE,
    262        *  #MELT_ERROR_AGE_RESTRICTION_NOT_SUPPORTED_BY_DENOMINATION,
    263        * we use this one field.
    264        */
    265       struct TALER_DenominationHashP denom_h;
    266 
    267       const char *db_fetch_context;
    268 
    269       enum TALER_ErrorCode ec_confirmation_sign;
    270 
    271       enum TALER_ErrorCode ec_denomination_sign;
    272 
    273       /* remaining value of the coin */
    274       struct TALER_Amount insufficient_funds;
    275 
    276     } details;
    277   } error;
    278 };
    279 
    280 /**
    281  * The following macros set the given error code,
    282  * set the phase to Melt_PHASE_GENERATE_REPLY_ERROR,
    283  * and optionally set the given field (with an optionally given value).
    284  */
    285 #define SET_ERROR(mc, ec) \
    286         do \
    287         { GNUNET_static_assert (MELT_ERROR_NONE != ec); \
    288           (mc)->error.code = (ec);                      \
    289           (mc)->phase = MELT_PHASE_GENERATE_REPLY_ERROR; } while (0)
    290 
    291 #define SET_ERROR_WITH_FIELD(mc, ec, field) \
    292         do \
    293         { GNUNET_static_assert (MELT_ERROR_NONE != ec); \
    294           (mc)->error.code = (ec);                      \
    295           (mc)->error.details.field = (field);          \
    296           (mc)->phase = MELT_PHASE_GENERATE_REPLY_ERROR; } while (0)
    297 
    298 #define SET_ERROR_WITH_DETAIL(mc, ec, field, value) \
    299         do \
    300         { GNUNET_static_assert (MELT_ERROR_NONE != ec); \
    301           (mc)->error.code = (ec);                      \
    302           (mc)->error.details.field = (value);          \
    303           (mc)->phase = MELT_PHASE_GENERATE_REPLY_ERROR; } while (0)
    304 
    305 
    306 /**
    307  * All melt context is kept in a DLL.
    308  */
    309 static struct MeltContext *mc_head;
    310 static struct MeltContext *mc_tail;
    311 
    312 void
    313 TEH_melt_cleanup ()
    314 {
    315   struct MeltContext *mc;
    316 
    317   while (NULL != (mc = mc_head))
    318   {
    319     GNUNET_CONTAINER_DLL_remove (mc_head,
    320                                  mc_tail,
    321                                  mc);
    322     MHD_resume_connection (mc->rc->connection);
    323   }
    324 }
    325 
    326 
    327 /**
    328  * Terminate the main loop by returning the final result.
    329  *
    330  * @param[in,out] mc context to update phase for
    331  * @param mres MHD status to return
    332  */
    333 static void
    334 finish_loop (struct MeltContext *mc,
    335              MHD_RESULT mres)
    336 {
    337   mc->phase = (MHD_YES == mres)
    338                          ? MELT_PHASE_RETURN_YES
    339                          : MELT_PHASE_RETURN_NO;
    340 }
    341 
    342 
    343 /**
    344  * Free information in @a re, but not @a re itself.
    345  *
    346  * @param[in] re refresh data to free
    347  */
    348 static void
    349 free_refresh (struct TALER_EXCHANGEDB_Refresh_vDOLDPLUS *re)
    350 {
    351   if (NULL != re->denom_sigs)
    352   {
    353     for (size_t i = 0; i<re->num_coins; i++)
    354       TALER_blinded_denom_sig_free (&re->denom_sigs[i]);
    355     GNUNET_free (re->denom_sigs);
    356   }
    357   GNUNET_free (re->cs_r_values);
    358   GNUNET_free (re->denom_serials);
    359   GNUNET_free (re->denom_pub_hashes);
    360   TALER_denom_sig_free (&re->coin.denom_sig);
    361 }
    362 
    363 
    364 /**
    365  * Cleanup routine for melt request.
    366  * The function is called upon completion of the request
    367  * that should clean up @a rh_ctx.
    368  *
    369  * @param rc request context to clean up
    370  */
    371 static void
    372 clean_melt_rc (struct TEH_RequestContext *rc)
    373 {
    374   struct MeltContext *mc = rc->rh_ctx;
    375 
    376   GNUNET_free (mc->request.denoms_h);
    377   for (uint8_t k = 0; k<TALER_CNC_KAPPA; k++)
    378   {
    379     for (size_t i = 0; i<mc->request.refresh.num_coins; i++)
    380       TALER_blinded_planchet_free (&mc->request.planchets[k][i]);
    381     GNUNET_free (mc->request.planchets[k]);
    382     if (! mc->request.refresh.is_v27_refresh)
    383       GNUNET_free (mc->request.transfer_pubs[k]);
    384   }
    385   free_refresh (&mc->request.refresh);
    386   if (mc->request.is_idempotent)
    387     free_refresh (&mc->request.refresh_idem);
    388   GNUNET_free (mc->request.cs_indices);
    389   GNUNET_free (mc);
    390 }
    391 
    392 
    393 /**
    394  * Creates a new context for the incoming melt request
    395  *
    396  * @param mc melt request context
    397  * @param root json body of the request
    398  */
    399 static void
    400 phase_parse_request (
    401   struct MeltContext *mc,
    402   const json_t *root)
    403 {
    404   const json_t *j_denoms_h;
    405   const json_t *j_coin_evs;
    406   const json_t *j_transfer_pubs;
    407   enum GNUNET_GenericReturnValue res;
    408   struct GNUNET_JSON_Specification spec[] = {
    409     GNUNET_JSON_spec_fixed_auto ("old_coin_pub",
    410                                  &mc->request.refresh.coin.coin_pub),
    411     GNUNET_JSON_spec_fixed_auto ("old_denom_pub_h",
    412                                  &mc->request.refresh.coin.denom_pub_hash),
    413     GNUNET_JSON_spec_mark_optional (
    414       GNUNET_JSON_spec_fixed_auto ("old_age_commitment_h",
    415                                    &mc->request.refresh.coin.h_age_commitment),
    416       &mc->request.refresh.coin.no_age_commitment),
    417     TALER_JSON_spec_denom_sig ("old_denom_sig",
    418                                &mc->request.refresh.coin.denom_sig),
    419     GNUNET_JSON_spec_fixed_auto ("refresh_seed",
    420                                  &mc->request.refresh.refresh_seed),
    421     GNUNET_JSON_spec_mark_optional (
    422       GNUNET_JSON_spec_fixed_auto ("blinding_seed",
    423                                    &mc->request.refresh.blinding_seed),
    424       &mc->request.refresh.no_blinding_seed),
    425     TALER_JSON_spec_amount ("value_with_fee",
    426                             TEH_currency,
    427                             &mc->request.refresh.amount_with_fee),
    428     GNUNET_JSON_spec_mark_optional (
    429       GNUNET_JSON_spec_array_const ("transfer_pubs",
    430                                     &j_transfer_pubs),
    431       &mc->request.refresh.is_v27_refresh),
    432     GNUNET_JSON_spec_array_const ("denoms_h",
    433                                   &j_denoms_h),
    434     GNUNET_JSON_spec_array_const ("coin_evs",
    435                                   &j_coin_evs),
    436     GNUNET_JSON_spec_fixed_auto ("confirm_sig",
    437                                  &mc->request.refresh.coin_sig),
    438     GNUNET_JSON_spec_end ()
    439   };
    440 
    441   res = TALER_MHD_parse_json_data (mc->rc->connection,
    442                                    root,
    443                                    spec);
    444   if (GNUNET_OK != res)
    445   {
    446     GNUNET_break_op (0);
    447     mc->phase = (GNUNET_NO == res)
    448       ? MELT_PHASE_RETURN_YES
    449       : MELT_PHASE_RETURN_NO;
    450     return;
    451   }
    452 
    453   /* validate array size */
    454   GNUNET_static_assert (
    455     TALER_MAX_COINS < INT_MAX / TALER_CNC_KAPPA);
    456 
    457   mc->request.refresh.num_coins = json_array_size (j_denoms_h);
    458   if (0 == mc->request.refresh.num_coins)
    459   {
    460     GNUNET_break_op (0);
    461     SET_ERROR_WITH_DETAIL (mc,
    462                            MELT_ERROR_REQUEST_PARAMETER_MALFORMED,
    463                            request_parameter_malformed,
    464                            "denoms_h must not be empty");
    465     return;
    466   }
    467   if (TALER_MAX_COINS < mc->request.refresh.num_coins)
    468   {
    469     /**
    470      * The wallet had committed to more than the maximum coins allowed, the
    471      * reserve has been charged, but now the user can not melt any money
    472      * from it.  Note that the user can't get their money back in this case!
    473      */
    474     GNUNET_break_op (0);
    475     SET_ERROR_WITH_DETAIL (mc,
    476                            MELT_ERROR_REQUEST_PARAMETER_MALFORMED,
    477                            request_parameter_malformed,
    478                            "maximum number of coins that can be refreshed has been exceeded");
    479     return;
    480   }
    481   if (TALER_CNC_KAPPA != json_array_size (j_coin_evs))
    482   {
    483     GNUNET_break_op (0);
    484     SET_ERROR_WITH_DETAIL (mc,
    485                            MELT_ERROR_REQUEST_PARAMETER_MALFORMED,
    486                            request_parameter_malformed,
    487                            "coin_evs must be an array of length "TALER_CNC_KAPPA_STR);
    488     return;
    489   }
    490   if (! mc->request.refresh.is_v27_refresh &&
    491       (TALER_CNC_KAPPA != json_array_size (j_transfer_pubs)))
    492   {
    493     GNUNET_break_op (0);
    494     SET_ERROR_WITH_DETAIL (mc,
    495                            MELT_ERROR_REQUEST_PARAMETER_MALFORMED,
    496                            request_parameter_malformed,
    497                            "transfer_pubs must be an array of length "TALER_CNC_KAPPA_STR);
    498     return;
    499   }
    500 
    501 
    502   /* Extract the denomination hashes */
    503   {
    504     size_t idx;
    505     json_t *value;
    506 
    507     mc->request.denoms_h
    508       = GNUNET_new_array (mc->request.refresh.num_coins,
    509                           struct TALER_DenominationHashP);
    510 
    511     json_array_foreach (j_denoms_h, idx, value) {
    512       struct GNUNET_JSON_Specification ispec[] = {
    513         GNUNET_JSON_spec_fixed_auto (NULL,
    514                                      &mc->request.denoms_h[idx]),
    515         GNUNET_JSON_spec_end ()
    516       };
    517 
    518       res = TALER_MHD_parse_json_data (mc->rc->connection,
    519                                        value,
    520                                        ispec);
    521       if (GNUNET_YES != res)
    522       {
    523         GNUNET_break_op (0);
    524         mc->phase = (GNUNET_NO == res)
    525           ? MELT_PHASE_RETURN_YES
    526           : MELT_PHASE_RETURN_NO;
    527         return;
    528       }
    529     }
    530   }
    531 
    532   /* Parse blinded envelopes. */
    533   {
    534     json_t *j_kappa_planchets;
    535     size_t kappa;
    536     struct GNUNET_HashContext *ctx;
    537 
    538     /* ctx to calculate the planchet_h */
    539     ctx = GNUNET_CRYPTO_hash_context_start ();
    540     GNUNET_assert (NULL != ctx);
    541 
    542     json_array_foreach (j_coin_evs, kappa, j_kappa_planchets)
    543     {
    544       json_t *j_cev;
    545       size_t idx;
    546 
    547       if (mc->request.refresh.num_coins != json_array_size (j_kappa_planchets))
    548       {
    549         GNUNET_break_op (0);
    550         SET_ERROR_WITH_DETAIL (mc,
    551                                MELT_ERROR_REQUEST_PARAMETER_MALFORMED,
    552                                request_parameter_malformed,
    553                                "coin_evs[i][] size");
    554         return;
    555       }
    556 
    557       mc->request.planchets[kappa] =
    558         GNUNET_new_array (mc->request.refresh.num_coins,
    559                           struct  TALER_BlindedPlanchet);
    560 
    561       json_array_foreach (j_kappa_planchets, idx, j_cev)
    562       {
    563         /* Now parse the individual envelopes and calculate the hash of
    564          * the commitment along the way. */
    565         struct GNUNET_JSON_Specification kspec[] = {
    566           TALER_JSON_spec_blinded_planchet (NULL,
    567                                             &mc->request.planchets[kappa][idx]),
    568           GNUNET_JSON_spec_end ()
    569         };
    570 
    571         res = TALER_MHD_parse_json_data (mc->rc->connection,
    572                                          j_cev,
    573                                          kspec);
    574         if (GNUNET_OK != res)
    575         {
    576           GNUNET_break_op (0);
    577           mc->phase = (GNUNET_NO == res)
    578             ? MELT_PHASE_RETURN_YES
    579             : MELT_PHASE_RETURN_NO;
    580           return;
    581         }
    582       }
    583       /* Save the hash of the batch of planchets for index kappa */
    584       TALER_wallet_blinded_planchets_hash (
    585         mc->request.refresh.num_coins,
    586         mc->request.planchets[kappa],
    587         mc->request.denoms_h,
    588         &mc->request.kappa_planchets_h.tuple[kappa]);
    589       GNUNET_CRYPTO_hash_context_read (
    590         ctx,
    591         &mc->request.kappa_planchets_h.tuple[kappa],
    592         sizeof(mc->request.kappa_planchets_h.tuple[kappa]));
    593     }
    594     /* Finally calculate the total hash over all planchets */
    595     GNUNET_CRYPTO_hash_context_finish (
    596       ctx,
    597       &mc->request.refresh.planchets_h.hash);
    598   }
    599   /* Check for duplicate planchets. Technically a bug on
    600    * the client side that is harmless for us, but still
    601    * not allowed per protocol
    602    */
    603   {
    604     size_t max_idx = TALER_CNC_KAPPA * mc->request.refresh.num_coins;
    605 
    606     for (size_t I = 0; I < max_idx - 1; I++)
    607     {
    608       size_t ki = I / mc->request.refresh.num_coins;
    609       size_t ni = I % mc->request.refresh.num_coins;
    610 
    611       for (size_t J = I + 1; J < max_idx; J++)
    612       {
    613         size_t kj = J / mc->request.refresh.num_coins;
    614         size_t nj = J % mc->request.refresh.num_coins;
    615 
    616         if (0 == TALER_blinded_planchet_cmp (
    617               &mc->request.planchets[ki][ni],
    618               &mc->request.planchets[kj][nj]))
    619         {
    620           GNUNET_break_op (0);
    621           SET_ERROR (mc,
    622                      MELT_ERROR_DUPLICATE_TRANSFER_PUB);
    623           return;
    624         }
    625       }
    626     }
    627   }
    628 
    629   /* Parse the transfer public keys, if applicable */
    630   if (! mc->request.refresh.is_v27_refresh)
    631   {
    632     json_t *j_ktp;
    633     size_t kappa;
    634     size_t max_idx;
    635 
    636     json_array_foreach (j_transfer_pubs, kappa, j_ktp)
    637     {
    638       if (mc->request.refresh.num_coins !=
    639           json_array_size (j_ktp))
    640       {
    641         GNUNET_break_op (0);
    642         SET_ERROR_WITH_DETAIL (mc,
    643                                MELT_ERROR_REQUEST_PARAMETER_MALFORMED,
    644                                request_parameter_malformed,
    645                                "transfer_pubs[i][] size");
    646         return;
    647       }
    648 
    649       mc->request.transfer_pubs[kappa] =
    650         GNUNET_new_array (mc->request.refresh.num_coins,
    651                           struct  TALER_TransferPublicKeyP);
    652 
    653       /* Parse the batch of @e num_coins transfer public keys
    654        * at index kappa  */
    655       {
    656         struct GNUNET_JSON_Specification ktp_spec[] = {
    657           TALER_JSON_spec_array_fixed (NULL,
    658                                        mc->request.refresh.num_coins,
    659                                        mc->request.transfer_pubs[kappa],
    660                                        sizeof(*mc->request.transfer_pubs[kappa])
    661                                        ),
    662           GNUNET_JSON_spec_end ()
    663         };
    664 
    665         res = TALER_MHD_parse_json_data (mc->rc->connection,
    666                                          j_ktp,
    667                                          ktp_spec);
    668         if (GNUNET_OK != res)
    669         {
    670           GNUNET_break_op (0);
    671           mc->phase = (GNUNET_NO == res)
    672             ? MELT_PHASE_RETURN_YES
    673             : MELT_PHASE_RETURN_NO;
    674           return;
    675         }
    676       }
    677     }
    678     /* Check for duplicate transfer public keys. Technically a bug on
    679      * the client side that is harmless for us, but still
    680      * not allowed per protocol
    681      */
    682     max_idx = TALER_CNC_KAPPA * mc->request.refresh.num_coins;
    683 
    684     for (size_t I = 0; I < max_idx - 1; I++)
    685     {
    686       size_t ki = I / mc->request.refresh.num_coins;
    687       size_t ni = I % mc->request.refresh.num_coins;
    688 
    689       for (size_t J = I + 1; J < max_idx; J++)
    690       {
    691         size_t kj = J / mc->request.refresh.num_coins;
    692         size_t nj = J % mc->request.refresh.num_coins;
    693 
    694         if (0 == GNUNET_memcmp (
    695               &mc->request.transfer_pubs[ki][ni],
    696               &mc->request.transfer_pubs[kj][nj]))
    697         {
    698           GNUNET_break_op (0);
    699           SET_ERROR (mc,
    700                      MELT_ERROR_DUPLICATE_TRANSFER_PUB);
    701           return;
    702         }
    703       }
    704     }
    705   }
    706 
    707   mc->ksh = TEH_keys_get_state ();
    708   if (NULL == mc->ksh)
    709   {
    710     GNUNET_break (0);
    711     SET_ERROR (mc,
    712                MELT_ERROR_KEYS_MISSING);
    713     return;
    714   }
    715   mc->phase = MELT_PHASE_CHECK_MELT_VALID;
    716 }
    717 
    718 
    719 /**
    720  * Check if the given denomination is still or already valid, has not been
    721  * revoked and potentically supports age restriction.
    722  *
    723  * @param[in,out] mc context for the melt operation
    724  * @param denom_h Hash of the denomination key to check
    725  * @param[out] pdk denomination key found, might be NULL
    726  * @return #GNUNET_OK when denomation was found and valid,
    727  *         #GNUNET_NO when denomination is not valid at this time
    728  *         #GNUNET_SYSERR otherwise (denomination invalid), with finish_loop called.
    729  */
    730 static enum GNUNET_GenericReturnValue
    731 find_denomination (
    732   struct MeltContext *mc,
    733   const struct TALER_DenominationHashP *denom_h,
    734   struct TEH_DenominationKey **pdk)
    735 {
    736   struct TEH_DenominationKey *dk;
    737 
    738   *pdk = NULL;
    739   GNUNET_assert (NULL != mc->ksh);
    740   dk = TEH_keys_denomination_by_hash_from_state (mc->ksh,
    741                                                  denom_h,
    742                                                  NULL,
    743                                                  NULL);
    744   if (NULL == dk)
    745   {
    746     SET_ERROR_WITH_DETAIL (mc,
    747                            MELT_ERROR_DENOMINATION_KEY_UNKNOWN,
    748                            denom_h,
    749                            *denom_h);
    750     return GNUNET_SYSERR;
    751   }
    752   *pdk = dk;
    753 
    754   if (GNUNET_TIME_absolute_is_past (
    755         dk->meta.expire_withdraw.abs_time))
    756   {
    757     SET_ERROR_WITH_DETAIL (mc,
    758                            MELT_ERROR_DENOMINATION_EXPIRED,
    759                            denom_h,
    760                            *denom_h);
    761     /**
    762      * Note that we return GNUNET_NO here.
    763      * This way phase_check_melt_valid can react
    764      * to it as a non-error case and do the zombie check.
    765      */
    766     return GNUNET_NO;
    767   }
    768 
    769   if (GNUNET_TIME_absolute_is_future (
    770         dk->meta.start.abs_time))
    771   {
    772     GNUNET_break_op (0);
    773     SET_ERROR_WITH_DETAIL (mc,
    774                            MELT_ERROR_DENOMINATION_VALIDITY_IN_FUTURE,
    775                            denom_h,
    776                            *denom_h);
    777     return GNUNET_SYSERR;
    778   }
    779 
    780   if (dk->recoup_possible)
    781   {
    782     SET_ERROR (mc,
    783                MELT_ERROR_DENOMINATION_REVOKED);
    784     return GNUNET_SYSERR;
    785   }
    786 
    787   /* In case of age melt, make sure that the denomination supports age restriction */
    788   if (! (mc->request.refresh.coin.no_age_commitment) &&
    789       (0 == dk->denom_pub.age_mask.bits))
    790   {
    791     GNUNET_break_op (0);
    792     SET_ERROR_WITH_DETAIL (mc,
    793                            MELT_ERROR_AGE_RESTRICTION_NOT_SUPPORTED_BY_DENOMINATION,
    794                            denom_h,
    795                            *denom_h);
    796     return GNUNET_SYSERR;
    797   }
    798   if ((mc->request.refresh.coin.no_age_commitment) &&
    799       (0 != dk->denom_pub.age_mask.bits))
    800   {
    801     GNUNET_break_op (0);
    802     SET_ERROR (mc,
    803                MELT_ERROR_AGE_RESTRICTION_COMMITMENT_INVALID);
    804     return GNUNET_SYSERR;
    805   }
    806 
    807   return GNUNET_OK;
    808 }
    809 
    810 
    811 /**
    812  * Check if the given array of hashes of denomination_keys
    813  * - belong to valid denominations
    814  * - calculate the total amount of the denominations including fees
    815  * for melt.
    816  *
    817  * @param mc context of the melt to check keys for
    818  */
    819 static void
    820 phase_check_keys (
    821   struct MeltContext *mc)
    822 {
    823   bool is_cs_denom[mc->request.refresh.num_coins];
    824 
    825   memset (is_cs_denom,
    826           0,
    827           sizeof(is_cs_denom));
    828 
    829   mc->request.refresh.denom_serials =
    830     GNUNET_new_array (mc->request.refresh.num_coins,
    831                       uint64_t);
    832   GNUNET_assert (GNUNET_OK ==
    833                  TALER_amount_set_zero (TEH_currency,
    834                                         &mc->request.amount));
    835 
    836   /* Calculate the total value and withdraw fees for the fresh coins */
    837   for (size_t i = 0; i < mc->request.refresh.num_coins; i++)
    838   {
    839     struct TEH_DenominationKey *dk;
    840 
    841     if (GNUNET_OK !=
    842         find_denomination (mc,
    843                            &mc->request.denoms_h[i],
    844                            &dk))
    845       return;
    846 
    847     if (GNUNET_CRYPTO_BSA_CS ==
    848         dk->denom_pub.bsign_pub_key->cipher)
    849     {
    850       if (mc->request.refresh.no_blinding_seed)
    851       {
    852         GNUNET_break_op (0);
    853         SET_ERROR (mc,
    854                    MELT_ERROR_BLINDING_SEED_REQUIRED);
    855         return;
    856       }
    857       mc->request.refresh.num_cs_r_values++;
    858       is_cs_denom[i] = true;
    859     }
    860     /* Ensure the ciphers from the planchets match the denominations'. */
    861     {
    862       for (uint8_t k = 0; k < TALER_CNC_KAPPA; k++)
    863       {
    864         if (dk->denom_pub.bsign_pub_key->cipher !=
    865             mc->request.planchets[k][i].blinded_message->cipher)
    866         {
    867           GNUNET_break_op (0);
    868           SET_ERROR (mc,
    869                      MELT_ERROR_COIN_CIPHER_MISMATCH);
    870           return;
    871         }
    872       }
    873     }
    874     /* Accumulate the values */
    875     if (0 > TALER_amount_add (&mc->request.amount,
    876                               &mc->request.amount,
    877                               &dk->meta.value))
    878     {
    879       GNUNET_break_op (0);
    880       SET_ERROR (mc,
    881                  MELT_ERROR_AMOUNT_OVERFLOW);
    882       return;
    883     }
    884     /* Accumulate the withdraw fees for the fresh coins */
    885     if (0 > TALER_amount_add (&mc->request.amount,
    886                               &mc->request.amount,
    887                               &dk->meta.fees.withdraw))
    888     {
    889       GNUNET_break_op (0);
    890       SET_ERROR (mc,
    891                  MELT_ERROR_AMOUNT_PLUS_FEE_OVERFLOW);
    892       return;
    893     }
    894     mc->request.refresh.denom_serials[i] = dk->meta.serial;
    895   }
    896 
    897   /**
    898    * Calculate the amount (with withdraw fee) plus refresh fee and
    899    * compare with the value provided by the client in the request.
    900    */
    901   {
    902     struct TALER_Amount amount_with_fee;
    903 
    904     if (0 > TALER_amount_add (&amount_with_fee,
    905                               &mc->request.amount,
    906                               &mc->melted_coin_denom->meta.fees.refresh))
    907     {
    908       GNUNET_break_op (0);
    909       SET_ERROR (mc,
    910                  MELT_ERROR_AMOUNT_PLUS_FEE_OVERFLOW);
    911       return;
    912     }
    913 
    914     if (0 != TALER_amount_cmp (&amount_with_fee,
    915                                &mc->request.refresh.amount_with_fee))
    916     {
    917       GNUNET_break_op (0);
    918       SET_ERROR (mc,
    919                  MELT_ERROR_AMOUNT_WITH_FEE_INCORRECT);
    920       return;
    921     }
    922   }
    923 
    924   /* Save the indices of CS denominations */
    925   if (0 < mc->request.refresh.num_cs_r_values)
    926   {
    927     size_t j = 0;
    928 
    929     mc->request.cs_indices = GNUNET_new_array (
    930       mc->request.refresh.num_cs_r_values,
    931       uint32_t);
    932 
    933     for (size_t i = 0; i < mc->request.refresh.num_coins; i++)
    934     {
    935       if (is_cs_denom[i])
    936         mc->request.cs_indices[j++] = i;
    937     }
    938   }
    939   mc->phase++;
    940 }
    941 
    942 
    943 /**
    944  * Check that the client signature authorizing the melt is valid.
    945  *
    946  * @param[in,out] mc request context to check
    947  */
    948 static void
    949 phase_check_coin_signature (
    950   struct MeltContext *mc)
    951 {
    952   /* We can now compute the commitment */
    953   {
    954     struct TALER_KappaHashBlindedPlanchetsP k_bps_h = {0};
    955     struct TALER_KappaTransferPublicKeys k_transfer_pubs = {0};
    956 
    957     for (uint8_t k = 0; k < TALER_CNC_KAPPA; k++)
    958       TALER_wallet_blinded_planchets_hash (
    959         mc->request.refresh.num_coins,
    960         mc->request.planchets[k],
    961         mc->request.denoms_h,
    962         &k_bps_h.tuple[k]);
    963 
    964     if (! mc->request.refresh.is_v27_refresh)
    965     {
    966       k_transfer_pubs.num_transfer_pubs = mc->request.refresh.num_coins;
    967       for (uint8_t k = 0; k < TALER_CNC_KAPPA; k++)
    968         k_transfer_pubs.batch[k] = &mc->request.refresh.transfer_pubs[k];
    969     }
    970 
    971     TALER_refresh_get_commitment (
    972       &mc->request.refresh.rc,
    973       &mc->request.refresh.refresh_seed,
    974       mc->request.no_blinding_seed
    975            ? NULL
    976            : &mc->request.refresh.blinding_seed,
    977       mc->request.refresh.is_v27_refresh
    978            ? NULL
    979            : &k_transfer_pubs,
    980       &k_bps_h,
    981       &mc->request.refresh.coin.coin_pub,
    982       &mc->request.refresh.amount_with_fee);
    983   }
    984 
    985   TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
    986   if (GNUNET_OK !=
    987       TALER_wallet_melt_verify (
    988         &mc->request.refresh.amount_with_fee,
    989         &mc->melted_coin_denom->meta.fees.refresh,
    990         &mc->request.refresh.rc,
    991         &mc->request.refresh.coin.denom_pub_hash,
    992         &mc->request.refresh.coin.h_age_commitment,
    993         &mc->request.refresh.coin.coin_pub,
    994         &mc->request.refresh.coin_sig))
    995   {
    996     GNUNET_break_op (0);
    997     SET_ERROR (mc,
    998                MELT_ERROR_COIN_SIGNATURE_INVALID);
    999     return;
   1000   }
   1001 
   1002   mc->phase++;
   1003 }
   1004 
   1005 
   1006 /**
   1007  * Check for information about the melted coin's denomination,
   1008  * extracting its validity status and fee structure.
   1009  * Baseline: check if deposits/refreshes are generally
   1010  * simply still allowed for this denomination.
   1011  *
   1012  * @param mc parsed request information
   1013  */
   1014 static void
   1015 phase_check_melt_valid (struct MeltContext *mc)
   1016 {
   1017   enum MeltPhase current_phase = mc->phase;
   1018   /**
   1019    * Find the old coin's denomination.
   1020    * Note that we return only on GNUNET_SYSERR,
   1021    * because GNUNET_NO for the expired denomination
   1022    * will be handled below, with the zombie-check.
   1023    */
   1024   if (GNUNET_SYSERR ==
   1025       find_denomination (mc,
   1026                          &mc->request.refresh.coin.denom_pub_hash,
   1027                          &mc->melted_coin_denom))
   1028     return;
   1029 
   1030   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1031               "Melted coin's denomination is worth %s\n",
   1032               TALER_amount2s (&mc->melted_coin_denom->meta.value));
   1033 
   1034   /* sanity-check that "total melt amount > melt fee" */
   1035   if (0 <
   1036       TALER_amount_cmp (&mc->melted_coin_denom->meta.fees.refresh,
   1037                         &mc->request.refresh.amount_with_fee))
   1038   {
   1039     GNUNET_break_op (0);
   1040     SET_ERROR (mc,
   1041                MELT_ERROR_FEES_EXCEED_CONTRIBUTION);
   1042     return;
   1043   }
   1044 
   1045   if (GNUNET_OK !=
   1046       TALER_test_coin_valid (&mc->request.refresh.coin,
   1047                              &mc->melted_coin_denom->denom_pub))
   1048   {
   1049     GNUNET_break_op (0);
   1050     SET_ERROR (mc,
   1051                MELT_ERROR_DENOMINATION_SIGNATURE_INVALID);
   1052     return;
   1053   }
   1054 
   1055   /**
   1056    * find_denomination might have set the phase to
   1057    * produce an error, but we are still investigating.
   1058    * We reset the phase.
   1059    */
   1060   mc->phase = current_phase;
   1061   mc->error.code = MELT_ERROR_NONE;
   1062 
   1063   if (GNUNET_TIME_absolute_is_past (
   1064         mc->melted_coin_denom->meta.expire_deposit.abs_time))
   1065   {
   1066     /**
   1067      * We are past deposit expiration time, but maybe this is a zombie?
   1068      */
   1069     struct TALER_DenominationHashP denom_hash;
   1070     enum GNUNET_DB_QueryStatus qs;
   1071 
   1072     /* Check that the coin is dirty (we have seen it before), as we will
   1073        not just allow melting of a *fresh* coin where the denomination was
   1074        revoked (those must be recouped) */
   1075     qs = TEH_plugin->get_coin_denomination (
   1076       TEH_plugin->cls,
   1077       &mc->request.refresh.coin.coin_pub,
   1078       &mc->known_coin_id,
   1079       &denom_hash);
   1080     if (0 > qs)
   1081     {
   1082       /* There is no good reason for a serialization failure here: */
   1083       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
   1084       SET_ERROR (mc,
   1085                  MELT_ERROR_DB_FETCH_FAILED);
   1086       return;
   1087     }
   1088     if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
   1089     {
   1090       /* We never saw this coin before, so _this_ justification is not OK.
   1091        * Note that the error was already set in find_denominations. */
   1092       GNUNET_assert (MELT_ERROR_DENOMINATION_EXPIRED ==
   1093                      mc->error.code);
   1094       GNUNET_assert (MELT_PHASE_GENERATE_REPLY_ERROR ==
   1095                      mc->phase);
   1096       return;
   1097     }
   1098     /* sanity check */
   1099     if (0 !=
   1100         GNUNET_memcmp (&denom_hash,
   1101                        &mc->request.refresh.coin.denom_pub_hash))
   1102     {
   1103       GNUNET_break_op (0);
   1104       SET_ERROR_WITH_DETAIL (mc,
   1105                              MELT_COIN_CONFLICTING_DENOMINATION_KEY,
   1106                              denom_h,
   1107                              denom_hash);
   1108       return;
   1109     }
   1110     /* Minor optimization: no need to run the
   1111        "ensure_coin_known" part of the transaction */
   1112     mc->coin_is_known = true;
   1113     /* check later that zombie is satisfied */
   1114     mc->zombie_required = true;
   1115   }
   1116   mc->phase++;
   1117 }
   1118 
   1119 
   1120 /**
   1121  * The request for melt was parsed successfully.
   1122  * Sign and persist the chosen blinded coins for the reveal step.
   1123  *
   1124  * @param mc The context for the current melt request
   1125  */
   1126 static void
   1127 phase_prepare_transaction (
   1128   struct MeltContext *mc)
   1129 {
   1130   mc->request.refresh.denom_sigs
   1131     = GNUNET_new_array (
   1132         mc->request.refresh.num_coins,
   1133         struct TALER_BlindedDenominationSignature);
   1134   mc->request.refresh.noreveal_index =
   1135     GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_STRONG,
   1136                               TALER_CNC_KAPPA);
   1137 
   1138   /* Choose and sign the coins */
   1139   {
   1140     struct TEH_CoinSignData csds[mc->request.refresh.num_coins];
   1141     enum TALER_ErrorCode ec_denomination_sign;
   1142     size_t noreveal_idx = mc->request.refresh.noreveal_index;
   1143 
   1144     memset (csds,
   1145             0,
   1146             sizeof(csds));
   1147 
   1148     /* Pick the chosen blinded coins */
   1149     for (size_t i = 0; i<mc->request.refresh.num_coins; i++)
   1150     {
   1151       csds[i].bp = &mc->request.planchets[noreveal_idx][i];
   1152       csds[i].h_denom_pub = &mc->request.denoms_h[i];
   1153     }
   1154 
   1155     ec_denomination_sign = TEH_keys_denomination_batch_sign (
   1156       mc->request.refresh.num_coins,
   1157       csds,
   1158       true, /* for melt */
   1159       mc->request.refresh.denom_sigs);
   1160     if (TALER_EC_NONE != ec_denomination_sign)
   1161     {
   1162       GNUNET_break (0);
   1163       SET_ERROR_WITH_FIELD (mc,
   1164                             MELT_ERROR_DENOMINATION_SIGN,
   1165                             ec_denomination_sign);
   1166       return;
   1167     }
   1168 
   1169     /* Save the hash of chosen planchets */
   1170     mc->request.refresh.selected_h =
   1171       mc->request.kappa_planchets_h.tuple[noreveal_idx];
   1172 
   1173     /* If applicable, save the chosen transfer public keys */
   1174     if (! mc->request.refresh.is_v27_refresh)
   1175       mc->request.refresh.transfer_pubs =
   1176         mc->request.transfer_pubs[noreveal_idx];
   1177 
   1178     /**
   1179      * For the denominations with cipher CS, calculate the R-values
   1180      * and save the choices we made now, as at a later point, the
   1181      * private keys for the denominations might now be available anymore
   1182      * to make the same choice again.
   1183      */
   1184     if (0 <  mc->request.refresh.num_cs_r_values)
   1185     {
   1186       size_t num_cs_r_values = mc->request.refresh.num_cs_r_values;
   1187       struct TEH_CsDeriveData cdds[num_cs_r_values];
   1188       struct GNUNET_CRYPTO_CsSessionNonce nonces[num_cs_r_values];
   1189 
   1190       memset (nonces,
   1191               0,
   1192               sizeof(nonces));
   1193       mc->request.refresh.cs_r_values =
   1194         GNUNET_new_array (num_cs_r_values,
   1195                           struct GNUNET_CRYPTO_CSPublicRPairP);
   1196       mc->request.refresh.cs_r_choices = 0;
   1197 
   1198       GNUNET_assert (! mc->request.refresh.no_blinding_seed);
   1199       TALER_cs_derive_nonces_from_seed (
   1200         &mc->request.refresh.blinding_seed,
   1201         true, /* for melt */
   1202         num_cs_r_values,
   1203         mc->request.cs_indices,
   1204         nonces);
   1205 
   1206       for (size_t i = 0; i < num_cs_r_values; i++)
   1207       {
   1208         size_t idx = mc->request.cs_indices[i];
   1209 
   1210         GNUNET_assert (idx < mc->request.refresh.num_coins);
   1211         cdds[i].h_denom_pub = &mc->request.denoms_h[idx];
   1212         cdds[i].nonce = &nonces[i];
   1213       }
   1214 
   1215       /**
   1216        * Let the crypto helper generate the R-values and
   1217        * make the choices
   1218        */
   1219       if (TALER_EC_NONE !=
   1220           TEH_keys_denomination_cs_batch_r_pub_simple (
   1221             mc->request.refresh.num_cs_r_values,
   1222             cdds,
   1223             true, /* for melt */
   1224             mc->request.refresh.cs_r_values))
   1225       {
   1226         GNUNET_break (0);
   1227         SET_ERROR (mc,
   1228                    MELT_ERROR_CRYPTO_HELPER);
   1229         return;
   1230       }
   1231 
   1232       /* Now save the choices for the selected bits */
   1233       GNUNET_assert (num_cs_r_values <= 64);
   1234       for (size_t i = 0; i < num_cs_r_values; i++)
   1235       {
   1236         size_t idx = mc->request.cs_indices[i];
   1237 
   1238         struct TALER_BlindedDenominationSignature *sig =
   1239           &mc->request.refresh.denom_sigs[idx];
   1240         uint64_t bit = sig->blinded_sig->details.blinded_cs_answer.b;
   1241 
   1242         mc->request.refresh.cs_r_choices |= bit << i;
   1243         GNUNET_static_assert (
   1244           TALER_MAX_COINS <=
   1245           sizeof(mc->request.refresh.cs_r_choices) * 8);
   1246       }
   1247     }
   1248   }
   1249   mc->phase++;
   1250 }
   1251 
   1252 
   1253 /**
   1254  * Generates response for the melt request.
   1255  *
   1256  * @param mc melt operation context
   1257  */
   1258 static void
   1259 phase_generate_reply_success (struct MeltContext *mc)
   1260 {
   1261   struct TALER_EXCHANGEDB_Refresh_vDOLDPLUS *db_obj;
   1262   struct TALER_ExchangePublicKeyP pub;
   1263   struct TALER_ExchangeSignatureP sig;
   1264   enum TALER_ErrorCode ec_confirmation_sign;
   1265 
   1266   db_obj = mc->request.is_idempotent
   1267     ? &mc->request.refresh_idem
   1268     : &mc->request.refresh;
   1269   ec_confirmation_sign =
   1270     TALER_exchange_online_melt_confirmation_sign (
   1271       &TEH_keys_exchange_sign_,
   1272       &db_obj->rc,
   1273       db_obj->noreveal_index,
   1274       &pub,
   1275       &sig);
   1276   if (TALER_EC_NONE != ec_confirmation_sign)
   1277   {
   1278     SET_ERROR_WITH_FIELD (mc,
   1279                           MELT_ERROR_CONFIRMATION_SIGN,
   1280                           ec_confirmation_sign);
   1281     return;
   1282   }
   1283 
   1284   finish_loop (mc,
   1285                TALER_MHD_REPLY_JSON_PACK (
   1286                  mc->rc->connection,
   1287                  MHD_HTTP_OK,
   1288                  GNUNET_JSON_pack_uint64 ("noreveal_index",
   1289                                           db_obj->noreveal_index),
   1290                  GNUNET_JSON_pack_data_auto ("exchange_sig",
   1291                                              &sig),
   1292                  GNUNET_JSON_pack_data_auto ("exchange_pub",
   1293                                              &pub)));
   1294 }
   1295 
   1296 
   1297 /**
   1298  * Check if the melt request is replayed and we already have an answer.
   1299  * If so, replay the existing answer and return the HTTP response.
   1300  *
   1301  * @param[in,out] mc parsed request data
   1302  * @return true if the request is idempotent with an existing request
   1303  *    false if we did not find the request in the DB and did not set @a mret
   1304  */
   1305 static bool
   1306 melt_is_idempotent (
   1307   struct MeltContext *mc)
   1308 {
   1309   enum GNUNET_DB_QueryStatus qs;
   1310 
   1311   qs = TEH_plugin->get_refresh (
   1312     TEH_plugin->cls,
   1313     &mc->request.refresh.rc,
   1314     &mc->request.refresh_idem);
   1315   if (0 > qs)
   1316   {
   1317     /* FIXME: soft error not handled correctly! */
   1318     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
   1319     if (GNUNET_DB_STATUS_HARD_ERROR == qs)
   1320       SET_ERROR_WITH_DETAIL (mc,
   1321                              MELT_ERROR_DB_FETCH_FAILED,
   1322                              db_fetch_context,
   1323                              "get_refresh");
   1324     return true; /* Well, kind-of. */
   1325   }
   1326   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
   1327     return false;
   1328 
   1329   mc->request.is_idempotent = true;
   1330   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
   1331               "request is idempotent\n");
   1332 
   1333   /* Generate idempotent reply */
   1334   TEH_METRICS_num_requests[TEH_MT_REQUEST_IDEMPOTENT_MELT]++;
   1335   mc->phase = MELT_PHASE_GENERATE_REPLY_SUCCESS;
   1336   mc->error.code = MELT_ERROR_NONE;
   1337   return true;
   1338 }
   1339 
   1340 
   1341 /**
   1342  * Reports an error, potentially with details.
   1343  * That is, it puts a error-type specific response into the MHD queue.
   1344  * It will do a idempotency check first, if needed for the error type.
   1345  *
   1346  * @param mc melt context
   1347  */
   1348 static void
   1349 phase_generate_reply_error (
   1350   struct MeltContext *mc)
   1351 {
   1352   GNUNET_assert (MELT_PHASE_GENERATE_REPLY_ERROR == mc->phase);
   1353   GNUNET_assert (MELT_ERROR_NONE != mc->error.code);
   1354 
   1355   if (IDEMPOTENCY_CHECK_REQUIRED (mc->error.code) &&
   1356       melt_is_idempotent (mc))
   1357   {
   1358     return;
   1359   }
   1360 
   1361   switch (mc->error.code)
   1362   {
   1363   case MELT_ERROR_NONE:
   1364     break;
   1365   case MELT_ERROR_REQUEST_PARAMETER_MALFORMED:
   1366     finish_loop (mc,
   1367                  TALER_MHD_reply_with_error (
   1368                    mc->rc->connection,
   1369                    MHD_HTTP_BAD_REQUEST,
   1370                    TALER_EC_GENERIC_PARAMETER_MALFORMED,
   1371                    mc->error.details.request_parameter_malformed));
   1372     return;
   1373   case MELT_ERROR_KEYS_MISSING:
   1374     finish_loop (mc,
   1375                  TALER_MHD_reply_with_error (
   1376                    mc->rc->connection,
   1377                    MHD_HTTP_INTERNAL_SERVER_ERROR,
   1378                    TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING,
   1379                    NULL));
   1380     return;
   1381   case MELT_ERROR_DB_FETCH_FAILED:
   1382     finish_loop (mc,
   1383                  TALER_MHD_reply_with_error (
   1384                    mc->rc->connection,
   1385                    MHD_HTTP_INTERNAL_SERVER_ERROR,
   1386                    TALER_EC_GENERIC_DB_FETCH_FAILED,
   1387                    mc->error.details.db_fetch_context));
   1388     return;
   1389   case MELT_ERROR_DB_INVARIANT_FAILURE:
   1390     finish_loop (mc,
   1391                  TALER_MHD_reply_with_error (
   1392                    mc->rc->connection,
   1393                    MHD_HTTP_INTERNAL_SERVER_ERROR,
   1394                    TALER_EC_GENERIC_DB_INVARIANT_FAILURE,
   1395                    NULL));
   1396     return;
   1397   case MELT_ERROR_DB_PREFLIGHT_FAILURE:
   1398     finish_loop (mc,
   1399                  TALER_MHD_reply_with_error (
   1400                    mc->rc->connection,
   1401                    MHD_HTTP_INTERNAL_SERVER_ERROR,
   1402                    TALER_EC_GENERIC_DB_COMMIT_FAILED,
   1403                    "make_coin_known"));
   1404     return;
   1405   case MELT_ERROR_DB_MAKE_COIN_KNOW_FAILURE:
   1406     finish_loop (mc,
   1407                  TALER_MHD_reply_with_error (
   1408                    mc->rc->connection,
   1409                    MHD_HTTP_INTERNAL_SERVER_ERROR,
   1410                    TALER_EC_GENERIC_DB_START_FAILED,
   1411                    "preflight failure"));
   1412     return;
   1413   case MELT_ERROR_COIN_UNKNOWN:
   1414     finish_loop (mc,
   1415                  TALER_MHD_reply_with_ec (
   1416                    mc->rc->connection,
   1417                    TALER_EC_EXCHANGE_GENERIC_COIN_UNKNOWN,
   1418                    NULL));
   1419     return;
   1420   case MELT_COIN_CONFLICTING_DENOMINATION_KEY:
   1421     finish_loop (mc,
   1422                  TALER_MHD_reply_with_ec (
   1423                    mc->rc->connection,
   1424                    TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY,
   1425                    TALER_B2S (&mc->error.details.denom_h)));
   1426     return;
   1427   case MELT_ERROR_COIN_EXPIRED_NO_ZOMBIE:
   1428     finish_loop (mc,
   1429                  TALER_MHD_reply_with_error (
   1430                    mc->rc->connection,
   1431                    MHD_HTTP_BAD_REQUEST,
   1432                    TALER_EC_EXCHANGE_MELT_COIN_EXPIRED_NO_ZOMBIE,
   1433                    NULL));
   1434     return;
   1435   case MELT_ERROR_DENOMINATION_SIGN:
   1436     finish_loop (mc,
   1437                  TALER_MHD_reply_with_ec (
   1438                    mc->rc->connection,
   1439                    mc->error.details.ec_denomination_sign,
   1440                    NULL));
   1441     return;
   1442   case MELT_ERROR_DENOMINATION_SIGNATURE_INVALID:
   1443     finish_loop (mc,
   1444                  TALER_MHD_reply_with_error (mc->rc->connection,
   1445                                              MHD_HTTP_FORBIDDEN,
   1446                                              TALER_EC_EXCHANGE_DENOMINATION_SIGNATURE_INVALID,
   1447                                              NULL));
   1448     return;
   1449   case MELT_ERROR_DENOMINATION_KEY_UNKNOWN:
   1450     GNUNET_break_op (0);
   1451     finish_loop (mc,
   1452                  TEH_RESPONSE_reply_unknown_denom_pub_hash (
   1453                    mc->rc->connection,
   1454                    &mc->error.details.denom_h));
   1455     return;
   1456   case MELT_ERROR_DENOMINATION_EXPIRED:
   1457     GNUNET_break_op (0);
   1458     finish_loop (mc,
   1459                  TEH_RESPONSE_reply_expired_denom_pub_hash (
   1460                    mc->rc->connection,
   1461                    &mc->error.details.denom_h,
   1462                    TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED,
   1463                    "MELT"));
   1464     return;
   1465   case MELT_ERROR_DENOMINATION_VALIDITY_IN_FUTURE:
   1466     finish_loop (mc,
   1467                  TEH_RESPONSE_reply_expired_denom_pub_hash (
   1468                    mc->rc->connection,
   1469                    &mc->error.details.denom_h,
   1470                    TALER_EC_EXCHANGE_GENERIC_DENOMINATION_VALIDITY_IN_FUTURE,
   1471                    "MELT"));
   1472     return;
   1473   case MELT_ERROR_DENOMINATION_REVOKED:
   1474     GNUNET_break_op (0);
   1475     finish_loop (mc,
   1476                  TALER_MHD_reply_with_ec (
   1477                    mc->rc->connection,
   1478                    TALER_EC_EXCHANGE_GENERIC_DENOMINATION_REVOKED,
   1479                    NULL));
   1480     return;
   1481   case MELT_ERROR_COIN_CIPHER_MISMATCH:
   1482     finish_loop (mc,
   1483                  TALER_MHD_reply_with_ec (
   1484                    mc->rc->connection,
   1485                    TALER_EC_EXCHANGE_GENERIC_CIPHER_MISMATCH,
   1486                    NULL));
   1487     return;
   1488   case MELT_ERROR_BLINDING_SEED_REQUIRED:
   1489     finish_loop (mc,
   1490                  TALER_MHD_reply_with_ec (
   1491                    mc->rc->connection,
   1492                    TALER_EC_GENERIC_PARAMETER_MISSING,
   1493                    "blinding_seed"));
   1494     return;
   1495   case MELT_ERROR_CRYPTO_HELPER:
   1496     finish_loop (mc,
   1497                  TALER_MHD_reply_with_ec (
   1498                    mc->rc->connection,
   1499                    TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
   1500                    NULL));
   1501     return;
   1502   case MELT_ERROR_AGE_RESTRICTION_NOT_SUPPORTED_BY_DENOMINATION:
   1503     {
   1504       char msg[256];
   1505 
   1506       GNUNET_snprintf (msg,
   1507                        sizeof(msg),
   1508                        "denomination %s does not support age restriction",
   1509                        GNUNET_h2s (&mc->error.details.denom_h.hash));
   1510       finish_loop (mc,
   1511                    TALER_MHD_reply_with_ec (
   1512                      mc->rc->connection,
   1513                      TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN,
   1514                      msg));
   1515       return;
   1516     }
   1517   case  MELT_ERROR_AGE_RESTRICTION_COMMITMENT_INVALID:
   1518     finish_loop (mc,
   1519                  TALER_MHD_reply_with_ec (
   1520                    mc->rc->connection,
   1521                    TALER_EC_EXCHANGE_REFRESHES_REVEAL_AGE_RESTRICTION_COMMITMENT_INVALID,
   1522                    "old_age_commitment_h"));
   1523     return;
   1524   case MELT_ERROR_AMOUNT_OVERFLOW:
   1525     finish_loop (mc,
   1526                  TALER_MHD_reply_with_error (
   1527                    mc->rc->connection,
   1528                    MHD_HTTP_BAD_REQUEST,
   1529                    TALER_EC_EXCHANGE_REFRESHES_REVEAL_COST_CALCULATION_OVERFLOW,
   1530                    "amount"));
   1531     return;
   1532   case MELT_ERROR_AMOUNT_PLUS_FEE_OVERFLOW:
   1533     finish_loop (mc,
   1534                  TALER_MHD_reply_with_error (
   1535                    mc->rc->connection,
   1536                    MHD_HTTP_INTERNAL_SERVER_ERROR,
   1537                    TALER_EC_EXCHANGE_REFRESHES_REVEAL_COST_CALCULATION_OVERFLOW,
   1538                    "amount+fee"));
   1539     return;
   1540   case MELT_ERROR_FEES_EXCEED_CONTRIBUTION:
   1541     finish_loop (mc,
   1542                  TALER_MHD_reply_with_error (mc->rc->connection,
   1543                                              MHD_HTTP_BAD_REQUEST,
   1544                                              TALER_EC_EXCHANGE_MELT_FEES_EXCEED_CONTRIBUTION,
   1545                                              NULL));
   1546     return;
   1547   case MELT_ERROR_AMOUNT_WITH_FEE_INCORRECT:
   1548     finish_loop (mc,
   1549                  TALER_MHD_reply_with_error (
   1550                    mc->rc->connection,
   1551                    MHD_HTTP_BAD_REQUEST,
   1552                    TALER_EC_EXCHANGE_REFRESHES_REVEAL_COST_CALCULATION_OVERFLOW,
   1553                    "value_with_fee incorrect"));
   1554     return;
   1555   case MELT_ERROR_CONFIRMATION_SIGN:
   1556     finish_loop (mc,
   1557                  TALER_MHD_reply_with_ec (
   1558                    mc->rc->connection,
   1559                    mc->error.details.ec_confirmation_sign,
   1560                    NULL));
   1561     return;
   1562   case MELT_ERROR_INSUFFICIENT_FUNDS:
   1563     finish_loop (mc,
   1564                  TEH_RESPONSE_reply_coin_insufficient_funds (
   1565                    mc->rc->connection,
   1566                    TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS,
   1567                    &mc->request.refresh.coin.denom_pub_hash,
   1568                    &mc->request.refresh.coin.coin_pub));
   1569     return;
   1570   case MELT_ERROR_DUPLICATE_PLANCHET:
   1571     finish_loop (mc,
   1572                  TALER_MHD_reply_with_error (
   1573                    mc->rc->connection,
   1574                    MHD_HTTP_BAD_REQUEST,
   1575                    TALER_EC_GENERIC_PARAMETER_MALFORMED,   /* FIXME: new error! */
   1576                    "duplicate planchet"));
   1577     return;
   1578   case MELT_ERROR_DUPLICATE_TRANSFER_PUB:
   1579     finish_loop (mc,
   1580                  TALER_MHD_reply_with_error (
   1581                    mc->rc->connection,
   1582                    MHD_HTTP_BAD_REQUEST,
   1583                    TALER_EC_GENERIC_PARAMETER_MALFORMED,   /* FIXME: new error! */
   1584                    "duplicate transfer_pub"));
   1585     return;
   1586   case MELT_ERROR_NONCE_RESUSE:
   1587     finish_loop (mc,
   1588                  TALER_MHD_reply_with_error (
   1589                    mc->rc->connection,
   1590                    MHD_HTTP_BAD_REQUEST,
   1591                    TALER_EC_GENERIC_PARAMETER_MALFORMED, /* FIXME: new error */
   1592                    "nonce reuse"));
   1593     return;
   1594   case MELT_ERROR_COIN_SIGNATURE_INVALID:
   1595     finish_loop (mc,
   1596                  TALER_MHD_reply_with_ec (
   1597                    mc->rc->connection,
   1598                    TALER_EC_EXCHANGE_MELT_COIN_SIGNATURE_INVALID,
   1599                    NULL));
   1600     return;
   1601   }
   1602   GNUNET_break (0);
   1603   finish_loop (mc,
   1604                TALER_MHD_reply_with_error (
   1605                  mc->rc->connection,
   1606                  MHD_HTTP_INTERNAL_SERVER_ERROR,
   1607                  TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
   1608                  "error phase without error"));
   1609 }
   1610 
   1611 
   1612 /**
   1613  * Function implementing melt transaction.  Runs the
   1614  * transaction logic; IF it returns a non-error code, the transaction
   1615  * logic MUST NOT queue a MHD response.  IF it returns an hard error,
   1616  * the transaction logic MUST queue a MHD response and set @a mhd_ret.
   1617  * IF it returns the soft error code, the function MAY be called again
   1618  * to retry and MUST not queue a MHD response.
   1619  *
   1620  * @param cls a `struct MeltContext *`
   1621  * @param connection MHD request which triggered the transaction
   1622  * @param[out] mhd_ret set to MHD response status for @a connection,
   1623  *             if transaction failed (!)
   1624  * @return transaction status
   1625  */
   1626 static enum GNUNET_DB_QueryStatus
   1627 melt_transaction (
   1628   void *cls,
   1629   struct MHD_Connection *connection,
   1630   MHD_RESULT *mhd_ret)
   1631 {
   1632   struct MeltContext *mc = cls;
   1633   enum GNUNET_DB_QueryStatus qs;
   1634   bool balance_ok;
   1635   bool found;
   1636   bool nonce_reuse;
   1637   uint32_t noreveal_index;
   1638   struct TALER_Amount insufficient_funds;
   1639 
   1640   (void) connection;
   1641   (void) mhd_ret;
   1642 
   1643   qs = TEH_plugin->do_refresh (TEH_plugin->cls,
   1644                                &mc->request.refresh,
   1645                                &mc->now,
   1646                                &found,
   1647                                &noreveal_index,
   1648                                &mc->zombie_required,
   1649                                &nonce_reuse,
   1650                                &balance_ok,
   1651                                &insufficient_funds);
   1652   if (0 > qs)
   1653   {
   1654     if (GNUNET_DB_STATUS_HARD_ERROR == qs)
   1655       SET_ERROR_WITH_DETAIL (mc,
   1656                              MELT_ERROR_DB_FETCH_FAILED,
   1657                              db_fetch_context,
   1658                              "do_refresh");
   1659     return qs;
   1660   }
   1661   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
   1662   {
   1663     GNUNET_break_op (0);
   1664     SET_ERROR (mc,
   1665                MELT_ERROR_COIN_UNKNOWN);
   1666     return GNUNET_DB_STATUS_HARD_ERROR;
   1667   }
   1668   if (found)
   1669   {
   1670     /**
   1671      * This request is idempotent, set the nonreveal_index
   1672      * to the previous one and reply success.
   1673      */
   1674     mc->request.refresh.noreveal_index = noreveal_index;
   1675     mc->phase = MELT_PHASE_GENERATE_REPLY_SUCCESS;
   1676     mc->error.code = MELT_ERROR_NONE;
   1677     return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
   1678   }
   1679   if (nonce_reuse)
   1680   {
   1681     GNUNET_break_op (0);
   1682     SET_ERROR (mc,
   1683                MELT_ERROR_NONCE_RESUSE);
   1684     return GNUNET_DB_STATUS_HARD_ERROR;
   1685   }
   1686   if (! balance_ok)
   1687   {
   1688     GNUNET_break_op (0);
   1689     SET_ERROR_WITH_FIELD (mc,
   1690                           MELT_ERROR_INSUFFICIENT_FUNDS,
   1691                           insufficient_funds);
   1692     return GNUNET_DB_STATUS_HARD_ERROR;
   1693   }
   1694   if (mc->zombie_required)
   1695   {
   1696     GNUNET_break_op (0);
   1697     SET_ERROR (mc,
   1698                MELT_ERROR_COIN_EXPIRED_NO_ZOMBIE);
   1699     return GNUNET_DB_STATUS_HARD_ERROR;
   1700   }
   1701 
   1702   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
   1703     TEH_METRICS_num_success[TEH_MT_SUCCESS_MELT]++;
   1704   return qs;
   1705 }
   1706 
   1707 
   1708 /**
   1709  * The request was prepared successfully.
   1710  * Run the main DB transaction.
   1711  *
   1712  * @param mc The context for the current melt request
   1713  */
   1714 static void
   1715 phase_run_transaction (
   1716   struct MeltContext *mc)
   1717 {
   1718   if (GNUNET_SYSERR ==
   1719       TEH_plugin->preflight (TEH_plugin->cls))
   1720   {
   1721     GNUNET_break (0);
   1722     SET_ERROR (mc,
   1723                MELT_ERROR_DB_PREFLIGHT_FAILURE);
   1724     return;
   1725   }
   1726 
   1727   /* first, make sure coin is known */
   1728   if (! mc->coin_is_known)
   1729   {
   1730     MHD_RESULT mhd_ret = -1;
   1731     enum GNUNET_DB_QueryStatus qs;
   1732 
   1733     for (unsigned int tries = 0; tries<MAX_TRANSACTION_COMMIT_RETRIES; tries++)
   1734     {
   1735       qs = TEH_make_coin_known (&mc->request.refresh.coin,
   1736                                 mc->rc->connection,
   1737                                 &mc->known_coin_id,
   1738                                 &mhd_ret);
   1739       if (GNUNET_DB_STATUS_SOFT_ERROR != qs)
   1740         break;
   1741     }
   1742     if (0 > qs)
   1743     {
   1744       GNUNET_break (0);
   1745       /* Check if an answer has been queued */
   1746       switch (mhd_ret)
   1747       {
   1748       case MHD_NO:
   1749         mc->phase = MELT_PHASE_RETURN_NO;
   1750         return;
   1751       case MHD_YES:
   1752         mc->phase = MELT_PHASE_RETURN_YES;
   1753         return;
   1754       default: /* ignore */
   1755         ;
   1756       }
   1757       SET_ERROR (mc,
   1758                  MELT_ERROR_DB_MAKE_COIN_KNOW_FAILURE);
   1759       return;
   1760     }
   1761   }
   1762 
   1763   /* run main database transaction */
   1764   {
   1765     MHD_RESULT mhd_ret = -1;
   1766     enum GNUNET_GenericReturnValue ret;
   1767     enum MeltPhase current_phase = mc->phase;
   1768 
   1769     GNUNET_assert (MELT_PHASE_RUN_TRANSACTION ==
   1770                    current_phase);
   1771     ret = TEH_DB_run_transaction (mc->rc->connection,
   1772                                   "run melt",
   1773                                   TEH_MT_REQUEST_MELT,
   1774                                   &mhd_ret,
   1775                                   &melt_transaction,
   1776                                   mc);
   1777     if (GNUNET_OK != ret)
   1778     {
   1779       GNUNET_break (0);
   1780       /* Check if an answer has been queued */
   1781       switch (mhd_ret)
   1782       {
   1783       case MHD_NO:
   1784         mc->phase = MELT_PHASE_RETURN_NO;
   1785         return;
   1786       case MHD_YES:
   1787         mc->phase = MELT_PHASE_RETURN_YES;
   1788         return;
   1789       default: /* ignore */
   1790         ;
   1791       }
   1792       GNUNET_assert (MELT_ERROR_NONE != mc->error.code);
   1793       GNUNET_assert (MELT_PHASE_GENERATE_REPLY_ERROR == mc->phase);
   1794       return;
   1795     }
   1796     /**
   1797      * In case of idempotency (which is not an error condition),
   1798      * the phase has changed in melt_transaction.
   1799      * We simple return.
   1800      */
   1801     if (current_phase != mc->phase)
   1802       return;
   1803   }
   1804   mc->phase++;
   1805 }
   1806 
   1807 
   1808 MHD_RESULT
   1809 TEH_handler_melt (
   1810   struct TEH_RequestContext *rc,
   1811   const json_t *root,
   1812   const char *const args[0])
   1813 {
   1814   struct MeltContext *mc = rc->rh_ctx;
   1815 
   1816   (void) args;
   1817   if (NULL == mc)
   1818   {
   1819     mc = GNUNET_new (struct MeltContext);
   1820     rc->rh_ctx = mc;
   1821     rc->rh_cleaner = &clean_melt_rc;
   1822     mc->rc = rc;
   1823     mc->now = GNUNET_TIME_timestamp_get ();
   1824   }
   1825 
   1826   while (true)
   1827   {
   1828     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1829                 "melt processing in phase %d\n",
   1830                 mc->phase);
   1831     switch (mc->phase)
   1832     {
   1833     case MELT_PHASE_PARSE:
   1834       phase_parse_request (mc,
   1835                            root);
   1836       break;
   1837     case MELT_PHASE_CHECK_MELT_VALID:
   1838       phase_check_melt_valid (mc);
   1839       break;
   1840     case MELT_PHASE_CHECK_KEYS:
   1841       phase_check_keys (mc);
   1842       break;
   1843     case MELT_PHASE_CHECK_COIN_SIGNATURE:
   1844       phase_check_coin_signature (mc);
   1845       break;
   1846     case MELT_PHASE_PREPARE_TRANSACTION:
   1847       phase_prepare_transaction (mc);
   1848       break;
   1849     case MELT_PHASE_RUN_TRANSACTION:
   1850       phase_run_transaction (mc);
   1851       break;
   1852     case MELT_PHASE_GENERATE_REPLY_SUCCESS:
   1853       phase_generate_reply_success (mc);
   1854       break;
   1855     case MELT_PHASE_GENERATE_REPLY_ERROR:
   1856       phase_generate_reply_error (mc);
   1857       break;
   1858     case MELT_PHASE_RETURN_YES:
   1859       return MHD_YES;
   1860     case MELT_PHASE_RETURN_NO:
   1861       return MHD_NO;
   1862     }
   1863   }
   1864 }