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


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