LCOV - code coverage report
Current view: top level - exchange - taler-exchange-httpd_batch-withdraw.c (source / functions) Hit Total Coverage
Test: GNU Taler exchange coverage report Lines: 0 217 0.0 %
Date: 2022-08-25 06:15:09 Functions: 0 7 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   Copyright (C) 2014-2022 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_batch-withdraw.c
      21             :  * @brief Handle /reserves/$RESERVE_PUB/batch-withdraw requests
      22             :  * @author Florian Dold
      23             :  * @author Benedikt Mueller
      24             :  * @author Christian Grothoff
      25             :  */
      26             : #include "platform.h"
      27             : #include <gnunet/gnunet_util_lib.h>
      28             : #include <jansson.h>
      29             : #include "taler_json_lib.h"
      30             : #include "taler_kyclogic_lib.h"
      31             : #include "taler_mhd_lib.h"
      32             : #include "taler-exchange-httpd_batch-withdraw.h"
      33             : #include "taler-exchange-httpd_responses.h"
      34             : #include "taler-exchange-httpd_keys.h"
      35             : 
      36             : 
      37             : /**
      38             :  * Information per planchet in the batch.
      39             :  */
      40             : struct PlanchetContext
      41             : {
      42             : 
      43             :   /**
      44             :    * Hash of the (blinded) message to be signed by the Exchange.
      45             :    */
      46             :   struct TALER_BlindedCoinHashP h_coin_envelope;
      47             : 
      48             :   /**
      49             :    * Value of the coin being exchanged (matching the denomination key)
      50             :    * plus the transaction fee.  We include this in what is being
      51             :    * signed so that we can verify a reserve's remaining total balance
      52             :    * without needing to access the respective denomination key
      53             :    * information each time.
      54             :    */
      55             :   struct TALER_Amount amount_with_fee;
      56             : 
      57             :   /**
      58             :    * Blinded planchet.
      59             :    */
      60             :   struct TALER_BlindedPlanchet blinded_planchet;
      61             : 
      62             :   /**
      63             :    * Set to the resulting signed coin data to be returned to the client.
      64             :    */
      65             :   struct TALER_EXCHANGEDB_CollectableBlindcoin collectable;
      66             : 
      67             : };
      68             : 
      69             : /**
      70             :  * Context for #batch_withdraw_transaction.
      71             :  */
      72             : struct BatchWithdrawContext
      73             : {
      74             : 
      75             :   /**
      76             :    * Public key of the reserv.
      77             :    */
      78             :   const struct TALER_ReservePublicKeyP *reserve_pub;
      79             : 
      80             :   /**
      81             :    * KYC status of the reserve used for the operation.
      82             :    */
      83             :   struct TALER_EXCHANGEDB_KycStatus kyc;
      84             : 
      85             :   /**
      86             :    * Array of @e planchets_length planchets we are processing.
      87             :    */
      88             :   struct PlanchetContext *planchets;
      89             : 
      90             :   /**
      91             :    * Hash of the payto-URI representing the reserve
      92             :    * from which we are withdrawing.
      93             :    */
      94             :   struct TALER_PaytoHashP h_payto;
      95             : 
      96             :   /**
      97             :    * Current time for the DB transaction.
      98             :    */
      99             :   struct GNUNET_TIME_Timestamp now;
     100             : 
     101             :   /**
     102             :    * Total amount from all coins with fees.
     103             :    */
     104             :   struct TALER_Amount batch_total;
     105             : 
     106             :   /**
     107             :    * Length of the @e planchets array.
     108             :    */
     109             :   unsigned int planchets_length;
     110             : 
     111             : };
     112             : 
     113             : 
     114             : /**
     115             :  * Function called to iterate over KYC-relevant
     116             :  * transaction amounts for a particular time range.
     117             :  * Called within a database transaction, so must
     118             :  * not start a new one.
     119             :  *
     120             :  * @param cls closure, identifies the event type and
     121             :  *        account to iterate over events for
     122             :  * @param limit maximum time-range for which events
     123             :  *        should be fetched (timestamp in the past)
     124             :  * @param cb function to call on each event found,
     125             :  *        events must be returned in reverse chronological
     126             :  *        order
     127             :  * @param cb_cls closure for @a cb
     128             :  */
     129             : static void
     130           0 : batch_withdraw_amount_cb (void *cls,
     131             :                           struct GNUNET_TIME_Absolute limit,
     132             :                           TALER_EXCHANGEDB_KycAmountCallback cb,
     133             :                           void *cb_cls)
     134             : {
     135           0 :   struct BatchWithdrawContext *wc = cls;
     136             :   enum GNUNET_DB_QueryStatus qs;
     137             : 
     138           0 :   if (GNUNET_OK !=
     139           0 :       cb (cb_cls,
     140           0 :           &wc->batch_total,
     141             :           wc->now.abs_time))
     142           0 :     return;
     143           0 :   qs = TEH_plugin->select_withdraw_amounts_for_kyc_check (
     144           0 :     TEH_plugin->cls,
     145           0 :     &wc->h_payto,
     146             :     limit,
     147             :     cb,
     148             :     cb_cls);
     149           0 :   GNUNET_break (qs >= 0);
     150             : }
     151             : 
     152             : 
     153             : /**
     154             :  * Function implementing withdraw transaction.  Runs the
     155             :  * transaction logic; IF it returns a non-error code, the transaction
     156             :  * logic MUST NOT queue a MHD response.  IF it returns an hard error,
     157             :  * the transaction logic MUST queue a MHD response and set @a mhd_ret.
     158             :  * IF it returns the soft error code, the function MAY be called again
     159             :  * to retry and MUST not queue a MHD response.
     160             :  *
     161             :  * Note that "wc->collectable.sig" is set before entering this function as we
     162             :  * signed before entering the transaction.
     163             :  *
     164             :  * @param cls a `struct BatchWithdrawContext *`
     165             :  * @param connection MHD request which triggered the transaction
     166             :  * @param[out] mhd_ret set to MHD response status for @a connection,
     167             :  *             if transaction failed (!)
     168             :  * @return transaction status
     169             :  */
     170             : static enum GNUNET_DB_QueryStatus
     171           0 : batch_withdraw_transaction (void *cls,
     172             :                             struct MHD_Connection *connection,
     173             :                             MHD_RESULT *mhd_ret)
     174             : {
     175           0 :   struct BatchWithdrawContext *wc = cls;
     176             :   uint64_t ruuid;
     177             :   enum GNUNET_DB_QueryStatus qs;
     178           0 :   bool balance_ok = false;
     179           0 :   bool found = false;
     180             :   const char *kyc_required;
     181             : 
     182           0 :   wc->now = GNUNET_TIME_timestamp_get ();
     183           0 :   qs = TEH_plugin->reserves_get_origin (TEH_plugin->cls,
     184             :                                         wc->reserve_pub,
     185             :                                         &wc->h_payto);
     186           0 :   if (qs < 0)
     187           0 :     return qs;
     188           0 :   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
     189             :   {
     190           0 :     *mhd_ret = TALER_MHD_reply_with_error (connection,
     191             :                                            MHD_HTTP_NOT_FOUND,
     192             :                                            TALER_EC_EXCHANGE_GENERIC_RESERVE_UNKNOWN,
     193             :                                            NULL);
     194           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
     195             :   }
     196           0 :   kyc_required = TALER_KYCLOGIC_kyc_test_required (
     197             :     TALER_KYCLOGIC_KYC_TRIGGER_WITHDRAW,
     198           0 :     &wc->h_payto,
     199           0 :     TEH_plugin->select_satisfied_kyc_processes,
     200           0 :     TEH_plugin->cls,
     201             :     &batch_withdraw_amount_cb,
     202             :     wc);
     203           0 :   if (NULL != kyc_required)
     204             :   {
     205             :     /* insert KYC requirement into DB! */
     206           0 :     wc->kyc.ok = false;
     207           0 :     return TEH_plugin->insert_kyc_requirement_for_account (
     208           0 :       TEH_plugin->cls,
     209             :       kyc_required,
     210           0 :       &wc->h_payto,
     211             :       &wc->kyc.requirement_row);
     212             :   }
     213           0 :   wc->kyc.ok = true;
     214           0 :   qs = TEH_plugin->do_batch_withdraw (TEH_plugin->cls,
     215             :                                       wc->now,
     216             :                                       wc->reserve_pub,
     217           0 :                                       &wc->batch_total,
     218             :                                       &found,
     219             :                                       &balance_ok,
     220             :                                       &ruuid);
     221           0 :   if (0 > qs)
     222             :   {
     223           0 :     if (GNUNET_DB_STATUS_HARD_ERROR == qs)
     224           0 :       *mhd_ret = TALER_MHD_reply_with_error (connection,
     225             :                                              MHD_HTTP_INTERNAL_SERVER_ERROR,
     226             :                                              TALER_EC_GENERIC_DB_FETCH_FAILED,
     227             :                                              "update_reserve_batch_withdraw");
     228           0 :     return qs;
     229             :   }
     230           0 :   if (! found)
     231             :   {
     232           0 :     *mhd_ret = TALER_MHD_reply_with_error (connection,
     233             :                                            MHD_HTTP_NOT_FOUND,
     234             :                                            TALER_EC_EXCHANGE_GENERIC_RESERVE_UNKNOWN,
     235             :                                            NULL);
     236           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
     237             :   }
     238           0 :   if (! balance_ok)
     239             :   {
     240           0 :     TEH_plugin->rollback (TEH_plugin->cls);
     241           0 :     *mhd_ret = TEH_RESPONSE_reply_reserve_insufficient_balance (
     242             :       connection,
     243           0 :       &wc->batch_total,
     244             :       wc->reserve_pub);
     245           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
     246             :   }
     247             : 
     248             :   /* Add information about each planchet in the batch */
     249           0 :   for (unsigned int i = 0; i<wc->planchets_length; i++)
     250             :   {
     251           0 :     struct PlanchetContext *pc = &wc->planchets[i];
     252           0 :     const struct TALER_BlindedPlanchet *bp = &pc->blinded_planchet;
     253             :     const struct TALER_CsNonce *nonce;
     254           0 :     bool denom_unknown = true;
     255           0 :     bool conflict = true;
     256           0 :     bool nonce_reuse = true;
     257             : 
     258           0 :     nonce = (TALER_DENOMINATION_CS == bp->cipher)
     259             :             ? &bp->details.cs_blinded_planchet.nonce
     260           0 :             : NULL;
     261           0 :     qs = TEH_plugin->do_batch_withdraw_insert (TEH_plugin->cls,
     262             :                                                nonce,
     263           0 :                                                &pc->collectable,
     264             :                                                wc->now,
     265             :                                                ruuid,
     266             :                                                &denom_unknown,
     267             :                                                &conflict,
     268             :                                                &nonce_reuse);
     269           0 :     if (0 > qs)
     270             :     {
     271           0 :       if (GNUNET_DB_STATUS_HARD_ERROR == qs)
     272           0 :         *mhd_ret = TALER_MHD_reply_with_error (connection,
     273             :                                                MHD_HTTP_INTERNAL_SERVER_ERROR,
     274             :                                                TALER_EC_GENERIC_DB_FETCH_FAILED,
     275             :                                                "do_withdraw");
     276           0 :       return qs;
     277             :     }
     278           0 :     if (denom_unknown)
     279             :     {
     280           0 :       GNUNET_break (0);
     281           0 :       *mhd_ret = TALER_MHD_reply_with_error (connection,
     282             :                                              MHD_HTTP_INTERNAL_SERVER_ERROR,
     283             :                                              TALER_EC_GENERIC_DB_INVARIANT_FAILURE,
     284             :                                              NULL);
     285           0 :       return GNUNET_DB_STATUS_HARD_ERROR;
     286             :     }
     287           0 :     if ( (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) ||
     288             :          (conflict) )
     289             :     {
     290           0 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     291             :                   "Idempotent coin in batch, not allowed. Aborting.\n");
     292           0 :       *mhd_ret = TALER_MHD_reply_with_error (connection,
     293             :                                              MHD_HTTP_CONFLICT,
     294             :                                              TALER_EC_EXCHANGE_WITHDRAW_BATCH_IDEMPOTENT_PLANCHET,
     295             :                                              NULL);
     296           0 :       return GNUNET_DB_STATUS_HARD_ERROR;
     297             :     }
     298           0 :     if (nonce_reuse)
     299             :     {
     300           0 :       GNUNET_break_op (0);
     301           0 :       *mhd_ret = TALER_MHD_reply_with_error (connection,
     302             :                                              MHD_HTTP_BAD_REQUEST,
     303             :                                              TALER_EC_EXCHANGE_WITHDRAW_NONCE_REUSE,
     304             :                                              NULL);
     305           0 :       return GNUNET_DB_STATUS_HARD_ERROR;
     306             :     }
     307             :   }
     308           0 :   TEH_METRICS_num_success[TEH_MT_SUCCESS_BATCH_WITHDRAW]++;
     309           0 :   return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
     310             : }
     311             : 
     312             : 
     313             : /**
     314             :  * Generates our final (successful) response.
     315             :  *
     316             :  * @param rc request context
     317             :  * @param wc operation context
     318             :  * @return MHD queue status
     319             :  */
     320             : static MHD_RESULT
     321           0 : generate_reply_success (const struct TEH_RequestContext *rc,
     322             :                         const struct BatchWithdrawContext *wc)
     323             : {
     324             :   json_t *sigs;
     325             : 
     326           0 :   if (! wc->kyc.ok)
     327             :   {
     328             :     /* KYC required */
     329           0 :     return TEH_RESPONSE_reply_kyc_required (rc->connection,
     330             :                                             &wc->h_payto,
     331             :                                             &wc->kyc);
     332             :   }
     333             : 
     334           0 :   sigs = json_array ();
     335           0 :   GNUNET_assert (NULL != sigs);
     336           0 :   for (unsigned int i = 0; i<wc->planchets_length; i++)
     337             :   {
     338           0 :     struct PlanchetContext *pc = &wc->planchets[i];
     339             : 
     340           0 :     GNUNET_assert (
     341             :       0 ==
     342             :       json_array_append_new (
     343             :         sigs,
     344             :         GNUNET_JSON_PACK (
     345             :           TALER_JSON_pack_blinded_denom_sig (
     346             :             "ev_sig",
     347             :             &pc->collectable.sig))));
     348             :   }
     349           0 :   TEH_METRICS_batch_withdraw_num_coins += wc->planchets_length;
     350           0 :   return TALER_MHD_REPLY_JSON_PACK (
     351             :     rc->connection,
     352             :     MHD_HTTP_OK,
     353             :     GNUNET_JSON_pack_array_steal ("ev_sigs",
     354             :                                   sigs));
     355             : }
     356             : 
     357             : 
     358             : /**
     359             :  * Check if the @a rc is replayed and we already have an
     360             :  * answer. If so, replay the existing answer and return the
     361             :  * HTTP response.
     362             :  *
     363             :  * @param rc request context
     364             :  * @param wc parsed request data
     365             :  * @param[out] mret HTTP status, set if we return true
     366             :  * @return true if the request is idempotent with an existing request
     367             :  *    false if we did not find the request in the DB and did not set @a mret
     368             :  */
     369             : static bool
     370           0 : check_request_idempotent (const struct TEH_RequestContext *rc,
     371             :                           const struct BatchWithdrawContext *wc,
     372             :                           MHD_RESULT *mret)
     373             : {
     374           0 :   for (unsigned int i = 0; i<wc->planchets_length; i++)
     375             :   {
     376           0 :     struct PlanchetContext *pc = &wc->planchets[i];
     377             :     enum GNUNET_DB_QueryStatus qs;
     378             : 
     379           0 :     qs = TEH_plugin->get_withdraw_info (TEH_plugin->cls,
     380           0 :                                         &pc->h_coin_envelope,
     381             :                                         &pc->collectable);
     382           0 :     if (0 > qs)
     383             :     {
     384           0 :       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     385           0 :       if (GNUNET_DB_STATUS_HARD_ERROR == qs)
     386           0 :         *mret = TALER_MHD_reply_with_error (rc->connection,
     387             :                                             MHD_HTTP_INTERNAL_SERVER_ERROR,
     388             :                                             TALER_EC_GENERIC_DB_FETCH_FAILED,
     389             :                                             "get_withdraw_info");
     390           0 :       return true; /* well, kind-of */
     391             :     }
     392           0 :     if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
     393           0 :       return false;
     394             :   }
     395             :   /* generate idempotent reply */
     396           0 :   TEH_METRICS_num_requests[TEH_MT_REQUEST_IDEMPOTENT_BATCH_WITHDRAW]++;
     397           0 :   *mret = generate_reply_success (rc,
     398             :                                   wc);
     399           0 :   return true;
     400             : }
     401             : 
     402             : 
     403             : /**
     404             :  * The request was parsed successfully. Prepare
     405             :  * our side for the main DB transaction.
     406             :  *
     407             :  * @param rc request details
     408             :  * @param wc storage for request processing
     409             :  * @return MHD result for the @a rc
     410             :  */
     411             : static MHD_RESULT
     412           0 : prepare_transaction (const struct TEH_RequestContext *rc,
     413             :                      struct BatchWithdrawContext *wc)
     414             : {
     415             :   /* Note: We could check the reserve balance here,
     416             :      just to be reasonably sure that the reserve has
     417             :      a sufficient balance before doing the "expensive"
     418             :      signatures... */
     419             :   /* Sign before transaction! */
     420           0 :   for (unsigned int i = 0; i<wc->planchets_length; i++)
     421             :   {
     422           0 :     struct PlanchetContext *pc = &wc->planchets[i];
     423             :     enum TALER_ErrorCode ec;
     424             : 
     425           0 :     ec = TEH_keys_denomination_sign_withdraw (
     426           0 :       &pc->collectable.denom_pub_hash,
     427           0 :       &pc->blinded_planchet,
     428             :       &pc->collectable.sig);
     429           0 :     if (TALER_EC_NONE != ec)
     430             :     {
     431           0 :       GNUNET_break (0);
     432           0 :       return TALER_MHD_reply_with_ec (rc->connection,
     433             :                                       ec,
     434             :                                       NULL);
     435             :     }
     436             :   }
     437             : 
     438             :   /* run transaction */
     439             :   {
     440             :     MHD_RESULT mhd_ret;
     441             : 
     442           0 :     if (GNUNET_OK !=
     443           0 :         TEH_DB_run_transaction (rc->connection,
     444             :                                 "run batch withdraw",
     445             :                                 TEH_MT_REQUEST_WITHDRAW,
     446             :                                 &mhd_ret,
     447             :                                 &batch_withdraw_transaction,
     448             :                                 wc))
     449             :     {
     450           0 :       return mhd_ret;
     451             :     }
     452             :   }
     453             :   /* return final positive response */
     454           0 :   return generate_reply_success (rc,
     455             :                                  wc);
     456             : }
     457             : 
     458             : 
     459             : /**
     460             :  * Continue processing the request @a rc by parsing the
     461             :  * @a planchets and then running the transaction.
     462             :  *
     463             :  * @param rc request details
     464             :  * @param wc storage for request processing
     465             :  * @param planchets array of planchets to parse
     466             :  * @return MHD result for the @a rc
     467             :  */
     468             : static MHD_RESULT
     469           0 : parse_planchets (const struct TEH_RequestContext *rc,
     470             :                  struct BatchWithdrawContext *wc,
     471             :                  const json_t *planchets)
     472             : {
     473             :   struct TEH_KeyStateHandle *ksh;
     474             :   MHD_RESULT mret;
     475             : 
     476           0 :   for (unsigned int i = 0; i<wc->planchets_length; i++)
     477             :   {
     478           0 :     struct PlanchetContext *pc = &wc->planchets[i];
     479             :     struct GNUNET_JSON_Specification ispec[] = {
     480           0 :       GNUNET_JSON_spec_fixed_auto ("reserve_sig",
     481             :                                    &pc->collectable.reserve_sig),
     482           0 :       GNUNET_JSON_spec_fixed_auto ("denom_pub_hash",
     483             :                                    &pc->collectable.denom_pub_hash),
     484           0 :       TALER_JSON_spec_blinded_planchet ("coin_ev",
     485             :                                         &pc->blinded_planchet),
     486           0 :       GNUNET_JSON_spec_end ()
     487             :     };
     488             : 
     489             :     {
     490             :       enum GNUNET_GenericReturnValue res;
     491             : 
     492           0 :       res = TALER_MHD_parse_json_data (rc->connection,
     493           0 :                                        json_array_get (planchets,
     494             :                                                        i),
     495             :                                        ispec);
     496           0 :       if (GNUNET_OK != res)
     497           0 :         return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
     498             :     }
     499           0 :     pc->collectable.reserve_pub = *wc->reserve_pub;
     500           0 :     for (unsigned int k = 0; k<i; k++)
     501             :     {
     502           0 :       const struct PlanchetContext *kpc = &wc->planchets[k];
     503             : 
     504           0 :       if (0 ==
     505           0 :           TALER_blinded_planchet_cmp (&kpc->blinded_planchet,
     506           0 :                                       &pc->blinded_planchet))
     507             :       {
     508           0 :         GNUNET_break_op (0);
     509           0 :         return TALER_MHD_reply_with_error (rc->connection,
     510             :                                            MHD_HTTP_BAD_REQUEST,
     511             :                                            TALER_EC_GENERIC_PARAMETER_MALFORMED,
     512             :                                            "duplicate planchet");
     513             :       }
     514             :     }
     515             :   }
     516             : 
     517           0 :   ksh = TEH_keys_get_state ();
     518           0 :   if (NULL == ksh)
     519             :   {
     520           0 :     if (! check_request_idempotent (rc,
     521             :                                     wc,
     522             :                                     &mret))
     523             :     {
     524           0 :       return TALER_MHD_reply_with_error (rc->connection,
     525             :                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
     526             :                                          TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING,
     527             :                                          NULL);
     528             :     }
     529           0 :     return mret;
     530             :   }
     531           0 :   for (unsigned int i = 0; i<wc->planchets_length; i++)
     532             :   {
     533           0 :     struct PlanchetContext *pc = &wc->planchets[i];
     534             :     struct TEH_DenominationKey *dk;
     535             : 
     536           0 :     dk = TEH_keys_denomination_by_hash2 (ksh,
     537           0 :                                          &pc->collectable.denom_pub_hash,
     538             :                                          NULL,
     539             :                                          NULL);
     540           0 :     if (NULL == dk)
     541             :     {
     542           0 :       if (! check_request_idempotent (rc,
     543             :                                       wc,
     544             :                                       &mret))
     545             :       {
     546           0 :         return TEH_RESPONSE_reply_unknown_denom_pub_hash (
     547             :           rc->connection,
     548           0 :           &pc->collectable.denom_pub_hash);
     549             :       }
     550           0 :       return mret;
     551             :     }
     552           0 :     if (GNUNET_TIME_absolute_is_past (dk->meta.expire_withdraw.abs_time))
     553             :     {
     554             :       /* This denomination is past the expiration time for withdraws */
     555           0 :       if (! check_request_idempotent (rc,
     556             :                                       wc,
     557             :                                       &mret))
     558             :       {
     559           0 :         return TEH_RESPONSE_reply_expired_denom_pub_hash (
     560             :           rc->connection,
     561           0 :           &pc->collectable.denom_pub_hash,
     562             :           TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED,
     563             :           "WITHDRAW");
     564             :       }
     565           0 :       return mret;
     566             :     }
     567           0 :     if (GNUNET_TIME_absolute_is_future (dk->meta.start.abs_time))
     568             :     {
     569             :       /* This denomination is not yet valid, no need to check
     570             :          for idempotency! */
     571           0 :       return TEH_RESPONSE_reply_expired_denom_pub_hash (
     572             :         rc->connection,
     573           0 :         &pc->collectable.denom_pub_hash,
     574             :         TALER_EC_EXCHANGE_GENERIC_DENOMINATION_VALIDITY_IN_FUTURE,
     575             :         "WITHDRAW");
     576             :     }
     577           0 :     if (dk->recoup_possible)
     578             :     {
     579             :       /* This denomination has been revoked */
     580           0 :       if (! check_request_idempotent (rc,
     581             :                                       wc,
     582             :                                       &mret))
     583             :       {
     584           0 :         return TEH_RESPONSE_reply_expired_denom_pub_hash (
     585             :           rc->connection,
     586           0 :           &pc->collectable.denom_pub_hash,
     587             :           TALER_EC_EXCHANGE_GENERIC_DENOMINATION_REVOKED,
     588             :           "WITHDRAW");
     589             :       }
     590           0 :       return mret;
     591             :     }
     592           0 :     if (dk->denom_pub.cipher != pc->blinded_planchet.cipher)
     593             :     {
     594             :       /* denomination cipher and blinded planchet cipher not the same */
     595           0 :       GNUNET_break_op (0);
     596           0 :       return TALER_MHD_reply_with_error (rc->connection,
     597             :                                          MHD_HTTP_BAD_REQUEST,
     598             :                                          TALER_EC_EXCHANGE_GENERIC_CIPHER_MISMATCH,
     599             :                                          NULL);
     600             :     }
     601           0 :     if (0 >
     602           0 :         TALER_amount_add (&pc->collectable.amount_with_fee,
     603           0 :                           &dk->meta.value,
     604           0 :                           &dk->meta.fees.withdraw))
     605             :     {
     606           0 :       GNUNET_break (0);
     607           0 :       return TALER_MHD_reply_with_error (rc->connection,
     608             :                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
     609             :                                          TALER_EC_EXCHANGE_WITHDRAW_AMOUNT_FEE_OVERFLOW,
     610             :                                          NULL);
     611             :     }
     612           0 :     if (0 >
     613           0 :         TALER_amount_add (&wc->batch_total,
     614           0 :                           &wc->batch_total,
     615           0 :                           &pc->collectable.amount_with_fee))
     616             :     {
     617           0 :       GNUNET_break (0);
     618           0 :       return TALER_MHD_reply_with_error (rc->connection,
     619             :                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
     620             :                                          TALER_EC_EXCHANGE_WITHDRAW_AMOUNT_FEE_OVERFLOW,
     621             :                                          NULL);
     622             :     }
     623             : 
     624           0 :     if (GNUNET_OK !=
     625           0 :         TALER_coin_ev_hash (&pc->blinded_planchet,
     626           0 :                             &pc->collectable.denom_pub_hash,
     627             :                             &pc->collectable.h_coin_envelope))
     628             :     {
     629           0 :       GNUNET_break (0);
     630           0 :       return TALER_MHD_reply_with_error (rc->connection,
     631             :                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
     632             :                                          TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
     633             :                                          NULL);
     634             :     }
     635           0 :     TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
     636           0 :     if (GNUNET_OK !=
     637           0 :         TALER_wallet_withdraw_verify (&pc->collectable.denom_pub_hash,
     638           0 :                                       &pc->collectable.amount_with_fee,
     639           0 :                                       &pc->collectable.h_coin_envelope,
     640           0 :                                       &pc->collectable.reserve_pub,
     641           0 :                                       &pc->collectable.reserve_sig))
     642             :     {
     643           0 :       GNUNET_break_op (0);
     644           0 :       return TALER_MHD_reply_with_error (rc->connection,
     645             :                                          MHD_HTTP_FORBIDDEN,
     646             :                                          TALER_EC_EXCHANGE_WITHDRAW_RESERVE_SIGNATURE_INVALID,
     647             :                                          NULL);
     648             :     }
     649             :   }
     650             :   /* everything parsed */
     651           0 :   return prepare_transaction (rc,
     652             :                               wc);
     653             : }
     654             : 
     655             : 
     656             : MHD_RESULT
     657           0 : TEH_handler_batch_withdraw (struct TEH_RequestContext *rc,
     658             :                             const struct TALER_ReservePublicKeyP *reserve_pub,
     659             :                             const json_t *root)
     660             : {
     661             :   struct BatchWithdrawContext wc;
     662             :   json_t *planchets;
     663             :   struct GNUNET_JSON_Specification spec[] = {
     664           0 :     GNUNET_JSON_spec_json ("planchets",
     665             :                            &planchets),
     666           0 :     GNUNET_JSON_spec_end ()
     667             :   };
     668             : 
     669           0 :   memset (&wc,
     670             :           0,
     671             :           sizeof (wc));
     672           0 :   GNUNET_assert (GNUNET_OK ==
     673             :                  TALER_amount_set_zero (TEH_currency,
     674             :                                         &wc.batch_total));
     675           0 :   wc.reserve_pub = reserve_pub;
     676             :   {
     677             :     enum GNUNET_GenericReturnValue res;
     678             : 
     679           0 :     res = TALER_MHD_parse_json_data (rc->connection,
     680             :                                      root,
     681             :                                      spec);
     682           0 :     if (GNUNET_OK != res)
     683           0 :       return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
     684             :   }
     685           0 :   if ( (! json_is_array (planchets)) ||
     686           0 :        (0 == json_array_size (planchets)) )
     687             :   {
     688           0 :     GNUNET_JSON_parse_free (spec);
     689           0 :     GNUNET_break_op (0);
     690           0 :     return TALER_MHD_reply_with_error (rc->connection,
     691             :                                        MHD_HTTP_BAD_REQUEST,
     692             :                                        TALER_EC_GENERIC_PARAMETER_MALFORMED,
     693             :                                        "planchets");
     694             :   }
     695           0 :   wc.planchets_length = json_array_size (planchets);
     696           0 :   if (wc.planchets_length > TALER_MAX_FRESH_COINS)
     697             :   {
     698           0 :     GNUNET_JSON_parse_free (spec);
     699           0 :     GNUNET_break_op (0);
     700           0 :     return TALER_MHD_reply_with_error (rc->connection,
     701             :                                        MHD_HTTP_BAD_REQUEST,
     702             :                                        TALER_EC_GENERIC_PARAMETER_MALFORMED,
     703             :                                        "too many planchets");
     704             :   }
     705           0 :   {
     706           0 :     struct PlanchetContext splanchets[wc.planchets_length];
     707             :     MHD_RESULT ret;
     708             : 
     709           0 :     memset (splanchets,
     710             :             0,
     711             :             sizeof (splanchets));
     712           0 :     wc.planchets = splanchets;
     713           0 :     ret = parse_planchets (rc,
     714             :                            &wc,
     715             :                            planchets);
     716             :     /* Clean up */
     717           0 :     for (unsigned int i = 0; i<wc.planchets_length; i++)
     718             :     {
     719           0 :       struct PlanchetContext *pc = &wc.planchets[i];
     720             : 
     721           0 :       TALER_blinded_planchet_free (&pc->blinded_planchet);
     722           0 :       TALER_blinded_denom_sig_free (&pc->collectable.sig);
     723             :     }
     724           0 :     GNUNET_JSON_parse_free (spec);
     725           0 :     return ret;
     726             :   }
     727             : }
     728             : 
     729             : 
     730             : /* end of taler-exchange-httpd_batch-withdraw.c */

Generated by: LCOV version 1.14