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


      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   if (NULL != wc->request.planchets)
   1201   {
   1202     /* num_planchets is set long before planchets is allocated,
   1203        so this needs the above guard */
   1204     for (unsigned int i = 0; i<wc->request.num_planchets; i++)
   1205       TALER_blinded_planchet_free (&wc->request.planchets[i]);
   1206     GNUNET_free (wc->request.planchets);
   1207   }
   1208   free_db_withdraw_data (&wc->request.withdraw);
   1209   GNUNET_free (wc->request.cs_indices);
   1210   if (wc->request.is_idempotent)
   1211     free_db_withdraw_data (&wc->request.withdraw_idem);
   1212   if ( (WITHDRAW_ERROR_LEGITIMIZATION_RESULT == wc->error.code) &&
   1213        (NULL != wc->error.details.legitimization_result.response) )
   1214   {
   1215     MHD_destroy_response (wc->error.details.legitimization_result.response);
   1216     wc->error.details.legitimization_result.response = NULL;
   1217   }
   1218   GNUNET_free (wc);
   1219 }
   1220 
   1221 
   1222 /**
   1223  * Generates response for the withdraw request.
   1224  *
   1225  * @param wc withdraw operation context
   1226  */
   1227 static void
   1228 phase_generate_reply_success (struct WithdrawContext *wc)
   1229 {
   1230   struct TALER_EXCHANGEDB_Withdraw *db_obj;
   1231 
   1232   db_obj = wc->request.is_idempotent
   1233     ? &wc->request.withdraw_idem
   1234     : &wc->request.withdraw;
   1235 
   1236   if (wc->request.withdraw.age_proof_required)
   1237   {
   1238     struct TALER_ExchangePublicKeyP pub;
   1239     struct TALER_ExchangeSignatureP sig;
   1240     enum TALER_ErrorCode ec_confirmation_sign;
   1241 
   1242     ec_confirmation_sign =
   1243       TALER_exchange_online_withdraw_age_confirmation_sign (
   1244         &TEH_keys_exchange_sign_,
   1245         &db_obj->planchets_h,
   1246         db_obj->noreveal_index,
   1247         &pub,
   1248         &sig);
   1249     if (TALER_EC_NONE != ec_confirmation_sign)
   1250     {
   1251       SET_ERROR_WITH_FIELD (wc,
   1252                             WITHDRAW_ERROR_CONFIRMATION_SIGN,
   1253                             ec_confirmation_sign);
   1254       return;
   1255     }
   1256 
   1257     finish_loop (wc,
   1258                  TALER_MHD_REPLY_JSON_PACK (
   1259                    wc->rc->connection,
   1260                    MHD_HTTP_CREATED,
   1261                    GNUNET_JSON_pack_uint64 ("noreveal_index",
   1262                                             db_obj->noreveal_index),
   1263                    GNUNET_JSON_pack_data_auto ("exchange_sig",
   1264                                                &sig),
   1265                    GNUNET_JSON_pack_data_auto ("exchange_pub",
   1266                                                &pub)));
   1267   }
   1268   else /* not age restricted */
   1269   {
   1270     json_t *sigs;
   1271 
   1272     sigs = json_array ();
   1273     GNUNET_assert (NULL != sigs);
   1274     for (unsigned int i = 0; i<db_obj->num_coins; i++)
   1275     {
   1276       GNUNET_assert (
   1277         0 ==
   1278         json_array_append_new (
   1279           sigs,
   1280           GNUNET_JSON_PACK (
   1281             TALER_JSON_pack_blinded_denom_sig (
   1282               NULL,
   1283               &db_obj->denom_sigs[i]))));
   1284     }
   1285     finish_loop (wc,
   1286                  TALER_MHD_REPLY_JSON_PACK (
   1287                    wc->rc->connection,
   1288                    MHD_HTTP_OK,
   1289                    GNUNET_JSON_pack_array_steal ("ev_sigs",
   1290                                                  sigs)));
   1291   }
   1292 
   1293   TEH_METRICS_withdraw_num_coins += db_obj->num_coins;
   1294 }
   1295 
   1296 
   1297 /**
   1298  * Reports an error, potentially with details.
   1299  * That is, it puts a error-type specific response into the MHD queue.
   1300  * It will do a idempotency check first, if needed for the error type.
   1301  *
   1302  * @param wc withdraw context
   1303  */
   1304 static void
   1305 phase_generate_reply_error (
   1306   struct WithdrawContext *wc)
   1307 {
   1308   GNUNET_assert (WITHDRAW_PHASE_GENERATE_REPLY_ERROR == wc->phase);
   1309   if (IDEMPOTENCY_CHECK_REQUIRED (wc->error.code) &&
   1310       withdraw_is_idempotent (wc))
   1311   {
   1312     return;
   1313   }
   1314 
   1315   switch (wc->error.code)
   1316   {
   1317   case WITHDRAW_ERROR_NONE:
   1318     break;
   1319   case WITHDRAW_ERROR_REQUEST_PARAMETER_MALFORMED:
   1320     finish_loop (wc,
   1321                  TALER_MHD_reply_with_error (
   1322                    wc->rc->connection,
   1323                    MHD_HTTP_BAD_REQUEST,
   1324                    TALER_EC_GENERIC_PARAMETER_MALFORMED,
   1325                    wc->error.details.request_parameter_malformed));
   1326     return;
   1327   case WITHDRAW_ERROR_KEYS_MISSING:
   1328     finish_loop (wc,
   1329                  TALER_MHD_reply_with_error (
   1330                    wc->rc->connection,
   1331                    MHD_HTTP_SERVICE_UNAVAILABLE,
   1332                    TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING,
   1333                    NULL));
   1334     return;
   1335   case WITHDRAW_ERROR_DB_FETCH_FAILED:
   1336     finish_loop (wc,
   1337                  TALER_MHD_reply_with_error (
   1338                    wc->rc->connection,
   1339                    MHD_HTTP_INTERNAL_SERVER_ERROR,
   1340                    TALER_EC_GENERIC_DB_FETCH_FAILED,
   1341                    wc->error.details.db_fetch_context));
   1342     return;
   1343   case WITHDRAW_ERROR_DB_INVARIANT_FAILURE:
   1344     finish_loop (wc,
   1345                  TALER_MHD_reply_with_error (
   1346                    wc->rc->connection,
   1347                    MHD_HTTP_INTERNAL_SERVER_ERROR,
   1348                    TALER_EC_GENERIC_DB_INVARIANT_FAILURE,
   1349                    NULL));
   1350     return;
   1351   case WITHDRAW_ERROR_RESERVE_UNKNOWN:
   1352     finish_loop (wc,
   1353                  TALER_MHD_reply_with_error (
   1354                    wc->rc->connection,
   1355                    MHD_HTTP_NOT_FOUND,
   1356                    TALER_EC_EXCHANGE_GENERIC_RESERVE_UNKNOWN,
   1357                    NULL));
   1358     return;
   1359   case WITHDRAW_ERROR_DENOMINATION_SIGN:
   1360     finish_loop (wc,
   1361                  TALER_MHD_reply_with_ec (
   1362                    wc->rc->connection,
   1363                    wc->error.details.ec_denomination_sign,
   1364                    NULL));
   1365     return;
   1366   case WITHDRAW_ERROR_KYC_REQUIRED:
   1367     finish_loop (wc,
   1368                  TEH_RESPONSE_reply_kyc_required (
   1369                    wc->rc->connection,
   1370                    &wc->h_normalized_payto,
   1371                    &wc->kyc,
   1372                    false));
   1373     return;
   1374   case WITHDRAW_ERROR_DENOMINATION_KEY_UNKNOWN:
   1375     GNUNET_break_op (0);
   1376     finish_loop (wc,
   1377                  TEH_RESPONSE_reply_unknown_denom_pub_hash (
   1378                    wc->rc->connection,
   1379                    wc->error.details.denom_h));
   1380     return;
   1381   case WITHDRAW_ERROR_DENOMINATION_EXPIRED:
   1382     GNUNET_break_op (0);
   1383     finish_loop (wc,
   1384                  TEH_RESPONSE_reply_expired_denom_pub_hash (
   1385                    wc->rc->connection,
   1386                    wc->error.details.denom_h,
   1387                    TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED,
   1388                    "WITHDRAW"));
   1389     return;
   1390   case WITHDRAW_ERROR_DENOMINATION_VALIDITY_IN_FUTURE:
   1391     finish_loop (wc,
   1392                  TEH_RESPONSE_reply_expired_denom_pub_hash (
   1393                    wc->rc->connection,
   1394                    wc->error.details.denom_h,
   1395                    TALER_EC_EXCHANGE_GENERIC_DENOMINATION_VALIDITY_IN_FUTURE,
   1396                    "WITHDRAW"));
   1397     return;
   1398   case WITHDRAW_ERROR_DENOMINATION_REVOKED:
   1399     GNUNET_break_op (0);
   1400     finish_loop (wc,
   1401                  TALER_MHD_reply_with_error (
   1402                    wc->rc->connection,
   1403                    MHD_HTTP_GONE,
   1404                    TALER_EC_EXCHANGE_GENERIC_DENOMINATION_REVOKED,
   1405                    NULL));
   1406     return;
   1407   case WITHDRAW_ERROR_CIPHER_MISMATCH:
   1408     finish_loop (wc,
   1409                  TALER_MHD_reply_with_error (
   1410                    wc->rc->connection,
   1411                    MHD_HTTP_BAD_REQUEST,
   1412                    TALER_EC_EXCHANGE_GENERIC_CIPHER_MISMATCH,
   1413                    NULL));
   1414     return;
   1415   case WITHDRAW_ERROR_BLINDING_SEED_REQUIRED:
   1416     finish_loop (wc,
   1417                  TALER_MHD_reply_with_error (
   1418                    wc->rc->connection,
   1419                    MHD_HTTP_BAD_REQUEST,
   1420                    TALER_EC_GENERIC_PARAMETER_MISSING,
   1421                    "blinding_seed"));
   1422     return;
   1423   case WITHDRAW_ERROR_CRYPTO_HELPER:
   1424     finish_loop (wc,
   1425                  TALER_MHD_reply_with_error (
   1426                    wc->rc->connection,
   1427                    MHD_HTTP_INTERNAL_SERVER_ERROR,
   1428                    TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
   1429                    NULL));
   1430     return;
   1431   case WITHDRAW_ERROR_RESERVE_CIPHER_UNKNOWN:
   1432     finish_loop (wc,
   1433                  TALER_MHD_reply_with_error (
   1434                    wc->rc->connection,
   1435                    MHD_HTTP_BAD_REQUEST,
   1436                    TALER_EC_EXCHANGE_GENERIC_CIPHER_MISMATCH,
   1437                    "cipher"));
   1438     return;
   1439   case WITHDRAW_ERROR_AGE_RESTRICTION_NOT_SUPPORTED_BY_DENOMINATION:
   1440     {
   1441       char msg[256];
   1442 
   1443       GNUNET_snprintf (msg,
   1444                        sizeof(msg),
   1445                        "denomination %s does not support age restriction",
   1446                        GNUNET_h2s (&wc->error.details.denom_h->hash));
   1447       finish_loop (wc,
   1448                    TALER_MHD_reply_with_error (
   1449                      wc->rc->connection,
   1450                      MHD_HTTP_NOT_FOUND,
   1451                      TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN,
   1452                      msg));
   1453       return;
   1454     }
   1455   case WITHDRAW_ERROR_MAXIMUM_AGE_TOO_LARGE:
   1456     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1457                 "Generating JSON response with code %d\n",
   1458                 (int) TALER_EC_EXCHANGE_WITHDRAW_MAXIMUM_AGE_TOO_LARGE);
   1459     finish_loop (wc,
   1460                  TALER_MHD_REPLY_JSON_PACK (
   1461                    wc->rc->connection,
   1462                    MHD_HTTP_CONFLICT,
   1463                    TALER_MHD_PACK_EC (
   1464                      TALER_EC_EXCHANGE_WITHDRAW_MAXIMUM_AGE_TOO_LARGE),
   1465                    GNUNET_JSON_pack_uint64 (
   1466                      "allowed_maximum_age",
   1467                      wc->error.details.maximum_age_too_large.max_allowed),
   1468                    GNUNET_JSON_pack_uint64 (
   1469                      "reserve_birthday",
   1470                      wc->error.details.maximum_age_too_large.birthday)));
   1471     return;
   1472   case WITHDRAW_ERROR_AGE_RESTRICTION_REQUIRED:
   1473     finish_loop (wc,
   1474                  TEH_RESPONSE_reply_reserve_age_restriction_required (
   1475                    wc->rc->connection,
   1476                    wc->error.details.age_restriction_required));
   1477     return;
   1478   case WITHDRAW_ERROR_AMOUNT_OVERFLOW:
   1479     finish_loop (wc,
   1480                  TALER_MHD_reply_with_error (
   1481                    wc->rc->connection,
   1482                    MHD_HTTP_BAD_REQUEST,
   1483                    TALER_EC_EXCHANGE_WITHDRAW_AMOUNT_OVERFLOW,
   1484                    "amount"));
   1485     return;
   1486   case WITHDRAW_ERROR_FEE_OVERFLOW:
   1487     finish_loop (wc,
   1488                  TALER_MHD_reply_with_error (
   1489                    wc->rc->connection,
   1490                    MHD_HTTP_BAD_REQUEST,
   1491                    TALER_EC_EXCHANGE_WITHDRAW_AMOUNT_OVERFLOW,
   1492                    "fee"));
   1493     return;
   1494   case WITHDRAW_ERROR_AMOUNT_PLUS_FEE_OVERFLOW:
   1495     finish_loop (wc,
   1496                  TALER_MHD_reply_with_error (
   1497                    wc->rc->connection,
   1498                    MHD_HTTP_INTERNAL_SERVER_ERROR,
   1499                    TALER_EC_EXCHANGE_WITHDRAW_AMOUNT_FEE_OVERFLOW,
   1500                    "amount+fee"));
   1501     return;
   1502   case WITHDRAW_ERROR_CONFIRMATION_SIGN:
   1503     finish_loop (wc,
   1504                  TALER_MHD_reply_with_ec (
   1505                    wc->rc->connection,
   1506                    wc->error.details.ec_confirmation_sign,
   1507                    NULL));
   1508     return;
   1509   case WITHDRAW_ERROR_INSUFFICIENT_FUNDS:
   1510     finish_loop (wc,
   1511                  TEH_RESPONSE_reply_reserve_insufficient_balance (
   1512                    wc->rc->connection,
   1513                    TALER_EC_EXCHANGE_WITHDRAW_INSUFFICIENT_FUNDS,
   1514                    &wc->error.details.insufficient_funds,
   1515                    &wc->request.withdraw.amount_with_fee,
   1516                    &wc->request.withdraw.reserve_pub));
   1517     return;
   1518   case WITHDRAW_ERROR_IDEMPOTENT_PLANCHET:
   1519     finish_loop (wc,
   1520                  TALER_MHD_reply_with_error (
   1521                    wc->rc->connection,
   1522                    MHD_HTTP_BAD_REQUEST,
   1523                    TALER_EC_EXCHANGE_WITHDRAW_IDEMPOTENT_PLANCHET,
   1524                    NULL));
   1525     return;
   1526   case WITHDRAW_ERROR_NONCE_REUSE:
   1527     finish_loop (wc,
   1528                  TALER_MHD_reply_with_error (
   1529                    wc->rc->connection,
   1530                    MHD_HTTP_CONFLICT,
   1531                    TALER_EC_EXCHANGE_WITHDRAW_NONCE_REUSE,
   1532                    NULL));
   1533     return;
   1534   case WITHDRAW_ERROR_RESERVE_SIGNATURE_INVALID:
   1535     finish_loop (wc,
   1536                  TALER_MHD_reply_with_error (
   1537                    wc->rc->connection,
   1538                    MHD_HTTP_FORBIDDEN,
   1539                    TALER_EC_EXCHANGE_WITHDRAW_RESERVE_SIGNATURE_INVALID,
   1540                    NULL));
   1541     return;
   1542   case WITHDRAW_ERROR_LEGITIMIZATION_RESULT: {
   1543       finish_loop (
   1544         wc,
   1545         MHD_queue_response (wc->rc->connection,
   1546                             wc->error.details.legitimization_result.http_status,
   1547                             wc->error.details.legitimization_result.response));
   1548       return;
   1549     }
   1550   }
   1551   GNUNET_break (0);
   1552   finish_loop (wc,
   1553                TALER_MHD_reply_with_error (
   1554                  wc->rc->connection,
   1555                  MHD_HTTP_INTERNAL_SERVER_ERROR,
   1556                  TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
   1557                  "error phase without error"));
   1558 }
   1559 
   1560 
   1561 /**
   1562  * Initializes the new context for the incoming withdraw request
   1563  *
   1564  * @param[in,out] wc withdraw request context
   1565  * @param root json body of the request
   1566  */
   1567 static void
   1568 withdraw_phase_parse (
   1569   struct WithdrawContext *wc,
   1570   const json_t *root)
   1571 {
   1572   const json_t *j_denoms_h;
   1573   const json_t *j_coin_evs;
   1574   const char *cipher;
   1575   bool no_max_age;
   1576   struct GNUNET_JSON_Specification spec[] = {
   1577     GNUNET_JSON_spec_string ("cipher",
   1578                              &cipher),
   1579     GNUNET_JSON_spec_fixed_auto  ("reserve_pub",
   1580                                   &wc->request.withdraw.reserve_pub),
   1581     GNUNET_JSON_spec_array_const ("denoms_h",
   1582                                   &j_denoms_h),
   1583     GNUNET_JSON_spec_array_const ("coin_evs",
   1584                                   &j_coin_evs),
   1585     GNUNET_JSON_spec_mark_optional (
   1586       GNUNET_JSON_spec_uint16 ("max_age",
   1587                                &wc->request.withdraw.max_age),
   1588       &no_max_age),
   1589     GNUNET_JSON_spec_mark_optional (
   1590       GNUNET_JSON_spec_fixed_auto ("blinding_seed",
   1591                                    &wc->request.withdraw.blinding_seed),
   1592       &wc->request.withdraw.no_blinding_seed),
   1593     GNUNET_JSON_spec_fixed_auto ("reserve_sig",
   1594                                  &wc->request.withdraw.reserve_sig),
   1595     GNUNET_JSON_spec_end ()
   1596   };
   1597   enum GNUNET_GenericReturnValue res;
   1598 
   1599   res = TALER_MHD_parse_json_data (wc->rc->connection,
   1600                                    root,
   1601                                    spec);
   1602   if (GNUNET_YES != res)
   1603   {
   1604     GNUNET_break_op (0);
   1605     wc->phase = (GNUNET_SYSERR == res)
   1606       ? WITHDRAW_PHASE_RETURN_NO
   1607       : WITHDRAW_PHASE_RETURN_YES;
   1608     return;
   1609   }
   1610 
   1611   /* For now, we only support cipher "ED25519" for signatures by the reserve */
   1612   if (0 != strcmp ("ED25519",
   1613                    cipher))
   1614   {
   1615     GNUNET_break_op (0);
   1616     SET_ERROR_WITH_DETAIL (wc,
   1617                            WITHDRAW_ERROR_RESERVE_CIPHER_UNKNOWN,
   1618                            reserve_cipher_unknown,
   1619                            cipher);
   1620     return;
   1621   }
   1622 
   1623   wc->request.withdraw.age_proof_required = ! no_max_age;
   1624 
   1625   if (wc->request.withdraw.age_proof_required)
   1626   {
   1627     /* The age value MUST be on the beginning of an age group */
   1628     if (wc->request.withdraw.max_age !=
   1629         TALER_get_lowest_age (&TEH_age_restriction_mask,
   1630                               wc->request.withdraw.max_age))
   1631     {
   1632       GNUNET_break_op (0);
   1633       SET_ERROR_WITH_DETAIL (
   1634         wc,
   1635         WITHDRAW_ERROR_REQUEST_PARAMETER_MALFORMED,
   1636         request_parameter_malformed,
   1637         "max_age must be the lower edge of an age group");
   1638       return;
   1639     }
   1640   }
   1641 
   1642   /* validate array size */
   1643   {
   1644     size_t num_coins = json_array_size (j_denoms_h);
   1645     size_t array_size = json_array_size (j_coin_evs);
   1646     const char *error;
   1647 
   1648     GNUNET_static_assert (
   1649       TALER_MAX_COINS < INT_MAX / TALER_CNC_KAPPA);
   1650 
   1651 #define BAIL_IF(cond, msg) \
   1652         if ((cond)) { \
   1653           GNUNET_break_op (0); \
   1654           error = (msg); break; \
   1655         }
   1656 
   1657     do {
   1658       BAIL_IF (0 == num_coins,
   1659                "denoms_h must not be empty")
   1660 
   1661       /**
   1662          * The wallet had committed to more than the maximum coins allowed, the
   1663          * reserve has been charged, but now the user can not withdraw any money
   1664          * from it.  Note that the user can't get their money back in this case!
   1665          */
   1666       BAIL_IF (num_coins > TALER_MAX_COINS,
   1667                "maximum number of coins that can be withdrawn has been exceeded")
   1668 
   1669       BAIL_IF ((! wc->request.withdraw.age_proof_required) &&
   1670                (num_coins != array_size),
   1671                "denoms_h and coin_evs must be arrays of the same size")
   1672 
   1673       BAIL_IF (wc->request.withdraw.age_proof_required &&
   1674                ((TALER_CNC_KAPPA * num_coins) != array_size),
   1675                "coin_evs must be an array of length "
   1676                TALER_CNC_KAPPA_STR
   1677                "*len(denoms_h)")
   1678 
   1679       wc->request.withdraw.num_coins = num_coins;
   1680       wc->request.num_planchets = array_size;
   1681       error = NULL;
   1682 
   1683     } while (0);
   1684 #undef BAIL_IF
   1685 
   1686     if (NULL != error)
   1687     {
   1688       SET_ERROR_WITH_DETAIL (wc,
   1689                              WITHDRAW_ERROR_REQUEST_PARAMETER_MALFORMED,
   1690                              request_parameter_malformed,
   1691                              error);
   1692       return;
   1693     }
   1694   }
   1695   /* extract the denomination hashes */
   1696   {
   1697     size_t idx;
   1698     json_t *value;
   1699 
   1700     wc->request.denoms_h
   1701       = GNUNET_new_array (wc->request.withdraw.num_coins,
   1702                           struct TALER_DenominationHashP);
   1703 
   1704     json_array_foreach (j_denoms_h, idx, value) {
   1705       struct GNUNET_JSON_Specification ispec[] = {
   1706         GNUNET_JSON_spec_fixed_auto (NULL,
   1707                                      &wc->request.denoms_h[idx]),
   1708         GNUNET_JSON_spec_end ()
   1709       };
   1710 
   1711       res = TALER_MHD_parse_json_data (wc->rc->connection,
   1712                                        value,
   1713                                        ispec);
   1714       if (GNUNET_YES != res)
   1715       {
   1716         GNUNET_break_op (0);
   1717         wc->phase = (GNUNET_SYSERR == res)
   1718           ? WITHDRAW_PHASE_RETURN_NO
   1719           : WITHDRAW_PHASE_RETURN_YES;
   1720         return;
   1721       }
   1722     }
   1723   }
   1724   /* Parse the blinded coin envelopes */
   1725   {
   1726     json_t *j_cev;
   1727     size_t idx;
   1728 
   1729     wc->request.planchets =
   1730       GNUNET_new_array (wc->request.num_planchets,
   1731                         struct  TALER_BlindedPlanchet);
   1732     json_array_foreach (j_coin_evs, idx, j_cev)
   1733     {
   1734       /* Now parse the individual envelopes and calculate the hash of
   1735        * the commitment along the way. */
   1736       struct GNUNET_JSON_Specification kspec[] = {
   1737         TALER_JSON_spec_blinded_planchet (NULL,
   1738                                           &wc->request.planchets[idx]),
   1739         GNUNET_JSON_spec_end ()
   1740       };
   1741 
   1742       res = TALER_MHD_parse_json_data (wc->rc->connection,
   1743                                        j_cev,
   1744                                        kspec);
   1745       if (GNUNET_OK != res)
   1746       {
   1747         GNUNET_break_op (0);
   1748         wc->phase = (GNUNET_SYSERR == res)
   1749           ? WITHDRAW_PHASE_RETURN_NO
   1750           : WITHDRAW_PHASE_RETURN_YES;
   1751         return;
   1752       }
   1753 
   1754       /* Check for duplicate planchets. Technically a bug on
   1755        * the client side that is harmless for us, but still
   1756        * not allowed per protocol */
   1757       for (size_t i = 0; i < idx; i++)
   1758       {
   1759         if (0 ==
   1760             TALER_blinded_planchet_cmp (
   1761               &wc->request.planchets[idx],
   1762               &wc->request.planchets[i]))
   1763         {
   1764           GNUNET_break_op (0);
   1765           SET_ERROR (wc,
   1766                      WITHDRAW_ERROR_IDEMPOTENT_PLANCHET);
   1767           return;
   1768         }
   1769       }       /* end duplicate check */
   1770     }       /* json_array_foreach over j_coin_evs */
   1771   }       /* scope of j_kappa_planchets, idx */
   1772   wc->phase = WITHDRAW_PHASE_CHECK_KEYS;
   1773 }
   1774 
   1775 
   1776 enum MHD_Result
   1777 TEH_handler_withdraw (
   1778   struct TEH_RequestContext *rc,
   1779   const json_t *root,
   1780   const char *const args[0])
   1781 {
   1782   struct WithdrawContext *wc = rc->rh_ctx;
   1783 
   1784   (void) args;
   1785   if (NULL == wc)
   1786   {
   1787     wc = GNUNET_new (struct WithdrawContext);
   1788     rc->rh_ctx = wc;
   1789     rc->rh_cleaner = &clean_withdraw_rc;
   1790     wc->rc = rc;
   1791     wc->now = GNUNET_TIME_timestamp_get ();
   1792   }
   1793   while (true)
   1794   {
   1795     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1796                 "withdrawal%s processing in phase %d\n",
   1797                 wc->request.withdraw.age_proof_required
   1798                      ? " (with required age proof)"
   1799                      : "",
   1800                 wc->phase);
   1801     switch (wc->phase)
   1802     {
   1803     case WITHDRAW_PHASE_PARSE:
   1804       withdraw_phase_parse (wc,
   1805                             root);
   1806       break;
   1807     case WITHDRAW_PHASE_CHECK_KEYS:
   1808       phase_check_keys (wc);
   1809       break;
   1810     case WITHDRAW_PHASE_CHECK_RESERVE_SIGNATURE:
   1811       phase_check_reserve_signature (wc);
   1812       break;
   1813     case WITHDRAW_PHASE_RUN_LEGI_CHECK:
   1814       phase_run_legi_check (wc);
   1815       break;
   1816     case WITHDRAW_PHASE_SUSPENDED:
   1817       return MHD_YES;
   1818     case WITHDRAW_PHASE_CHECK_KYC_RESULT:
   1819       phase_check_kyc_result (wc);
   1820       break;
   1821     case WITHDRAW_PHASE_PREPARE_TRANSACTION:
   1822       phase_prepare_transaction (wc);
   1823       break;
   1824     case WITHDRAW_PHASE_RUN_TRANSACTION:
   1825       phase_run_transaction (wc);
   1826       break;
   1827     case WITHDRAW_PHASE_GENERATE_REPLY_SUCCESS:
   1828       phase_generate_reply_success (wc);
   1829       break;
   1830     case WITHDRAW_PHASE_GENERATE_REPLY_ERROR:
   1831       phase_generate_reply_error (wc);
   1832       break;
   1833     case WITHDRAW_PHASE_RETURN_YES:
   1834       return MHD_YES;
   1835     case WITHDRAW_PHASE_RETURN_NO:
   1836       return MHD_NO;
   1837     }
   1838   }
   1839 }