LCOV - code coverage report
Current view: top level - exchange - taler-exchange-httpd_melt_v27.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 335 603 55.6 %
Date: 2025-06-22 12:09:43 Functions: 16 16 100.0 %

          Line data    Source code
       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          21 : TEH_melt_v27_cleanup ()
     297             : {
     298             :   struct MeltContext *mc;
     299             : 
     300          21 :   while (NULL != (mc = mc_head))
     301             :   {
     302           0 :     GNUNET_CONTAINER_DLL_remove (mc_head,
     303             :                                  mc_tail,
     304             :                                  mc);
     305           0 :     MHD_resume_connection (mc->rc->connection);
     306             :   }
     307          21 : }
     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          37 : finish_loop (struct MeltContext *mc,
     318             :              MHD_RESULT mres)
     319             : {
     320          37 :   mc->phase = (MHD_YES == mres)
     321             :                          ? MELT_PHASE_RETURN_YES
     322          37 :                          : MELT_PHASE_RETURN_NO;
     323          37 : }
     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          38 : free_refresh (struct TALER_EXCHANGEDB_Refresh_v27 *re)
     333             : {
     334          38 :   if (NULL != re->denom_sigs)
     335             :   {
     336         246 :     for (size_t i = 0; i<re->num_coins; i++)
     337         208 :       TALER_blinded_denom_sig_free (&re->denom_sigs[i]);
     338          38 :     GNUNET_free (re->denom_sigs);
     339             :   }
     340          38 :   GNUNET_free (re->cs_r_values);
     341          38 :   GNUNET_free (re->denom_serials);
     342          38 :   GNUNET_free (re->denom_pub_hashes);
     343          38 :   TALER_denom_sig_free (&re->coin.denom_sig);
     344          38 : }
     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          38 : clean_melt_rc (struct TEH_RequestContext *rc)
     356             : {
     357          38 :   struct MeltContext *mc = rc->rh_ctx;
     358             : 
     359          38 :   GNUNET_free (mc->request.denoms_h);
     360         152 :   for (uint8_t k = 0; k<TALER_CNC_KAPPA; k++)
     361             :   {
     362         738 :     for (size_t i = 0; i<mc->request.refresh.num_coins; i++)
     363         624 :       TALER_blinded_planchet_free (&mc->request.planchets[k][i]);
     364         114 :     GNUNET_free (mc->request.planchets[k]);
     365             :   }
     366          38 :   free_refresh (&mc->request.refresh);
     367          38 :   if (mc->request.is_idempotent)
     368             :   {
     369           0 :     free_refresh (&mc->request.refresh_idem);
     370             :   }
     371          38 :   GNUNET_free (mc->request.cs_indices);
     372          38 :   GNUNET_free (mc);
     373          38 : }
     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          38 : 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          38 :     GNUNET_JSON_spec_fixed_auto ("old_coin_pub",
     392             :                                  &mc->request.refresh.coin.coin_pub),
     393          38 :     GNUNET_JSON_spec_fixed_auto ("old_denom_pub_h",
     394             :                                  &mc->request.refresh.coin.denom_pub_hash),
     395          38 :     GNUNET_JSON_spec_mark_optional (
     396          38 :       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          38 :     TALER_JSON_spec_denom_sig ("old_denom_sig",
     400             :                                &mc->request.refresh.coin.denom_sig),
     401          38 :     GNUNET_JSON_spec_fixed_auto ("refresh_seed",
     402             :                                  &mc->request.refresh.refresh_seed),
     403          38 :     GNUNET_JSON_spec_mark_optional (
     404          38 :       GNUNET_JSON_spec_fixed_auto ("blinding_seed",
     405             :                                    &mc->request.refresh.blinding_seed),
     406             :       &mc->request.refresh.no_blinding_seed),
     407          38 :     TALER_JSON_spec_amount ("value_with_fee",
     408             :                             TEH_currency,
     409             :                             &mc->request.refresh.amount_with_fee),
     410          38 :     GNUNET_JSON_spec_array_const ("denoms_h",
     411             :                                   &j_denoms_h),
     412          38 :     GNUNET_JSON_spec_array_const ("coin_evs",
     413             :                                   &j_coin_evs),
     414          38 :     GNUNET_JSON_spec_fixed_auto ("confirm_sig",
     415             :                                  &mc->request.refresh.coin_sig),
     416          38 :     GNUNET_JSON_spec_end ()
     417             :   };
     418             : 
     419          38 :   res = TALER_MHD_parse_json_data (mc->rc->connection,
     420             :                                    root,
     421             :                                    spec);
     422          38 :   if (GNUNET_OK != res)
     423             :   {
     424           0 :     GNUNET_break_op (0);
     425           0 :     mc->phase = (GNUNET_NO == res)
     426             :       ? MELT_PHASE_RETURN_YES
     427           0 :       : MELT_PHASE_RETURN_NO;
     428           0 :     return;
     429             :   }
     430             : 
     431             :   /* validate array size */
     432             :   GNUNET_static_assert (
     433             :     TALER_MAX_COINS < INT_MAX / TALER_CNC_KAPPA);
     434             : 
     435          38 :   mc->request.refresh.num_coins = json_array_size (j_denoms_h);
     436          38 :   if (0 == mc->request.refresh.num_coins)
     437             :   {
     438           0 :     GNUNET_break_op (0);
     439           0 :     SET_ERROR_WITH_DETAIL (mc,
     440             :                            MELT_ERROR_REQUEST_PARAMETER_MALFORMED,
     441             :                            request_parameter_malformed,
     442             :                            "denoms_h must not be empty");
     443           0 :     return;
     444             :   }
     445          38 :   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           0 :     GNUNET_break_op (0);
     453           0 :     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           0 :     return;
     458             :   }
     459          38 :   else if (TALER_CNC_KAPPA != json_array_size (j_coin_evs))
     460             :   {
     461           0 :     GNUNET_break_op (0);
     462           0 :     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           0 :     return;
     467             :   }
     468             : 
     469             :   /* Extract the denomination hashes */
     470             :   {
     471             :     size_t idx;
     472             :     json_t *value;
     473             : 
     474             :     mc->request.denoms_h
     475          38 :       = GNUNET_new_array (mc->request.refresh.num_coins,
     476             :                           struct TALER_DenominationHashP);
     477             : 
     478         246 :     json_array_foreach (j_denoms_h, idx, value) {
     479             :       struct GNUNET_JSON_Specification ispec[] = {
     480         208 :         GNUNET_JSON_spec_fixed_auto (NULL,
     481             :                                      &mc->request.denoms_h[idx]),
     482         208 :         GNUNET_JSON_spec_end ()
     483             :       };
     484             : 
     485         208 :       res = TALER_MHD_parse_json_data (mc->rc->connection,
     486             :                                        value,
     487             :                                        ispec);
     488         208 :       if (GNUNET_YES != res)
     489             :       {
     490           0 :         GNUNET_break_op (0);
     491           0 :         mc->phase = (GNUNET_NO == res)
     492             :           ? MELT_PHASE_RETURN_YES
     493           0 :           : MELT_PHASE_RETURN_NO;
     494           0 :         return;
     495             :       }
     496             :     }
     497             :   }
     498             : 
     499             :   /* Calculate the hash over the blinded coin envelopes */
     500         152 :   for (size_t k = 0; k<TALER_CNC_KAPPA; k++)
     501             :   {
     502         114 :     mc->request.planchets[k] =
     503         114 :       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          38 :     ctx = GNUNET_CRYPTO_hash_context_start ();
     515          38 :     GNUNET_assert (NULL != ctx);
     516             : 
     517         152 :     json_array_foreach (j_coin_evs, kappa, j_kappa_planchets)
     518             :     {
     519             :       json_t *j_cev;
     520             :       size_t idx;
     521             : 
     522         114 :       if (mc->request.refresh.num_coins != json_array_size (j_kappa_planchets))
     523             :       {
     524           0 :         GNUNET_break_op (0);
     525           0 :         SET_ERROR_WITH_DETAIL (mc,
     526             :                                MELT_ERROR_REQUEST_PARAMETER_MALFORMED,
     527             :                                request_parameter_malformed,
     528             :                                "coin_evs[] size");
     529           0 :         return;
     530             :       }
     531             : 
     532         738 :       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         624 :           TALER_JSON_spec_blinded_planchet (NULL,
     538         624 :                                             &mc->request.planchets[kappa][idx]),
     539         624 :           GNUNET_JSON_spec_end ()
     540             :         };
     541             : 
     542         624 :         res = TALER_MHD_parse_json_data (mc->rc->connection,
     543             :                                          j_cev,
     544             :                                          kspec);
     545         624 :         if (GNUNET_OK != res)
     546             :         {
     547           0 :           GNUNET_break_op (0);
     548           0 :           mc->phase = (GNUNET_NO == res)
     549             :             ? MELT_PHASE_RETURN_YES
     550           0 :             : MELT_PHASE_RETURN_NO;
     551           0 :           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        1872 :         for (size_t k = 0; k <= kappa; k++)
     557             :         {
     558        1248 :           size_t max = (k == kappa)
     559             :                           ? idx
     560        1248 :                           : mc->request.refresh.num_coins;
     561        7470 :           for (size_t i = 0; i < max; i++)
     562             :           {
     563        6222 :             if (0 ==
     564        6222 :                 TALER_blinded_planchet_cmp (
     565        6222 :                   &mc->request.planchets[kappa][idx],
     566        6222 :                   &mc->request.planchets[k][i]))
     567             :             {
     568           0 :               GNUNET_break_op (0);
     569           0 :               GNUNET_JSON_parse_free (kspec);
     570           0 :               SET_ERROR (mc,
     571             :                          MELT_ERROR_IDEMPOTENT_PLANCHET);
     572           0 :               return;
     573             :             }
     574             :           }
     575             :         }
     576             :       }
     577             :       /* Save the hash of the batch of planchets for index kappa */
     578         114 :       TALER_wallet_blinded_planchets_hash (
     579             :         mc->request.refresh.num_coins,
     580         114 :         mc->request.planchets[kappa],
     581         114 :         mc->request.denoms_h,
     582             :         &mc->request.kappa_planchets_h.tuple[kappa]);
     583         114 :       GNUNET_CRYPTO_hash_context_read (
     584             :         ctx,
     585         114 :         &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          38 :     GNUNET_CRYPTO_hash_context_finish (
     591             :       ctx,
     592             :       &mc->request.refresh.planchets_h.hash);
     593             :   }
     594          38 :   mc->ksh = TEH_keys_get_state ();
     595          38 :   if (NULL == mc->ksh)
     596             :   {
     597           0 :     GNUNET_break (0);
     598           0 :     SET_ERROR (mc,
     599             :                MELT_ERROR_KEYS_MISSING);
     600           0 :     return;
     601             :   }
     602          38 :   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         246 : 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         246 :   *pdk = NULL;
     626         246 :   GNUNET_assert (NULL != mc->ksh);
     627         246 :   dk = TEH_keys_denomination_by_hash_from_state (
     628         246 :     mc->ksh,
     629             :     denom_h,
     630             :     NULL,
     631             :     NULL);
     632         246 :   if (NULL == dk)
     633             :   {
     634           0 :     SET_ERROR_WITH_DETAIL (mc,
     635             :                            MELT_ERROR_DENOMINATION_KEY_UNKNOWN,
     636             :                            denom_h,
     637             :                            *denom_h);
     638           0 :     return GNUNET_SYSERR;
     639             :   }
     640         246 :   *pdk = dk;
     641             : 
     642         246 :   if (GNUNET_TIME_absolute_is_past (
     643             :         dk->meta.expire_withdraw.abs_time))
     644             :   {
     645           0 :     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           0 :     return GNUNET_NO;
     655             :   }
     656             : 
     657         246 :   if (GNUNET_TIME_absolute_is_future (
     658             :         dk->meta.start.abs_time))
     659             :   {
     660           0 :     GNUNET_break_op (0);
     661           0 :     SET_ERROR_WITH_DETAIL (mc,
     662             :                            MELT_ERROR_DENOMINATION_VALIDITY_IN_FUTURE,
     663             :                            denom_h,
     664             :                            *denom_h);
     665           0 :     return GNUNET_SYSERR;
     666             :   }
     667             : 
     668         246 :   if (dk->recoup_possible)
     669             :   {
     670           0 :     SET_ERROR (mc,
     671             :                MELT_ERROR_DENOMINATION_REVOKED);
     672           0 :     return GNUNET_SYSERR;
     673             :   }
     674             : 
     675             :   /* In case of age melt, make sure that the denomitation supports age restriction */
     676         246 :   if (! (mc->request.refresh.coin.no_age_commitment) &&
     677          80 :       (0 == dk->denom_pub.age_mask.bits))
     678             :   {
     679           0 :     GNUNET_break_op (0);
     680           0 :     SET_ERROR_WITH_DETAIL (mc,
     681             :                            MELT_ERROR_AGE_RESTRICTION_NOT_SUPPORTED_BY_DENOMINATION,
     682             :                            denom_h,
     683             :                            *denom_h);
     684           0 :     return GNUNET_SYSERR;
     685             :   }
     686         246 :   if ((mc->request.refresh.coin.no_age_commitment) &&
     687         166 :       (0 != dk->denom_pub.age_mask.bits))
     688             :   {
     689           0 :     GNUNET_break_op (0);
     690           0 :     SET_ERROR (mc,
     691             :                MELT_ERROR_AGE_RESTRICTION_COMMITMENT_INVALID);
     692           0 :     return GNUNET_SYSERR;
     693             :   }
     694             : 
     695         246 :   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          38 : phase_check_keys (
     709             :   struct MeltContext *mc)
     710          38 : {
     711          38 :   bool is_cs_denom[mc->request.refresh.num_coins];
     712             : 
     713          38 :   memset (is_cs_denom,
     714             :           0,
     715             :           sizeof(is_cs_denom));
     716             : 
     717          38 :   mc->request.refresh.denom_serials =
     718          38 :     GNUNET_new_array (mc->request.refresh.num_coins,
     719             :                       uint64_t);
     720          38 :   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         246 :   for (size_t i = 0; i < mc->request.refresh.num_coins; i++)
     726             :   {
     727             :     struct TEH_DenominationKey *dk;
     728             : 
     729         208 :     if (GNUNET_OK !=
     730         208 :         find_denomination (
     731             :           mc,
     732         208 :           &mc->request.denoms_h[i],
     733             :           &dk))
     734           0 :       return;
     735             : 
     736         208 :     if (GNUNET_CRYPTO_BSA_CS ==
     737         208 :         dk->denom_pub.bsign_pub_key->cipher)
     738             :     {
     739          60 :       if (mc->request.refresh.no_blinding_seed)
     740             :       {
     741           0 :         GNUNET_break_op (0);
     742           0 :         SET_ERROR (mc,
     743             :                    MELT_ERROR_BLINDING_SEED_REQUIRED);
     744           0 :         return;
     745             :       }
     746          60 :       mc->request.refresh.num_cs_r_values++;
     747          60 :       is_cs_denom[i] = true;
     748             :     }
     749             :     /* Ensure the ciphers from the planchets match the denominations'. */
     750             :     {
     751         832 :       for (uint8_t k = 0; k < TALER_CNC_KAPPA; k++)
     752             :       {
     753         624 :         if (dk->denom_pub.bsign_pub_key->cipher !=
     754         624 :             mc->request.planchets[k][i].blinded_message->cipher)
     755             :         {
     756           0 :           GNUNET_break_op (0);
     757           0 :           SET_ERROR (mc,
     758             :                      MELT_ERROR_COIN_CIPHER_MISMATCH);
     759           0 :           return;
     760             :         }
     761             :       }
     762             :     }
     763             :     /* Accumulate the values */
     764         208 :     if (0 > TALER_amount_add (&mc->request.amount,
     765         208 :                               &mc->request.amount,
     766         208 :                               &dk->meta.value))
     767             :     {
     768           0 :       GNUNET_break_op (0);
     769           0 :       SET_ERROR (mc,
     770             :                  MELT_ERROR_AMOUNT_OVERFLOW);
     771           0 :       return;
     772             :     }
     773             :     /* Accumulate the withdraw fees for the fresh coins */
     774         208 :     if (0 > TALER_amount_add (&mc->request.amount,
     775         208 :                               &mc->request.amount,
     776         208 :                               &dk->meta.fees.withdraw))
     777             :     {
     778           0 :       GNUNET_break_op (0);
     779           0 :       SET_ERROR (mc,
     780             :                  MELT_ERROR_AMOUNT_PLUS_FEE_OVERFLOW);
     781           0 :       return;
     782             :     }
     783         208 :     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          38 :     if (0 > TALER_amount_add (&amount_with_fee,
     794          38 :                               &mc->request.amount,
     795          38 :                               &mc->melted_coin_denom->meta.fees.refresh))
     796             :     {
     797           0 :       GNUNET_break_op (0);
     798           0 :       SET_ERROR (mc,
     799             :                  MELT_ERROR_AMOUNT_PLUS_FEE_OVERFLOW);
     800           0 :       return;
     801             :     }
     802             : 
     803          38 :     if (0 != TALER_amount_cmp (&amount_with_fee,
     804          38 :                                &mc->request.refresh.amount_with_fee))
     805             :     {
     806           0 :       GNUNET_break_op (0);
     807           0 :       SET_ERROR (mc,
     808             :                  MELT_ERROR_AMOUNT_WITH_FEE_INCORRECT);
     809           0 :       return;
     810             :     }
     811             :   }
     812             : 
     813             :   /* Save the indices of CS denominations */
     814          38 :   if (0 < mc->request.refresh.num_cs_r_values)
     815             :   {
     816          15 :     size_t j = 0;
     817             : 
     818          15 :     mc->request.cs_indices = GNUNET_new_array (
     819             :       mc->request.refresh.num_cs_r_values,
     820             :       uint32_t);
     821             : 
     822          75 :     for (size_t i = 0; i < mc->request.refresh.num_coins; i++)
     823             :     {
     824          60 :       if (is_cs_denom[i])
     825          60 :         mc->request.cs_indices[j++] = i;
     826             :     }
     827             :   }
     828          38 :   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          38 : phase_check_coin_signature (
     839             :   struct MeltContext *mc)
     840             : {
     841             :   /* We can now compute the commitment */
     842             :   {
     843          38 :     struct TALER_KappaHashBlindedPlanchetsP k_bps_h = {0};
     844             : 
     845         152 :     for (uint8_t k = 0; k < TALER_CNC_KAPPA; k++)
     846         114 :       TALER_wallet_blinded_planchets_hash (
     847             :         mc->request.refresh.num_coins,
     848         114 :         mc->request.planchets[k],
     849         114 :         mc->request.denoms_h,
     850         114 :         &k_bps_h.tuple[k]);
     851             : 
     852          38 :     TALER_refresh_get_commitment_v27 (
     853             :       &mc->request.refresh.rc,
     854          38 :       &mc->request.refresh.refresh_seed,
     855          38 :       mc->request.no_blinding_seed
     856             :            ? NULL
     857             :            : &mc->request.refresh.blinding_seed,
     858             :       &k_bps_h,
     859          38 :       &mc->request.refresh.coin.coin_pub,
     860          38 :       &mc->request.refresh.amount_with_fee);
     861             :   }
     862             : 
     863             : 
     864          38 :   TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
     865          38 :   if (GNUNET_OK !=
     866          38 :       TALER_wallet_melt_verify (
     867          38 :         &mc->request.refresh.amount_with_fee,
     868          38 :         &mc->melted_coin_denom->meta.fees.refresh,
     869          38 :         &mc->request.refresh.rc,
     870          38 :         &mc->request.refresh.coin.denom_pub_hash,
     871          38 :         &mc->request.refresh.coin.h_age_commitment,
     872          38 :         &mc->request.refresh.coin.coin_pub,
     873          38 :         &mc->request.refresh.coin_sig))
     874             :   {
     875           0 :     GNUNET_break_op (0);
     876           0 :     SET_ERROR (mc,
     877             :                MELT_ERROR_COIN_SIGNATURE_INVALID);
     878           0 :     return;
     879             :   }
     880             : 
     881          38 :   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          38 : phase_check_melt_valid (struct MeltContext *mc)
     895             : {
     896          38 :   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          38 :   if (GNUNET_SYSERR ==
     904          38 :       find_denomination (mc,
     905          38 :                          &mc->request.refresh.coin.denom_pub_hash,
     906             :                          &mc->melted_coin_denom))
     907           0 :     return;
     908             : 
     909          38 :   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          38 :   if (0 <
     915          38 :       TALER_amount_cmp (&mc->melted_coin_denom->meta.fees.refresh,
     916          38 :                         &mc->request.refresh.amount_with_fee))
     917             :   {
     918           0 :     GNUNET_break_op (0);
     919           0 :     SET_ERROR (mc,
     920             :                MELT_ERROR_FEES_EXCEED_CONTRIBUTION);
     921           0 :     return;
     922             :   }
     923             : 
     924          38 :   if (GNUNET_OK !=
     925          38 :       TALER_test_coin_valid (&mc->request.refresh.coin,
     926          38 :                              &mc->melted_coin_denom->denom_pub))
     927             :   {
     928           0 :     GNUNET_break_op (0);
     929           0 :     SET_ERROR (mc,
     930             :                MELT_ERROR_DENOMINATION_SIGNATURE_INVALID);
     931           0 :     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          38 :   mc->phase = current_phase;
     940          38 :   mc->error.code = MELT_ERROR_NONE;
     941             : 
     942          38 :   if (GNUNET_TIME_absolute_is_past (
     943          38 :         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           0 :     qs = TEH_plugin->get_coin_denomination (
     955           0 :       TEH_plugin->cls,
     956           0 :       &mc->request.refresh.coin.coin_pub,
     957             :       &mc->known_coin_id,
     958             :       &denom_hash);
     959           0 :     if (0 > qs)
     960             :     {
     961             :       /* There is no good reason for a serialization failure here: */
     962           0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
     963           0 :       SET_ERROR (mc,
     964             :                  MELT_ERROR_DB_FETCH_FAILED);
     965           0 :       return;
     966             :     }
     967           0 :     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           0 :       GNUNET_assert (MELT_ERROR_DENOMINATION_EXPIRED ==
     972             :                      mc->error.code);
     973           0 :       GNUNET_assert (MELT_PHASE_GENERATE_REPLY_ERROR ==
     974             :                      mc->phase);
     975           0 :       return;
     976             :     }
     977             :     /* sanity check */
     978           0 :     if (0 !=
     979           0 :         GNUNET_memcmp (&denom_hash,
     980             :                        &mc->request.refresh.coin.denom_pub_hash))
     981             :     {
     982           0 :       GNUNET_break_op (0);
     983           0 :       SET_ERROR_WITH_DETAIL (mc,
     984             :                              MELT_COIN_CONFLICTING_DENOMINATION_KEY,
     985             :                              denom_h,
     986             :                              denom_hash);
     987           0 :       return;
     988             :     }
     989             :     /* Minor optimization: no need to run the
     990             :        "ensure_coin_known" part of the transaction */
     991           0 :     mc->coin_is_dirty = true;
     992             :     /* check later that zombie is satisfied */
     993           0 :     mc->zombie_required = true;
     994             :   }
     995          38 :   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          38 : phase_prepare_transaction (
    1007             :   struct MeltContext *mc)
    1008             : {
    1009             :   mc->request.refresh.denom_sigs
    1010          38 :     = GNUNET_new_array (
    1011             :         mc->request.refresh.num_coins,
    1012             :         struct TALER_BlindedDenominationSignature);
    1013          38 :   mc->request.refresh.noreveal_index =
    1014          38 :     GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_STRONG,
    1015             :                               TALER_CNC_KAPPA);
    1016             : 
    1017             :   /* Choose and sign the coins */
    1018          38 :   {
    1019          38 :     struct TEH_CoinSignData csds[mc->request.refresh.num_coins];
    1020             :     enum TALER_ErrorCode ec_denomination_sign;
    1021          38 :     size_t noreveal_idx = mc->request.refresh.noreveal_index;
    1022             : 
    1023          38 :     memset (csds,
    1024             :             0,
    1025             :             sizeof(csds));
    1026             : 
    1027             :     /* Pick the chosen blinded coins */
    1028         246 :     for (size_t i = 0; i<mc->request.refresh.num_coins; i++)
    1029             :     {
    1030         208 :       csds[i].bp = &mc->request.planchets[noreveal_idx][i];
    1031         208 :       csds[i].h_denom_pub = &mc->request.denoms_h[i];
    1032             :     }
    1033             : 
    1034          38 :     ec_denomination_sign = TEH_keys_denomination_batch_sign (
    1035          38 :       mc->request.refresh.num_coins,
    1036             :       csds,
    1037             :       true, /* for melt */
    1038             :       mc->request.refresh.denom_sigs);
    1039          38 :     if (TALER_EC_NONE != ec_denomination_sign)
    1040             :     {
    1041           0 :       GNUNET_break (0);
    1042           0 :       SET_ERROR_WITH_FIELD (mc,
    1043             :                             MELT_ERROR_DENOMINATION_SIGN,
    1044             :                             ec_denomination_sign);
    1045           0 :       return;
    1046             :     }
    1047             : 
    1048             :     /* Save the hash of chosen planchets */
    1049          38 :     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          38 :     if (0 <  mc->request.refresh.num_cs_r_values)
    1059          15 :     {
    1060          15 :       size_t num_cs_r_values = mc->request.refresh.num_cs_r_values;
    1061          15 :       struct TEH_CsDeriveData cdds[num_cs_r_values];
    1062          15 :       struct GNUNET_CRYPTO_CsSessionNonce nonces[num_cs_r_values];
    1063             : 
    1064          15 :       memset (nonces, 0, sizeof(nonces));
    1065             :       mc->request.refresh.cs_r_values
    1066          15 :         = GNUNET_new_array (
    1067             :             num_cs_r_values,
    1068             :             struct GNUNET_CRYPTO_CSPublicRPairP);
    1069          15 :       mc->request.refresh.cs_r_choices = 0;
    1070             : 
    1071          15 :       GNUNET_assert (! mc->request.refresh.no_blinding_seed);
    1072          15 :       TALER_cs_derive_nonces_from_seed (
    1073          15 :         &mc->request.refresh.blinding_seed,
    1074             :         true, /* for melt */
    1075             :         num_cs_r_values,
    1076          15 :         mc->request.cs_indices,
    1077             :         nonces);
    1078             : 
    1079          75 :       for (size_t i = 0; i < num_cs_r_values; i++)
    1080             :       {
    1081          60 :         size_t idx = mc->request.cs_indices[i];
    1082             : 
    1083          60 :         GNUNET_assert (idx < mc->request.refresh.num_coins);
    1084          60 :         cdds[i].h_denom_pub = &mc->request.denoms_h[idx];
    1085          60 :         cdds[i].nonce = &nonces[i];
    1086             :       }
    1087             : 
    1088             :       /**
    1089             :        * Let the crypto helper generate the R-values and
    1090             :        * make the choices
    1091             :        */
    1092          15 :       if (TALER_EC_NONE !=
    1093          15 :           TEH_keys_denomination_cs_batch_r_pub_simple (
    1094          15 :             mc->request.refresh.num_cs_r_values,
    1095             :             cdds,
    1096             :             true, /* for melt */
    1097             :             mc->request.refresh.cs_r_values))
    1098             :       {
    1099           0 :         GNUNET_break (0);
    1100           0 :         SET_ERROR (mc,
    1101             :                    MELT_ERROR_CRYPTO_HELPER);
    1102           0 :         return;
    1103             :       }
    1104             : 
    1105             :       /* Now save the choices for the selected bits */
    1106          75 :       for (size_t i = 0; i < num_cs_r_values; i++)
    1107             :       {
    1108          60 :         size_t idx = mc->request.cs_indices[i];
    1109             : 
    1110          60 :         struct TALER_BlindedDenominationSignature *sig =
    1111          60 :           &mc->request.refresh.denom_sigs[idx];
    1112          60 :         uint8_t bit = sig->blinded_sig->details.blinded_cs_answer.b;
    1113             : 
    1114          60 :         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          38 :   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          24 : 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          48 :   db_obj = mc->request.is_idempotent
    1139             :     ? &mc->request.refresh_idem
    1140          24 :     : &mc->request.refresh;
    1141             :   ec_confirmation_sign =
    1142          24 :     TALER_exchange_online_melt_confirmation_sign (
    1143             :       &TEH_keys_exchange_sign_,
    1144          24 :       &db_obj->rc,
    1145             :       db_obj->noreveal_index,
    1146             :       &pub,
    1147             :       &sig);
    1148          24 :   if (TALER_EC_NONE != ec_confirmation_sign)
    1149             :   {
    1150           0 :     SET_ERROR_WITH_FIELD (mc,
    1151             :                           MELT_ERROR_CONFIRMATION_SIGN,
    1152             :                           ec_confirmation_sign);
    1153           0 :     return;
    1154             :   }
    1155             : 
    1156          48 :   finish_loop (mc,
    1157          48 :                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          13 : melt_is_idempotent (
    1179             :   struct MeltContext *mc)
    1180             : {
    1181             :   enum GNUNET_DB_QueryStatus qs;
    1182             : 
    1183          13 :   qs = TEH_plugin->get_refresh (
    1184          13 :     TEH_plugin->cls,
    1185          13 :     &mc->request.refresh.rc,
    1186             :     &mc->request.refresh_idem);
    1187          13 :   if (0 > qs)
    1188             :   {
    1189             :     /* FIXME: soft error not handled correctly! */
    1190           0 :     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    1191           0 :     if (GNUNET_DB_STATUS_HARD_ERROR == qs)
    1192           0 :       SET_ERROR_WITH_DETAIL (mc,
    1193             :                              MELT_ERROR_DB_FETCH_FAILED,
    1194             :                              db_fetch_context,
    1195             :                              "get_refresh");
    1196           0 :     return true; /* Well, kind-of. */
    1197             :   }
    1198          13 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    1199          13 :     return false;
    1200             : 
    1201           0 :   mc->request.is_idempotent = true;
    1202           0 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    1203             :               "request is idempotent\n");
    1204             : 
    1205             :   /* Generate idempotent reply */
    1206           0 :   TEH_METRICS_num_requests[TEH_MT_REQUEST_IDEMPOTENT_MELT]++;
    1207           0 :   mc->phase = MELT_PHASE_GENERATE_REPLY_SUCCESS;
    1208           0 :   mc->error.code = MELT_ERROR_NONE;
    1209           0 :   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          13 : phase_generate_reply_error (
    1222             :   struct MeltContext *mc)
    1223             : {
    1224          13 :   GNUNET_assert (MELT_PHASE_GENERATE_REPLY_ERROR == mc->phase);
    1225          13 :   GNUNET_assert (MELT_ERROR_NONE != mc->error.code);
    1226             : 
    1227          26 :   if (IDEMPOTENCY_CHECK_REQUIRED (mc->error.code) &&
    1228          13 :       melt_is_idempotent (mc))
    1229             :   {
    1230           0 :     return;
    1231             :   }
    1232             : 
    1233          13 :   switch (mc->error.code)
    1234             :   {
    1235           0 :   case MELT_ERROR_NONE:
    1236           0 :     break;
    1237           0 :   case MELT_ERROR_REQUEST_PARAMETER_MALFORMED:
    1238           0 :     finish_loop (mc,
    1239             :                  TALER_MHD_reply_with_error (
    1240           0 :                    mc->rc->connection,
    1241             :                    MHD_HTTP_BAD_REQUEST,
    1242             :                    TALER_EC_GENERIC_PARAMETER_MALFORMED,
    1243             :                    mc->error.details.request_parameter_malformed));
    1244           0 :     return;
    1245           0 :   case MELT_ERROR_KEYS_MISSING:
    1246           0 :     finish_loop (mc,
    1247             :                  TALER_MHD_reply_with_error (
    1248           0 :                    mc->rc->connection,
    1249             :                    MHD_HTTP_INTERNAL_SERVER_ERROR,
    1250             :                    TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING,
    1251             :                    NULL));
    1252           0 :     return;
    1253           0 :   case MELT_ERROR_DB_FETCH_FAILED:
    1254           0 :     finish_loop (mc,
    1255             :                  TALER_MHD_reply_with_error (
    1256           0 :                    mc->rc->connection,
    1257             :                    MHD_HTTP_INTERNAL_SERVER_ERROR,
    1258             :                    TALER_EC_GENERIC_DB_FETCH_FAILED,
    1259             :                    mc->error.details.db_fetch_context));
    1260           0 :     return;
    1261           0 :   case MELT_ERROR_DB_INVARIANT_FAILURE:
    1262           0 :     finish_loop (mc,
    1263             :                  TALER_MHD_reply_with_error (
    1264           0 :                    mc->rc->connection,
    1265             :                    MHD_HTTP_INTERNAL_SERVER_ERROR,
    1266             :                    TALER_EC_GENERIC_DB_INVARIANT_FAILURE,
    1267             :                    NULL));
    1268           0 :     return;
    1269           0 :   case MELT_ERROR_DB_PREFLIGHT_FAILURE:
    1270           0 :     finish_loop (mc,
    1271             :                  TALER_MHD_reply_with_error (
    1272           0 :                    mc->rc->connection,
    1273             :                    MHD_HTTP_INTERNAL_SERVER_ERROR,
    1274             :                    TALER_EC_GENERIC_DB_COMMIT_FAILED,
    1275             :                    "make_coin_known"));
    1276           0 :     return;
    1277           0 :   case MELT_ERROR_DB_MAKE_COIN_KNOW_FAILURE:
    1278           0 :     finish_loop (mc,
    1279             :                  TALER_MHD_reply_with_error (
    1280           0 :                    mc->rc->connection,
    1281             :                    MHD_HTTP_INTERNAL_SERVER_ERROR,
    1282             :                    TALER_EC_GENERIC_DB_START_FAILED,
    1283             :                    "preflight failure"));
    1284           0 :     return;
    1285           0 :   case MELT_ERROR_COIN_UNKNOWN:
    1286           0 :     finish_loop (mc,
    1287             :                  TALER_MHD_reply_with_ec (
    1288           0 :                    mc->rc->connection,
    1289             :                    TALER_EC_EXCHANGE_GENERIC_COIN_UNKNOWN,
    1290             :                    NULL));
    1291           0 :     return;
    1292           0 :   case MELT_COIN_CONFLICTING_DENOMINATION_KEY:
    1293           0 :     finish_loop (mc,
    1294             :                  TALER_MHD_reply_with_ec (
    1295           0 :                    mc->rc->connection,
    1296             :                    TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY,
    1297           0 :                    TALER_B2S (&mc->error.details.denom_h)));
    1298           0 :     return;
    1299           0 :   case MELT_ERROR_COIN_EXPIRED_NO_ZOMBIE:
    1300           0 :     finish_loop (mc,
    1301             :                  TALER_MHD_reply_with_error (
    1302           0 :                    mc->rc->connection,
    1303             :                    MHD_HTTP_BAD_REQUEST,
    1304             :                    TALER_EC_EXCHANGE_MELT_COIN_EXPIRED_NO_ZOMBIE,
    1305             :                    NULL));
    1306           0 :     return;
    1307           0 :   case MELT_ERROR_DENOMINATION_SIGN:
    1308           0 :     finish_loop (mc,
    1309             :                  TALER_MHD_reply_with_ec (
    1310           0 :                    mc->rc->connection,
    1311             :                    mc->error.details.ec_denomination_sign,
    1312             :                    NULL));
    1313           0 :     return;
    1314           0 :   case MELT_ERROR_DENOMINATION_SIGNATURE_INVALID:
    1315           0 :     finish_loop (mc,
    1316           0 :                  TALER_MHD_reply_with_error (mc->rc->connection,
    1317             :                                              MHD_HTTP_FORBIDDEN,
    1318             :                                              TALER_EC_EXCHANGE_DENOMINATION_SIGNATURE_INVALID,
    1319             :                                              NULL));
    1320           0 :     return;
    1321           0 :   case MELT_ERROR_DENOMINATION_KEY_UNKNOWN:
    1322           0 :     GNUNET_break_op (0);
    1323           0 :     finish_loop (mc,
    1324             :                  TEH_RESPONSE_reply_unknown_denom_pub_hash (
    1325           0 :                    mc->rc->connection,
    1326           0 :                    &mc->error.details.denom_h));
    1327           0 :     return;
    1328           0 :   case MELT_ERROR_DENOMINATION_EXPIRED:
    1329           0 :     GNUNET_break_op (0);
    1330           0 :     finish_loop (mc,
    1331             :                  TEH_RESPONSE_reply_expired_denom_pub_hash (
    1332           0 :                    mc->rc->connection,
    1333           0 :                    &mc->error.details.denom_h,
    1334             :                    TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED,
    1335             :                    "MELT"));
    1336           0 :     return;
    1337           0 :   case MELT_ERROR_DENOMINATION_VALIDITY_IN_FUTURE:
    1338           0 :     finish_loop (mc,
    1339             :                  TEH_RESPONSE_reply_expired_denom_pub_hash (
    1340           0 :                    mc->rc->connection,
    1341           0 :                    &mc->error.details.denom_h,
    1342             :                    TALER_EC_EXCHANGE_GENERIC_DENOMINATION_VALIDITY_IN_FUTURE,
    1343             :                    "MELT"));
    1344           0 :     return;
    1345           0 :   case MELT_ERROR_DENOMINATION_REVOKED:
    1346           0 :     GNUNET_break_op (0);
    1347           0 :     finish_loop (mc,
    1348             :                  TALER_MHD_reply_with_ec (
    1349           0 :                    mc->rc->connection,
    1350             :                    TALER_EC_EXCHANGE_GENERIC_DENOMINATION_REVOKED,
    1351             :                    NULL));
    1352           0 :     return;
    1353           0 :   case MELT_ERROR_COIN_CIPHER_MISMATCH:
    1354           0 :     finish_loop (mc,
    1355             :                  TALER_MHD_reply_with_ec (
    1356           0 :                    mc->rc->connection,
    1357             :                    TALER_EC_EXCHANGE_GENERIC_CIPHER_MISMATCH,
    1358             :                    NULL));
    1359           0 :     return;
    1360           0 :   case MELT_ERROR_BLINDING_SEED_REQUIRED:
    1361           0 :     finish_loop (mc,
    1362             :                  TALER_MHD_reply_with_ec (
    1363           0 :                    mc->rc->connection,
    1364             :                    TALER_EC_GENERIC_PARAMETER_MISSING,
    1365             :                    "blinding_seed"));
    1366           0 :     return;
    1367           0 :   case MELT_ERROR_CRYPTO_HELPER:
    1368           0 :     finish_loop (mc,
    1369             :                  TALER_MHD_reply_with_ec (
    1370           0 :                    mc->rc->connection,
    1371             :                    TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
    1372             :                    NULL));
    1373           0 :     return;
    1374           0 :   case MELT_ERROR_AGE_RESTRICTION_NOT_SUPPORTED_BY_DENOMINATION:
    1375             :     {
    1376             :       char msg[256];
    1377             : 
    1378           0 :       GNUNET_snprintf (msg,
    1379             :                        sizeof(msg),
    1380             :                        "denomination %s does not support age restriction",
    1381           0 :                        GNUNET_h2s (&mc->error.details.denom_h.hash));
    1382           0 :       finish_loop (mc,
    1383             :                    TALER_MHD_reply_with_ec (
    1384           0 :                      mc->rc->connection,
    1385             :                      TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN,
    1386             :                      msg));
    1387           0 :       return;
    1388             :     }
    1389           0 :   case  MELT_ERROR_AGE_RESTRICTION_COMMITMENT_INVALID:
    1390           0 :     finish_loop (mc,
    1391             :                  TALER_MHD_reply_with_ec (
    1392           0 :                    mc->rc->connection,
    1393             :                    TALER_EC_EXCHANGE_REFRESHES_REVEAL_AGE_RESTRICTION_COMMITMENT_INVALID,
    1394             :                    "old_age_commitment_h"));
    1395           0 :     return;
    1396           0 :   case MELT_ERROR_AMOUNT_OVERFLOW:
    1397           0 :     finish_loop (mc,
    1398             :                  TALER_MHD_reply_with_error (
    1399           0 :                    mc->rc->connection,
    1400             :                    MHD_HTTP_BAD_REQUEST,
    1401             :                    TALER_EC_EXCHANGE_REFRESHES_REVEAL_COST_CALCULATION_OVERFLOW,
    1402             :                    "amount"));
    1403           0 :     return;
    1404           0 :   case MELT_ERROR_AMOUNT_PLUS_FEE_OVERFLOW:
    1405           0 :     finish_loop (mc,
    1406             :                  TALER_MHD_reply_with_error (
    1407           0 :                    mc->rc->connection,
    1408             :                    MHD_HTTP_INTERNAL_SERVER_ERROR,
    1409             :                    TALER_EC_EXCHANGE_REFRESHES_REVEAL_COST_CALCULATION_OVERFLOW,
    1410             :                    "amount+fee"));
    1411           0 :     return;
    1412           0 :   case MELT_ERROR_FEES_EXCEED_CONTRIBUTION:
    1413           0 :     finish_loop (mc,
    1414           0 :                  TALER_MHD_reply_with_error (mc->rc->connection,
    1415             :                                              MHD_HTTP_BAD_REQUEST,
    1416             :                                              TALER_EC_EXCHANGE_MELT_FEES_EXCEED_CONTRIBUTION,
    1417             :                                              NULL));
    1418           0 :     return;
    1419           0 :   case MELT_ERROR_AMOUNT_WITH_FEE_INCORRECT:
    1420           0 :     finish_loop (mc,
    1421             :                  TALER_MHD_reply_with_error (
    1422           0 :                    mc->rc->connection,
    1423             :                    MHD_HTTP_BAD_REQUEST,
    1424             :                    TALER_EC_EXCHANGE_REFRESHES_REVEAL_COST_CALCULATION_OVERFLOW,
    1425             :                    "value_with_fee incorrect"));
    1426           0 :     return;
    1427           0 :   case MELT_ERROR_CONFIRMATION_SIGN:
    1428           0 :     finish_loop (mc,
    1429             :                  TALER_MHD_reply_with_ec (
    1430           0 :                    mc->rc->connection,
    1431             :                    mc->error.details.ec_confirmation_sign,
    1432             :                    NULL));
    1433           0 :     return;
    1434          13 :   case MELT_ERROR_INSUFFICIENT_FUNDS:
    1435          13 :     finish_loop (mc,
    1436             :                  TEH_RESPONSE_reply_coin_insufficient_funds (
    1437          13 :                    mc->rc->connection,
    1438             :                    TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS,
    1439          13 :                    &mc->request.refresh.coin.denom_pub_hash,
    1440          13 :                    &mc->request.refresh.coin.coin_pub));
    1441          13 :     return;
    1442           0 :   case MELT_ERROR_IDEMPOTENT_PLANCHET:
    1443           0 :     finish_loop (mc,
    1444             :                  TALER_MHD_reply_with_error (
    1445           0 :                    mc->rc->connection,
    1446             :                    MHD_HTTP_BAD_REQUEST,
    1447             :                    TALER_EC_GENERIC_PARAMETER_MALFORMED,   /* FIXME: new error! */
    1448             :                    "idempotent planchet"));
    1449           0 :     return;
    1450           0 :   case MELT_ERROR_NONCE_RESUSE:
    1451           0 :     finish_loop (mc,
    1452             :                  TALER_MHD_reply_with_error (
    1453           0 :                    mc->rc->connection,
    1454             :                    MHD_HTTP_BAD_REQUEST,
    1455             :                    TALER_EC_GENERIC_PARAMETER_MALFORMED, /* FIXME: new error */
    1456             :                    "nonce reuse"));
    1457           0 :     return;
    1458           0 :   case MELT_ERROR_COIN_SIGNATURE_INVALID:
    1459           0 :     finish_loop (mc,
    1460             :                  TALER_MHD_reply_with_ec (
    1461           0 :                    mc->rc->connection,
    1462             :                    TALER_EC_EXCHANGE_MELT_COIN_SIGNATURE_INVALID,
    1463             :                    NULL));
    1464           0 :     return;
    1465             :   }
    1466           0 :   GNUNET_break (0);
    1467           0 :   finish_loop (mc,
    1468             :                TALER_MHD_reply_with_error (
    1469           0 :                  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          37 : melt_transaction (
    1492             :   void *cls,
    1493             :   struct MHD_Connection *connection,
    1494             :   MHD_RESULT *mhd_ret)
    1495             : {
    1496          37 :   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          37 :   qs = TEH_plugin->do_refresh (TEH_plugin->cls,
    1505             :                                &mc->request.refresh,
    1506          37 :                                &mc->now,
    1507             :                                &found,
    1508             :                                &noreveal_index,
    1509             :                                &mc->zombie_required,
    1510             :                                &nonce_reuse,
    1511             :                                &balance_ok,
    1512             :                                &insufficient_funds);
    1513          37 :   if (0 > qs)
    1514             :   {
    1515           0 :     if (GNUNET_DB_STATUS_HARD_ERROR == qs)
    1516           0 :       SET_ERROR_WITH_DETAIL (mc,
    1517             :                              MELT_ERROR_DB_FETCH_FAILED,
    1518             :                              db_fetch_context,
    1519             :                              "do_refresh");
    1520           0 :     return qs;
    1521             :   }
    1522          37 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    1523             :   {
    1524           0 :     GNUNET_break_op (0);
    1525           0 :     SET_ERROR (mc,
    1526             :                MELT_ERROR_COIN_UNKNOWN);
    1527           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    1528             :   }
    1529          37 :   if (found)
    1530             :   {
    1531             :     /**
    1532             :      * This request is idempotent, set the nonreveal_index
    1533             :      * to the previous one and reply success.
    1534             :      */
    1535           8 :     mc->request.refresh.noreveal_index = noreveal_index;
    1536           8 :     mc->phase = MELT_PHASE_GENERATE_REPLY_SUCCESS;
    1537           8 :     mc->error.code = MELT_ERROR_NONE;
    1538           8 :     return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
    1539             :   }
    1540          29 :   if (nonce_reuse)
    1541             :   {
    1542           0 :     GNUNET_break_op (0);
    1543           0 :     SET_ERROR (mc,
    1544             :                MELT_ERROR_NONCE_RESUSE);
    1545           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    1546             :   }
    1547          29 :   if (! balance_ok)
    1548             :   {
    1549          13 :     GNUNET_break_op (0);
    1550          13 :     SET_ERROR_WITH_FIELD (mc,
    1551             :                           MELT_ERROR_INSUFFICIENT_FUNDS,
    1552             :                           insufficient_funds);
    1553          13 :     return GNUNET_DB_STATUS_HARD_ERROR;
    1554             :   }
    1555          16 :   if (mc->zombie_required)
    1556             :   {
    1557           0 :     GNUNET_break_op (0);
    1558           0 :     SET_ERROR (mc,
    1559             :                MELT_ERROR_COIN_EXPIRED_NO_ZOMBIE);
    1560           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
    1561             :   }
    1562             : 
    1563          16 :   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
    1564          16 :     TEH_METRICS_num_success[TEH_MT_SUCCESS_MELT]++;
    1565          16 :   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          38 : phase_run_transaction (
    1577             :   struct MeltContext *mc)
    1578             : {
    1579          38 :   if (GNUNET_SYSERR ==
    1580          38 :       TEH_plugin->preflight (TEH_plugin->cls))
    1581             :   {
    1582           0 :     GNUNET_break (0);
    1583           0 :     SET_ERROR (mc,
    1584             :                MELT_ERROR_DB_PREFLIGHT_FAILURE);
    1585           0 :     return;
    1586             :   }
    1587             : 
    1588             :   /* first, make sure coin is known */
    1589          38 :   if (! mc->coin_is_dirty)
    1590             :   {
    1591          38 :     MHD_RESULT mhd_ret = -1;
    1592             :     enum GNUNET_DB_QueryStatus qs;
    1593             : 
    1594          38 :     for (unsigned int tries = 0; tries<MAX_TRANSACTION_COMMIT_RETRIES; tries++)
    1595             :     {
    1596          38 :       qs = TEH_make_coin_known (&mc->request.refresh.coin,
    1597          38 :                                 mc->rc->connection,
    1598             :                                 &mc->known_coin_id,
    1599             :                                 &mhd_ret);
    1600          38 :       if (GNUNET_DB_STATUS_SOFT_ERROR != qs)
    1601          38 :         break;
    1602             :     }
    1603          38 :     if (0 > qs)
    1604             :     {
    1605           1 :       GNUNET_break (0);
    1606             :       /* Check if an answer has been queued */
    1607           1 :       switch (mhd_ret)
    1608             :       {
    1609           0 :       case MHD_NO:
    1610           0 :         mc->phase = MELT_PHASE_RETURN_NO;
    1611           1 :         return;
    1612           1 :       case MHD_YES:
    1613           1 :         mc->phase = MELT_PHASE_RETURN_YES;
    1614           1 :         return;
    1615           0 :       default:
    1616             :         /* ignore */
    1617             :       }
    1618           0 :       SET_ERROR (mc,
    1619             :                  MELT_ERROR_DB_MAKE_COIN_KNOW_FAILURE);
    1620           0 :       return;
    1621             :     }
    1622             :   }
    1623             : 
    1624             :   /* run main database transaction */
    1625             :   {
    1626          37 :     MHD_RESULT mhd_ret = -1;
    1627             :     enum GNUNET_GenericReturnValue ret;
    1628          37 :     enum MeltPhase current_phase = mc->phase;
    1629             : 
    1630          37 :     GNUNET_assert (MELT_PHASE_RUN_TRANSACTION ==
    1631             :                    current_phase);
    1632          37 :     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          37 :     if (GNUNET_OK != ret)
    1639             :     {
    1640          13 :       GNUNET_break (0);
    1641             :       /* Check if an answer has been queued */
    1642          13 :       switch (mhd_ret)
    1643             :       {
    1644           0 :       case MHD_NO:
    1645           0 :         mc->phase = MELT_PHASE_RETURN_NO;
    1646          21 :         return;
    1647           0 :       case MHD_YES:
    1648           0 :         mc->phase = MELT_PHASE_RETURN_YES;
    1649           0 :         return;
    1650          13 :       default:
    1651             :         /* ignore */
    1652             :       }
    1653          13 :       GNUNET_assert (MELT_ERROR_NONE != mc->error.code);
    1654          13 :       GNUNET_assert (MELT_PHASE_GENERATE_REPLY_ERROR == mc->phase);
    1655          13 :       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          24 :     if (current_phase != mc->phase)
    1663           8 :       return;
    1664             :   }
    1665          16 :   mc->phase++;
    1666             : }
    1667             : 
    1668             : 
    1669             : MHD_RESULT
    1670          38 : TEH_handler_melt_v27 (
    1671             :   struct TEH_RequestContext *rc,
    1672             :   const json_t *root,
    1673             :   const char *const args[0])
    1674             : {
    1675          38 :   struct MeltContext *mc = rc->rh_ctx;
    1676             : 
    1677             :   (void) args;
    1678          38 :   if (NULL == mc)
    1679             :   {
    1680          38 :     mc = GNUNET_new (struct MeltContext);
    1681          38 :     rc->rh_ctx = mc;
    1682          38 :     rc->rh_cleaner = &clean_melt_rc;
    1683          38 :     mc->rc = rc;
    1684          38 :     mc->now = GNUNET_TIME_timestamp_get ();
    1685             :   }
    1686             : 
    1687             :   while (true)
    1688             :   {
    1689         568 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1690             :                 "melt processing in phase %d\n",
    1691             :                 mc->phase);
    1692         303 :     switch (mc->phase)
    1693             :     {
    1694          38 :     case MELT_PHASE_PARSE:
    1695          38 :       phase_parse_request (mc,
    1696             :                            root);
    1697          38 :       break;
    1698          38 :     case MELT_PHASE_CHECK_MELT_VALID:
    1699          38 :       phase_check_melt_valid (mc);
    1700          38 :       break;
    1701          38 :     case MELT_PHASE_CHECK_KEYS:
    1702          38 :       phase_check_keys (mc);
    1703          38 :       break;
    1704          38 :     case MELT_PHASE_CHECK_COIN_SIGNATURE:
    1705          38 :       phase_check_coin_signature (mc);
    1706          38 :       break;
    1707          38 :     case MELT_PHASE_PREPARE_TRANSACTION:
    1708          38 :       phase_prepare_transaction (mc);
    1709          38 :       break;
    1710          38 :     case MELT_PHASE_RUN_TRANSACTION:
    1711          38 :       phase_run_transaction (mc);
    1712          38 :       break;
    1713          24 :     case MELT_PHASE_GENERATE_REPLY_SUCCESS:
    1714          24 :       phase_generate_reply_success (mc);
    1715          24 :       break;
    1716          13 :     case MELT_PHASE_GENERATE_REPLY_ERROR:
    1717          13 :       phase_generate_reply_error (mc);
    1718          13 :       break;
    1719          38 :     case MELT_PHASE_RETURN_YES:
    1720          38 :       return MHD_YES;
    1721           0 :     case MELT_PHASE_RETURN_NO:
    1722           0 :       return MHD_NO;
    1723             :     }
    1724             :   }
    1725             : }

Generated by: LCOV version 1.16