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_purses_create.c (23679B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2022 Taler Systems SA
      4 
      5   TALER is free software; you can redistribute it and/or modify it under the
      6   terms of the GNU Affero General Public License as published by the Free Software
      7   Foundation; either version 3, or (at your option) any later version.
      8 
      9   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
     10   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
     11   A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more details.
     12 
     13   You should have received a copy of the GNU Affero General Public License along with
     14   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
     15 */
     16 /**
     17  * @file taler-exchange-httpd_purses_create.c
     18  * @brief Handle /purses/$PID/create requests; parses the POST and JSON and
     19  *        verifies the coin signature before handing things off
     20  *        to the database.
     21  * @author Christian Grothoff
     22  */
     23 #include "taler/platform.h"
     24 #include <gnunet/gnunet_util_lib.h>
     25 #include <gnunet/gnunet_json_lib.h>
     26 #include <jansson.h>
     27 #include <microhttpd.h>
     28 #include "taler/taler_json_lib.h"
     29 #include "taler/taler_mhd_lib.h"
     30 #include "taler-exchange-httpd_common_deposit.h"
     31 #include "taler-exchange-httpd_purses_create.h"
     32 #include "taler-exchange-httpd_responses.h"
     33 #include "taler/taler_exchangedb_lib.h"
     34 #include "taler-exchange-httpd_keys.h"
     35 
     36 
     37 /**
     38  * Closure for #create_transaction.
     39  */
     40 struct PurseCreateContext
     41 {
     42 
     43   /**
     44    * Total actually deposited by all the coins.
     45    */
     46   struct TALER_Amount deposit_total;
     47 
     48   /**
     49    * Our current time.
     50    */
     51   struct GNUNET_TIME_Timestamp exchange_timestamp;
     52 
     53   /**
     54    * Merge key for the purse.
     55    */
     56   struct TALER_PurseMergePublicKeyP merge_pub;
     57 
     58   /**
     59    * Encrypted contract of for the purse.
     60    */
     61   struct TALER_EncryptedContract econtract;
     62 
     63   /**
     64    * Signature of the client affiming this request.
     65    */
     66   struct TALER_PurseContractSignatureP purse_sig;
     67 
     68   /**
     69    * Fundamental details about the purse.
     70    */
     71   struct TEH_PurseDetails pd;
     72 
     73   /**
     74    * Array of coins being deposited.
     75    */
     76   struct TEH_PurseDepositedCoin *coins;
     77 
     78   /**
     79    * Length of the @e coins array.
     80    */
     81   unsigned int num_coins;
     82 
     83   /**
     84    * Minimum age for deposits into this purse.
     85    */
     86   uint32_t min_age;
     87 
     88   /**
     89    * Do we have an @e econtract?
     90    */
     91   bool no_econtract;
     92 
     93 };
     94 
     95 
     96 /**
     97  * Execute database transaction for /purses/$PID/create.  Runs the transaction
     98  * logic; IF it returns a non-error code, the transaction logic MUST NOT queue
     99  * a MHD response.  IF it returns an hard error, the transaction logic MUST
    100  * queue a MHD response and set @a mhd_ret.  IF it returns the soft error
    101  * code, the function MAY be called again to retry and MUST not queue a MHD
    102  * response.
    103  *
    104  * @param cls a `struct PurseCreateContext`
    105  * @param connection MHD request context
    106  * @param[out] mhd_ret set to MHD status on error
    107  * @return transaction status
    108  */
    109 static enum GNUNET_DB_QueryStatus
    110 create_transaction (void *cls,
    111                     struct MHD_Connection *connection,
    112                     MHD_RESULT *mhd_ret)
    113 {
    114   struct PurseCreateContext *pcc = cls;
    115   enum GNUNET_DB_QueryStatus qs;
    116   struct TALER_Amount purse_fee;
    117   bool in_conflict = true;
    118 
    119   GNUNET_assert (GNUNET_OK ==
    120                  TALER_amount_set_zero (TEH_currency,
    121                                         &purse_fee));
    122   /* 1) create purse */
    123   qs = TEH_plugin->insert_purse_request (
    124     TEH_plugin->cls,
    125     &pcc->pd.purse_pub,
    126     &pcc->merge_pub,
    127     pcc->pd.purse_expiration,
    128     &pcc->pd.h_contract_terms,
    129     pcc->min_age,
    130     TALER_WAMF_MODE_MERGE_FULLY_PAID_PURSE,
    131     &purse_fee,
    132     &pcc->pd.target_amount,
    133     &pcc->purse_sig,
    134     &in_conflict);
    135   if (qs < 0)
    136   {
    137     if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
    138       return qs;
    139     TALER_LOG_WARNING (
    140       "Failed to store create purse information in database\n");
    141     *mhd_ret =
    142       TALER_MHD_reply_with_error (connection,
    143                                   MHD_HTTP_INTERNAL_SERVER_ERROR,
    144                                   TALER_EC_GENERIC_DB_STORE_FAILED,
    145                                   "purse create");
    146     return GNUNET_DB_STATUS_HARD_ERROR;
    147   }
    148   if (in_conflict)
    149   {
    150     struct TALER_PurseMergePublicKeyP merge_pub;
    151     struct GNUNET_TIME_Timestamp purse_expiration;
    152     struct TALER_PrivateContractHashP h_contract_terms;
    153     struct TALER_Amount target_amount;
    154     struct TALER_Amount balance;
    155     struct TALER_PurseContractSignatureP purse_sig;
    156     uint32_t min_age;
    157 
    158     TEH_plugin->rollback (TEH_plugin->cls);
    159     qs = TEH_plugin->get_purse_request (TEH_plugin->cls,
    160                                         &pcc->pd.purse_pub,
    161                                         &merge_pub,
    162                                         &purse_expiration,
    163                                         &h_contract_terms,
    164                                         &min_age,
    165                                         &target_amount,
    166                                         &balance,
    167                                         &purse_sig);
    168     if (qs < 0)
    169     {
    170       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
    171       TALER_LOG_WARNING ("Failed to fetch purse information from database\n");
    172       *mhd_ret = TALER_MHD_reply_with_error (connection,
    173                                              MHD_HTTP_INTERNAL_SERVER_ERROR,
    174                                              TALER_EC_GENERIC_DB_FETCH_FAILED,
    175                                              "select purse request");
    176       return GNUNET_DB_STATUS_HARD_ERROR;
    177     }
    178     *mhd_ret
    179       = TALER_MHD_REPLY_JSON_PACK (
    180           connection,
    181           MHD_HTTP_CONFLICT,
    182           TALER_JSON_pack_ec (
    183             TALER_EC_EXCHANGE_PURSE_CREATE_CONFLICTING_META_DATA),
    184           TALER_JSON_pack_amount ("amount",
    185                                   &target_amount),
    186           GNUNET_JSON_pack_uint64 ("min_age",
    187                                    min_age),
    188           GNUNET_JSON_pack_timestamp ("purse_expiration",
    189                                       purse_expiration),
    190           GNUNET_JSON_pack_data_auto ("purse_sig",
    191                                       &purse_sig),
    192           GNUNET_JSON_pack_data_auto ("h_contract_terms",
    193                                       &h_contract_terms),
    194           GNUNET_JSON_pack_data_auto ("merge_pub",
    195                                       &merge_pub));
    196     return GNUNET_DB_STATUS_HARD_ERROR;
    197   }
    198   /* 2) deposit all coins */
    199   for (unsigned int i = 0; i<pcc->num_coins; i++)
    200   {
    201     struct TEH_PurseDepositedCoin *coin = &pcc->coins[i];
    202     bool balance_ok = false;
    203     bool conflict = true;
    204     bool too_late = true;
    205 
    206     qs = TEH_make_coin_known (&coin->cpi,
    207                               connection,
    208                               &coin->known_coin_id,
    209                               mhd_ret);
    210     if (qs < 0)
    211       return qs;
    212     qs = TEH_plugin->do_purse_deposit (TEH_plugin->cls,
    213                                        &pcc->pd.purse_pub,
    214                                        &coin->cpi.coin_pub,
    215                                        &coin->amount,
    216                                        &coin->coin_sig,
    217                                        &coin->amount_minus_fee,
    218                                        &balance_ok,
    219                                        &too_late,
    220                                        &conflict);
    221     if (qs <= 0)
    222     {
    223       if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
    224         return qs;
    225       GNUNET_break (0 != qs);
    226       TALER_LOG_WARNING (
    227         "Failed to store purse deposit information in database\n");
    228       *mhd_ret = TALER_MHD_reply_with_error (connection,
    229                                              MHD_HTTP_INTERNAL_SERVER_ERROR,
    230                                              TALER_EC_GENERIC_DB_STORE_FAILED,
    231                                              "purse create deposit");
    232       return GNUNET_DB_STATUS_HARD_ERROR;
    233     }
    234     if (! balance_ok)
    235     {
    236       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    237                   "Coin %s has insufficient balance for purse deposit of amount %s\n",
    238                   TALER_B2S (&coin->cpi.coin_pub),
    239                   TALER_amount2s (&coin->amount));
    240       *mhd_ret
    241         = TEH_RESPONSE_reply_coin_insufficient_funds (
    242             connection,
    243             TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS,
    244             &coin->cpi.denom_pub_hash,
    245             &coin->cpi.coin_pub);
    246       return GNUNET_DB_STATUS_HARD_ERROR;
    247     }
    248     if (too_late)
    249     {
    250       *mhd_ret
    251         = TALER_MHD_reply_with_ec (
    252             connection,
    253             TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
    254             "too late to deposit on purse creation");
    255       return GNUNET_DB_STATUS_HARD_ERROR;
    256     }
    257     if (conflict)
    258     {
    259       struct TALER_Amount amount;
    260       struct TALER_CoinSpendPublicKeyP coin_pub;
    261       struct TALER_CoinSpendSignatureP coin_sig;
    262       struct TALER_DenominationHashP h_denom_pub;
    263       struct TALER_AgeCommitmentHashP phac;
    264       char *partner_url = NULL;
    265 
    266       TEH_plugin->rollback (TEH_plugin->cls);
    267       qs = TEH_plugin->get_purse_deposit (TEH_plugin->cls,
    268                                           &pcc->pd.purse_pub,
    269                                           &coin->cpi.coin_pub,
    270                                           &amount,
    271                                           &h_denom_pub,
    272                                           &phac,
    273                                           &coin_sig,
    274                                           &partner_url);
    275       if (qs < 0)
    276       {
    277         GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
    278         TALER_LOG_WARNING (
    279           "Failed to fetch purse deposit information from database\n");
    280         *mhd_ret = TALER_MHD_reply_with_error (connection,
    281                                                MHD_HTTP_INTERNAL_SERVER_ERROR,
    282                                                TALER_EC_GENERIC_DB_FETCH_FAILED,
    283                                                "get purse deposit");
    284         return GNUNET_DB_STATUS_HARD_ERROR;
    285       }
    286 
    287       *mhd_ret
    288         = TALER_MHD_REPLY_JSON_PACK (
    289             connection,
    290             MHD_HTTP_CONFLICT,
    291             TALER_JSON_pack_ec (
    292               TALER_EC_EXCHANGE_PURSE_DEPOSIT_CONFLICTING_META_DATA),
    293             GNUNET_JSON_pack_data_auto ("coin_pub",
    294                                         &coin_pub),
    295             GNUNET_JSON_pack_data_auto ("coin_sig",
    296                                         &coin_sig),
    297             GNUNET_JSON_pack_data_auto ("h_denom_pub",
    298                                         &h_denom_pub),
    299             GNUNET_JSON_pack_data_auto ("h_age_restrictions",
    300                                         &phac),
    301             GNUNET_JSON_pack_allow_null (
    302               GNUNET_JSON_pack_string ("partner_url",
    303                                        partner_url)),
    304             TALER_JSON_pack_amount ("amount",
    305                                     &amount));
    306       GNUNET_free (partner_url);
    307       return GNUNET_DB_STATUS_HARD_ERROR;
    308     }
    309   }
    310   /* 3) if present, persist contract */
    311   if (! pcc->no_econtract)
    312   {
    313     in_conflict = true;
    314     qs = TEH_plugin->insert_contract (TEH_plugin->cls,
    315                                       &pcc->pd.purse_pub,
    316                                       &pcc->econtract,
    317                                       &in_conflict);
    318     if (qs < 0)
    319     {
    320       if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
    321         return qs;
    322       TALER_LOG_WARNING ("Failed to store purse information in database\n");
    323       *mhd_ret = TALER_MHD_reply_with_error (connection,
    324                                              MHD_HTTP_INTERNAL_SERVER_ERROR,
    325                                              TALER_EC_GENERIC_DB_STORE_FAILED,
    326                                              "purse create contract");
    327       return GNUNET_DB_STATUS_HARD_ERROR;
    328     }
    329     if (in_conflict)
    330     {
    331       struct TALER_EncryptedContract econtract;
    332       struct GNUNET_HashCode h_econtract;
    333 
    334       qs = TEH_plugin->select_contract_by_purse (
    335         TEH_plugin->cls,
    336         &pcc->pd.purse_pub,
    337         &econtract);
    338       if (qs <= 0)
    339       {
    340         if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
    341           return qs;
    342         GNUNET_break (0 != qs);
    343         TALER_LOG_WARNING (
    344           "Failed to store fetch contract information from database\n");
    345         *mhd_ret = TALER_MHD_reply_with_error (connection,
    346                                                MHD_HTTP_INTERNAL_SERVER_ERROR,
    347                                                TALER_EC_GENERIC_DB_FETCH_FAILED,
    348                                                "select contract");
    349         return GNUNET_DB_STATUS_HARD_ERROR;
    350       }
    351       GNUNET_CRYPTO_hash (econtract.econtract,
    352                           econtract.econtract_size,
    353                           &h_econtract);
    354       *mhd_ret
    355         = TALER_MHD_REPLY_JSON_PACK (
    356             connection,
    357             MHD_HTTP_CONFLICT,
    358             TALER_JSON_pack_ec (
    359               TALER_EC_EXCHANGE_PURSE_ECONTRACT_CONFLICTING_META_DATA),
    360             GNUNET_JSON_pack_data_auto ("h_econtract",
    361                                         &h_econtract),
    362             GNUNET_JSON_pack_data_auto ("econtract_sig",
    363                                         &econtract.econtract_sig),
    364             GNUNET_JSON_pack_data_auto ("contract_pub",
    365                                         &econtract.contract_pub));
    366       GNUNET_free (econtract.econtract);
    367       return GNUNET_DB_STATUS_HARD_ERROR;
    368     }
    369   }
    370   return qs;
    371 }
    372 
    373 
    374 /**
    375  * Parse a coin and check signature of the coin and the denomination
    376  * signature over the coin.
    377  *
    378  * @param[in,out] connection our HTTP connection
    379  * @param[in,out] pcc request context
    380  * @param[out] coin coin to initialize
    381  * @param jcoin coin to parse
    382  * @return #GNUNET_OK on success, #GNUNET_NO if an error was returned,
    383  *         #GNUNET_SYSERR on failure and no error could be returned
    384  */
    385 static enum GNUNET_GenericReturnValue
    386 parse_coin (struct MHD_Connection *connection,
    387             struct PurseCreateContext *pcc,
    388             struct TEH_PurseDepositedCoin *coin,
    389             const json_t *jcoin)
    390 {
    391   enum GNUNET_GenericReturnValue iret;
    392 
    393   if (GNUNET_OK !=
    394       (iret = TEH_common_purse_deposit_parse_coin (connection,
    395                                                    coin,
    396                                                    jcoin)))
    397     return iret;
    398   if (GNUNET_OK !=
    399       (iret = TEH_common_deposit_check_purse_deposit (
    400          connection,
    401          coin,
    402          &pcc->pd.purse_pub,
    403          pcc->min_age)))
    404     return iret;
    405   if (0 >
    406       TALER_amount_add (&pcc->deposit_total,
    407                         &pcc->deposit_total,
    408                         &coin->amount_minus_fee))
    409   {
    410     GNUNET_break (0);
    411     return (MHD_YES ==
    412             TALER_MHD_reply_with_error (connection,
    413                                         MHD_HTTP_INTERNAL_SERVER_ERROR,
    414                                         TALER_EC_GENERIC_FAILED_COMPUTE_AMOUNT,
    415                                         "total deposit contribution"))
    416            ? GNUNET_NO
    417            : GNUNET_SYSERR;
    418   }
    419   return GNUNET_OK;
    420 }
    421 
    422 
    423 MHD_RESULT
    424 TEH_handler_purses_create (
    425   struct TEH_RequestContext *rc,
    426   const struct TALER_PurseContractPublicKeyP *purse_pub,
    427   const json_t *root)
    428 {
    429   struct MHD_Connection *connection = rc->connection;
    430   struct PurseCreateContext pcc = {
    431     .pd.purse_pub = *purse_pub,
    432     .exchange_timestamp = GNUNET_TIME_timestamp_get ()
    433   };
    434   const json_t *deposits;
    435   json_t *deposit;
    436   unsigned int idx;
    437   struct GNUNET_JSON_Specification spec[] = {
    438     TALER_JSON_spec_amount ("amount",
    439                             TEH_currency,
    440                             &pcc.pd.target_amount),
    441     GNUNET_JSON_spec_uint32 ("min_age",
    442                              &pcc.min_age),
    443     GNUNET_JSON_spec_mark_optional (
    444       TALER_JSON_spec_econtract ("econtract",
    445                                  &pcc.econtract),
    446       &pcc.no_econtract),
    447     GNUNET_JSON_spec_fixed_auto ("merge_pub",
    448                                  &pcc.merge_pub),
    449     GNUNET_JSON_spec_fixed_auto ("purse_sig",
    450                                  &pcc.purse_sig),
    451     GNUNET_JSON_spec_fixed_auto ("h_contract_terms",
    452                                  &pcc.pd.h_contract_terms),
    453     GNUNET_JSON_spec_array_const ("deposits",
    454                                   &deposits),
    455     GNUNET_JSON_spec_timestamp ("purse_expiration",
    456                                 &pcc.pd.purse_expiration),
    457     GNUNET_JSON_spec_end ()
    458   };
    459   const struct TEH_GlobalFee *gf;
    460 
    461   {
    462     enum GNUNET_GenericReturnValue res;
    463 
    464     res = TALER_MHD_parse_json_data (connection,
    465                                      root,
    466                                      spec);
    467     if (GNUNET_SYSERR == res)
    468     {
    469       GNUNET_break (0);
    470       return MHD_NO; /* hard failure */
    471     }
    472     if (GNUNET_NO == res)
    473     {
    474       GNUNET_break_op (0);
    475       return MHD_YES; /* failure */
    476     }
    477   }
    478   GNUNET_assert (GNUNET_OK ==
    479                  TALER_amount_set_zero (TEH_currency,
    480                                         &pcc.deposit_total));
    481   if (GNUNET_TIME_timestamp_cmp (pcc.pd.purse_expiration,
    482                                  <,
    483                                  pcc.exchange_timestamp))
    484   {
    485     GNUNET_break_op (0);
    486     GNUNET_JSON_parse_free (spec);
    487     return TALER_MHD_reply_with_error (connection,
    488                                        MHD_HTTP_BAD_REQUEST,
    489                                        TALER_EC_EXCHANGE_PURSE_CREATE_EXPIRATION_BEFORE_NOW,
    490                                        NULL);
    491   }
    492   if (GNUNET_TIME_absolute_is_never (pcc.pd.purse_expiration.abs_time))
    493   {
    494     GNUNET_break_op (0);
    495     GNUNET_JSON_parse_free (spec);
    496     return TALER_MHD_reply_with_error (connection,
    497                                        MHD_HTTP_BAD_REQUEST,
    498                                        TALER_EC_EXCHANGE_PURSE_CREATE_EXPIRATION_IS_NEVER,
    499                                        NULL);
    500   }
    501   pcc.num_coins = json_array_size (deposits);
    502   if ( (0 == pcc.num_coins) ||
    503        (pcc.num_coins > TALER_MAX_COINS) )
    504   {
    505     GNUNET_break_op (0);
    506     GNUNET_JSON_parse_free (spec);
    507     return TALER_MHD_reply_with_error (connection,
    508                                        MHD_HTTP_BAD_REQUEST,
    509                                        TALER_EC_GENERIC_PARAMETER_MALFORMED,
    510                                        "deposits");
    511   }
    512   {
    513     struct TEH_KeyStateHandle *keys;
    514 
    515     keys = TEH_keys_get_state ();
    516     if (NULL == keys)
    517     {
    518       GNUNET_break (0);
    519       GNUNET_JSON_parse_free (spec);
    520       return TALER_MHD_reply_with_error (connection,
    521                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
    522                                          TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING,
    523                                          NULL);
    524     }
    525     gf = TEH_keys_global_fee_by_time (keys,
    526                                       pcc.exchange_timestamp);
    527   }
    528   if (NULL == gf)
    529   {
    530     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    531                 "Cannot create purse: global fees not configured!\n");
    532     GNUNET_JSON_parse_free (spec);
    533     return TALER_MHD_reply_with_error (connection,
    534                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
    535                                        TALER_EC_EXCHANGE_GENERIC_GLOBAL_FEES_MISSING,
    536                                        NULL);
    537   }
    538   /* parse deposits */
    539   pcc.coins = GNUNET_new_array (pcc.num_coins,
    540                                 struct TEH_PurseDepositedCoin);
    541   json_array_foreach (deposits, idx, deposit)
    542   {
    543     enum GNUNET_GenericReturnValue res;
    544     struct TEH_PurseDepositedCoin *coin = &pcc.coins[idx];
    545 
    546     res = parse_coin (connection,
    547                       &pcc,
    548                       coin,
    549                       deposit);
    550     if (GNUNET_OK != res)
    551     {
    552       for (unsigned int i = 0; i<idx; i++)
    553         TEH_common_purse_deposit_free_coin (&pcc.coins[i]);
    554       GNUNET_free (pcc.coins);
    555       GNUNET_JSON_parse_free (spec);
    556       return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
    557     }
    558   }
    559 
    560   if (0 < TALER_amount_cmp (&gf->fees.purse,
    561                             &pcc.deposit_total))
    562   {
    563     GNUNET_break_op (0);
    564     GNUNET_free (pcc.coins);
    565     GNUNET_JSON_parse_free (spec);
    566     return TALER_MHD_reply_with_error (connection,
    567                                        MHD_HTTP_BAD_REQUEST,
    568                                        TALER_EC_EXCHANGE_CREATE_PURSE_NEGATIVE_VALUE_AFTER_FEE,
    569                                        NULL);
    570   }
    571   TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
    572 
    573   if (GNUNET_OK !=
    574       TALER_wallet_purse_create_verify (
    575         pcc.pd.purse_expiration,
    576         &pcc.pd.h_contract_terms,
    577         &pcc.merge_pub,
    578         pcc.min_age,
    579         &pcc.pd.target_amount,
    580         &pcc.pd.purse_pub,
    581         &pcc.purse_sig))
    582   {
    583     TALER_LOG_WARNING ("Invalid signature on /purses/$PID/create request\n");
    584     for (unsigned int i = 0; i<pcc.num_coins; i++)
    585       TEH_common_purse_deposit_free_coin (&pcc.coins[i]);
    586     GNUNET_free (pcc.coins);
    587     GNUNET_JSON_parse_free (spec);
    588     return TALER_MHD_reply_with_error (connection,
    589                                        MHD_HTTP_FORBIDDEN,
    590                                        TALER_EC_EXCHANGE_PURSE_CREATE_SIGNATURE_INVALID,
    591                                        NULL);
    592   }
    593   if ( (! pcc.no_econtract) &&
    594        (GNUNET_OK !=
    595         TALER_wallet_econtract_upload_verify (pcc.econtract.econtract,
    596                                               pcc.econtract.econtract_size,
    597                                               &pcc.econtract.contract_pub,
    598                                               purse_pub,
    599                                               &pcc.econtract.econtract_sig)) )
    600   {
    601     TALER_LOG_WARNING ("Invalid signature on /purses/$PID/create request\n");
    602     for (unsigned int i = 0; i<pcc.num_coins; i++)
    603       TEH_common_purse_deposit_free_coin (&pcc.coins[i]);
    604     GNUNET_free (pcc.coins);
    605     GNUNET_JSON_parse_free (spec);
    606     return TALER_MHD_reply_with_error (connection,
    607                                        MHD_HTTP_FORBIDDEN,
    608                                        TALER_EC_EXCHANGE_PURSE_ECONTRACT_SIGNATURE_INVALID,
    609                                        NULL);
    610   }
    611 
    612 
    613   if (GNUNET_SYSERR ==
    614       TEH_plugin->preflight (TEH_plugin->cls))
    615   {
    616     GNUNET_break (0);
    617     for (unsigned int i = 0; i<pcc.num_coins; i++)
    618       TEH_common_purse_deposit_free_coin (&pcc.coins[i]);
    619     GNUNET_free (pcc.coins);
    620     GNUNET_JSON_parse_free (spec);
    621     return TALER_MHD_reply_with_error (connection,
    622                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
    623                                        TALER_EC_GENERIC_DB_START_FAILED,
    624                                        "preflight failure");
    625   }
    626 
    627   /* execute transaction */
    628   {
    629     MHD_RESULT mhd_ret;
    630 
    631     if (GNUNET_OK !=
    632         TEH_DB_run_transaction (connection,
    633                                 "execute purse create",
    634                                 TEH_MT_REQUEST_PURSE_CREATE,
    635                                 &mhd_ret,
    636                                 &create_transaction,
    637                                 &pcc))
    638     {
    639       for (unsigned int i = 0; i<pcc.num_coins; i++)
    640         TEH_common_purse_deposit_free_coin (&pcc.coins[i]);
    641       GNUNET_free (pcc.coins);
    642       GNUNET_JSON_parse_free (spec);
    643       return mhd_ret;
    644     }
    645   }
    646 
    647   /* generate regular response */
    648   {
    649     MHD_RESULT res;
    650 
    651     res = TEH_RESPONSE_reply_purse_created (connection,
    652                                             pcc.exchange_timestamp,
    653                                             &pcc.deposit_total,
    654                                             &pcc.pd);
    655     for (unsigned int i = 0; i<pcc.num_coins; i++)
    656       TEH_common_purse_deposit_free_coin (&pcc.coins[i]);
    657     GNUNET_free (pcc.coins);
    658     GNUNET_JSON_parse_free (spec);
    659     return res;
    660   }
    661 }
    662 
    663 
    664 /* end of taler-exchange-httpd_purses_create.c */