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_v27.c (52308B)


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