exchange

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

taler-exchange-httpd_post-withdraw.c (55465B)


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