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

          Line data    Source code
       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_reserves_purse.c
      18             :  * @brief Handle /reserves/$RID/purse 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 "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 <pthread.h>
      29             : #include "taler_json_lib.h"
      30             : #include "taler_kyclogic_lib.h"
      31             : #include "taler_mhd_lib.h"
      32             : #include "taler-exchange-httpd_reserves_purse.h"
      33             : #include "taler-exchange-httpd_responses.h"
      34             : #include "taler_exchangedb_lib.h"
      35             : #include "taler-exchange-httpd_keys.h"
      36             : 
      37             : 
      38             : /**
      39             :  * Closure for #purse_transaction.
      40             :  */
      41             : struct ReservePurseContext
      42             : {
      43             : 
      44             :   /**
      45             :    * Public key of the reserve we are creating a purse for.
      46             :    */
      47             :   const struct TALER_ReservePublicKeyP *reserve_pub;
      48             : 
      49             :   /**
      50             :    * Fees for the operation.
      51             :    */
      52             :   const struct TEH_GlobalFee *gf;
      53             : 
      54             :   /**
      55             :    * Signature of the reserve affirming the merge.
      56             :    */
      57             :   struct TALER_ReserveSignatureP reserve_sig;
      58             : 
      59             :   /**
      60             :    * Purse fee the client is willing to pay.
      61             :    */
      62             :   struct TALER_Amount purse_fee;
      63             : 
      64             :   /**
      65             :    * Total amount already put into the purse.
      66             :    */
      67             :   struct TALER_Amount deposit_total;
      68             : 
      69             :   /**
      70             :    * Merge time.
      71             :    */
      72             :   struct GNUNET_TIME_Timestamp merge_timestamp;
      73             : 
      74             :   /**
      75             :    * Our current time.
      76             :    */
      77             :   struct GNUNET_TIME_Timestamp exchange_timestamp;
      78             : 
      79             :   /**
      80             :    * Details about an encrypted contract, if any.
      81             :    */
      82             :   struct TALER_EncryptedContract econtract;
      83             : 
      84             :   /**
      85             :    * Merge key for the purse.
      86             :    */
      87             :   struct TALER_PurseMergePublicKeyP merge_pub;
      88             : 
      89             :   /**
      90             :    * Merge affirmation by the @e merge_pub.
      91             :    */
      92             :   struct TALER_PurseMergeSignatureP merge_sig;
      93             : 
      94             :   /**
      95             :    * Signature of the client affiming this request.
      96             :    */
      97             :   struct TALER_PurseContractSignatureP purse_sig;
      98             : 
      99             :   /**
     100             :    * Fundamental details about the purse.
     101             :    */
     102             :   struct TEH_PurseDetails pd;
     103             : 
     104             :   /**
     105             :    * Hash of the @e payto_uri.
     106             :    */
     107             :   struct TALER_PaytoHashP h_payto;
     108             : 
     109             :   /**
     110             :    * KYC status of the operation.
     111             :    */
     112             :   struct TALER_EXCHANGEDB_KycStatus kyc;
     113             : 
     114             :   /**
     115             :    * Minimum age for deposits into this purse.
     116             :    */
     117             :   uint32_t min_age;
     118             : 
     119             :   /**
     120             :    * Flags for the operation.
     121             :    */
     122             :   enum TALER_WalletAccountMergeFlags flags;
     123             : 
     124             :   /**
     125             :    * Do we lack an @e econtract?
     126             :    */
     127             :   bool no_econtract;
     128             : 
     129             : };
     130             : 
     131             : 
     132             : /**
     133             :  * Function called to iterate over KYC-relevant
     134             :  * transaction amounts for a particular time range.
     135             :  * Called within a database transaction, so must
     136             :  * not start a new one.
     137             :  *
     138             :  * @param cls a `struct ReservePurseContext`
     139             :  * @param limit maximum time-range for which events
     140             :  *        should be fetched (timestamp in the past)
     141             :  * @param cb function to call on each event found,
     142             :  *        events must be returned in reverse chronological
     143             :  *        order
     144             :  * @param cb_cls closure for @a cb
     145             :  */
     146             : static void
     147           0 : amount_iterator (void *cls,
     148             :                  struct GNUNET_TIME_Absolute limit,
     149             :                  TALER_EXCHANGEDB_KycAmountCallback cb,
     150             :                  void *cb_cls)
     151             : {
     152           0 :   struct ReservePurseContext *rpc = cls;
     153             :   enum GNUNET_DB_QueryStatus qs;
     154             : 
     155           0 :   cb (cb_cls,
     156           0 :       &rpc->deposit_total,
     157             :       GNUNET_TIME_absolute_get ());
     158           0 :   qs = TEH_plugin->select_merge_amounts_for_kyc_check (
     159           0 :     TEH_plugin->cls,
     160           0 :     &rpc->h_payto,
     161             :     limit,
     162             :     cb,
     163             :     cb_cls);
     164           0 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     165             :               "Got %d additional transactions for this merge and limit %llu\n",
     166             :               qs,
     167             :               (unsigned long long) limit.abs_value_us);
     168           0 :   GNUNET_break (qs >= 0);
     169           0 : }
     170             : 
     171             : 
     172             : /**
     173             :  * Execute database transaction for /reserves/$PID/purse.  Runs the transaction
     174             :  * logic; IF it returns a non-error code, the transaction logic MUST NOT queue
     175             :  * a MHD response.  IF it returns an hard error, the transaction logic MUST
     176             :  * queue a MHD response and set @a mhd_ret.  IF it returns the soft error
     177             :  * code, the function MAY be called again to retry and MUST not queue a MHD
     178             :  * response.
     179             :  *
     180             :  * @param cls a `struct ReservePurseContext`
     181             :  * @param connection MHD request context
     182             :  * @param[out] mhd_ret set to MHD status on error
     183             :  * @return transaction status
     184             :  */
     185             : static enum GNUNET_DB_QueryStatus
     186           0 : purse_transaction (void *cls,
     187             :                    struct MHD_Connection *connection,
     188             :                    MHD_RESULT *mhd_ret)
     189             : {
     190           0 :   struct ReservePurseContext *rpc = cls;
     191             :   enum GNUNET_DB_QueryStatus qs;
     192             : 
     193             :   const char *required;
     194             : 
     195           0 :   required = TALER_KYCLOGIC_kyc_test_required (
     196             :     TALER_KYCLOGIC_KYC_TRIGGER_P2P_RECEIVE,
     197           0 :     &rpc->h_payto,
     198           0 :     TEH_plugin->select_satisfied_kyc_processes,
     199           0 :     TEH_plugin->cls,
     200             :     &amount_iterator,
     201             :     rpc);
     202           0 :   if (NULL != required)
     203             :   {
     204           0 :     rpc->kyc.ok = false;
     205           0 :     return TEH_plugin->insert_kyc_requirement_for_account (
     206           0 :       TEH_plugin->cls,
     207             :       required,
     208           0 :       &rpc->h_payto,
     209             :       &rpc->kyc.requirement_row);
     210             :   }
     211           0 :   rpc->kyc.ok = true;
     212             : 
     213             :   {
     214           0 :     bool in_conflict = true;
     215             : 
     216             :     /* 1) store purse */
     217           0 :     qs = TEH_plugin->insert_purse_request (
     218           0 :       TEH_plugin->cls,
     219           0 :       &rpc->pd.purse_pub,
     220           0 :       &rpc->merge_pub,
     221             :       rpc->pd.purse_expiration,
     222           0 :       &rpc->pd.h_contract_terms,
     223             :       rpc->min_age,
     224             :       rpc->flags,
     225           0 :       &rpc->purse_fee,
     226           0 :       &rpc->pd.target_amount,
     227           0 :       &rpc->purse_sig,
     228             :       &in_conflict);
     229           0 :     if (qs < 0)
     230             :     {
     231           0 :       if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
     232           0 :         return qs;
     233           0 :       TALER_LOG_WARNING (
     234             :         "Failed to store purse purse information in database\n");
     235           0 :       *mhd_ret =
     236           0 :         TALER_MHD_reply_with_error (connection,
     237             :                                     MHD_HTTP_INTERNAL_SERVER_ERROR,
     238             :                                     TALER_EC_GENERIC_DB_STORE_FAILED,
     239             :                                     "insert purse request");
     240           0 :       return GNUNET_DB_STATUS_HARD_ERROR;
     241             :     }
     242           0 :     if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
     243           0 :       return qs;
     244           0 :     if (in_conflict)
     245             :     {
     246             :       struct TALER_PurseMergePublicKeyP merge_pub;
     247             :       struct GNUNET_TIME_Timestamp purse_expiration;
     248             :       struct TALER_PrivateContractHashP h_contract_terms;
     249             :       struct TALER_Amount target_amount;
     250             :       struct TALER_Amount balance;
     251             :       struct TALER_PurseContractSignatureP purse_sig;
     252             :       uint32_t min_age;
     253             : 
     254           0 :       TEH_plugin->rollback (TEH_plugin->cls);
     255           0 :       qs = TEH_plugin->select_purse_request (
     256           0 :         TEH_plugin->cls,
     257           0 :         &rpc->pd.purse_pub,
     258             :         &merge_pub,
     259             :         &purse_expiration,
     260             :         &h_contract_terms,
     261             :         &min_age,
     262             :         &target_amount,
     263             :         &balance,
     264             :         &purse_sig);
     265           0 :       if (qs <= 0)
     266             :       {
     267           0 :         GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
     268           0 :         GNUNET_break (0 != qs);
     269           0 :         TALER_LOG_WARNING ("Failed to fetch purse information from database\n");
     270           0 :         *mhd_ret = TALER_MHD_reply_with_error (connection,
     271             :                                                MHD_HTTP_INTERNAL_SERVER_ERROR,
     272             :                                                TALER_EC_GENERIC_DB_FETCH_FAILED,
     273             :                                                "select purse request");
     274           0 :         return GNUNET_DB_STATUS_HARD_ERROR;
     275             :       }
     276             :       *mhd_ret
     277           0 :         = TALER_MHD_REPLY_JSON_PACK (
     278             :             connection,
     279             :             MHD_HTTP_CONFLICT,
     280             :             TALER_JSON_pack_ec (
     281             :               TALER_EC_EXCHANGE_RESERVES_PURSE_CREATE_CONFLICTING_META_DATA),
     282             :             TALER_JSON_pack_amount ("amount",
     283             :                                     &target_amount),
     284             :             GNUNET_JSON_pack_uint64 ("min_age",
     285             :                                      min_age),
     286             :             GNUNET_JSON_pack_timestamp ("purse_expiration",
     287             :                                         purse_expiration),
     288             :             GNUNET_JSON_pack_data_auto ("purse_sig",
     289             :                                         &purse_sig),
     290             :             GNUNET_JSON_pack_data_auto ("h_contract_terms",
     291             :                                         &h_contract_terms),
     292             :             GNUNET_JSON_pack_data_auto ("merge_pub",
     293             :                                         &merge_pub));
     294           0 :       return GNUNET_DB_STATUS_HARD_ERROR;
     295             :     }
     296             : 
     297             :   }
     298             : 
     299             :   /* 2) create purse with reserve (and debit reserve for purse creation!) */
     300             :   {
     301           0 :     bool in_conflict = true;
     302           0 :     bool insufficient_funds = true;
     303           0 :     bool no_reserve = true;
     304             : 
     305           0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     306             :                 "Creating purse with flags %d\n",
     307             :                 rpc->flags);
     308           0 :     qs = TEH_plugin->do_reserve_purse (
     309           0 :       TEH_plugin->cls,
     310           0 :       &rpc->pd.purse_pub,
     311           0 :       &rpc->merge_sig,
     312             :       rpc->merge_timestamp,
     313           0 :       &rpc->reserve_sig,
     314             :       (TALER_WAMF_MODE_CREATE_FROM_PURSE_QUOTA
     315           0 :        == rpc->flags)
     316             :       ? NULL
     317           0 :       : &rpc->gf->fees.purse,
     318             :       rpc->reserve_pub,
     319             :       &in_conflict,
     320             :       &no_reserve,
     321             :       &insufficient_funds);
     322           0 :     if (qs < 0)
     323             :     {
     324           0 :       if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
     325           0 :         return qs;
     326           0 :       TALER_LOG_WARNING (
     327             :         "Failed to store purse merge information in database\n");
     328           0 :       *mhd_ret =
     329           0 :         TALER_MHD_reply_with_error (connection,
     330             :                                     MHD_HTTP_INTERNAL_SERVER_ERROR,
     331             :                                     TALER_EC_GENERIC_DB_STORE_FAILED,
     332             :                                     "do reserve purse");
     333           0 :       return GNUNET_DB_STATUS_HARD_ERROR;
     334             :     }
     335           0 :     if (in_conflict)
     336             :     {
     337             :       /* same purse already merged into a different reserve!? */
     338             :       struct TALER_PurseContractPublicKeyP purse_pub;
     339             :       struct TALER_PurseMergeSignatureP merge_sig;
     340             :       struct GNUNET_TIME_Timestamp merge_timestamp;
     341             :       char *partner_url;
     342             :       struct TALER_ReservePublicKeyP reserve_pub;
     343             : 
     344           0 :       TEH_plugin->rollback (TEH_plugin->cls);
     345           0 :       qs = TEH_plugin->select_purse_merge (
     346           0 :         TEH_plugin->cls,
     347             :         &purse_pub,
     348             :         &merge_sig,
     349             :         &merge_timestamp,
     350             :         &partner_url,
     351             :         &reserve_pub);
     352           0 :       if (qs <= 0)
     353             :       {
     354           0 :         GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
     355           0 :         GNUNET_break (0 != qs);
     356           0 :         TALER_LOG_WARNING (
     357             :           "Failed to fetch purse merge information from database\n");
     358           0 :         *mhd_ret = TALER_MHD_reply_with_error (connection,
     359             :                                                MHD_HTTP_INTERNAL_SERVER_ERROR,
     360             :                                                TALER_EC_GENERIC_DB_FETCH_FAILED,
     361             :                                                "select purse merge");
     362           0 :         return GNUNET_DB_STATUS_HARD_ERROR;
     363             :       }
     364             :       *mhd_ret
     365           0 :         = TALER_MHD_REPLY_JSON_PACK (
     366             :             connection,
     367             :             MHD_HTTP_CONFLICT,
     368             :             TALER_JSON_pack_ec (
     369             :               TALER_EC_EXCHANGE_RESERVES_PURSE_MERGE_CONFLICTING_META_DATA),
     370             :             GNUNET_JSON_pack_string ("partner_url",
     371             :                                      NULL == partner_url
     372             :                                      ? TEH_base_url
     373             :                                      : partner_url),
     374             :             GNUNET_JSON_pack_timestamp ("merge_timestamp",
     375             :                                         merge_timestamp),
     376             :             GNUNET_JSON_pack_data_auto ("merge_sig",
     377             :                                         &merge_sig),
     378             :             GNUNET_JSON_pack_data_auto ("reserve_pub",
     379             :                                         &reserve_pub));
     380           0 :       GNUNET_free (partner_url);
     381           0 :       return GNUNET_DB_STATUS_HARD_ERROR;
     382             :     }
     383           0 :     if ( (no_reserve) &&
     384             :          ( (TALER_WAMF_MODE_CREATE_FROM_PURSE_QUOTA
     385           0 :             == rpc->flags) ||
     386           0 :            (! TALER_amount_is_zero (&rpc->gf->fees.purse)) ) )
     387             :     {
     388             :       *mhd_ret
     389           0 :         = TALER_MHD_REPLY_JSON_PACK (
     390             :             connection,
     391             :             MHD_HTTP_NOT_FOUND,
     392             :             TALER_JSON_pack_ec (
     393             :               TALER_EC_EXCHANGE_GENERIC_RESERVE_UNKNOWN));
     394           0 :       return GNUNET_DB_STATUS_HARD_ERROR;
     395             :     }
     396           0 :     if (insufficient_funds)
     397             :     {
     398             :       *mhd_ret
     399           0 :         = TALER_MHD_REPLY_JSON_PACK (
     400             :             connection,
     401             :             MHD_HTTP_CONFLICT,
     402             :             TALER_JSON_pack_ec (
     403             :               TALER_EC_EXCHANGE_RESERVES_PURSE_CREATE_INSUFFICIENT_FUNDS));
     404           0 :       return GNUNET_DB_STATUS_HARD_ERROR;
     405             :     }
     406             :   }
     407             :   /* 3) if present, persist contract */
     408           0 :   if (! rpc->no_econtract)
     409             :   {
     410           0 :     bool in_conflict = true;
     411             : 
     412           0 :     qs = TEH_plugin->insert_contract (TEH_plugin->cls,
     413           0 :                                       &rpc->pd.purse_pub,
     414           0 :                                       &rpc->econtract,
     415             :                                       &in_conflict);
     416           0 :     if (qs < 0)
     417             :     {
     418           0 :       if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
     419           0 :         return qs;
     420           0 :       TALER_LOG_WARNING ("Failed to store purse information in database\n");
     421           0 :       *mhd_ret = TALER_MHD_reply_with_error (connection,
     422             :                                              MHD_HTTP_INTERNAL_SERVER_ERROR,
     423             :                                              TALER_EC_GENERIC_DB_STORE_FAILED,
     424             :                                              "purse purse contract");
     425           0 :       return GNUNET_DB_STATUS_HARD_ERROR;
     426             :     }
     427           0 :     if (in_conflict)
     428             :     {
     429             :       struct TALER_EncryptedContract econtract;
     430             :       struct GNUNET_HashCode h_econtract;
     431             : 
     432           0 :       qs = TEH_plugin->select_contract_by_purse (
     433           0 :         TEH_plugin->cls,
     434           0 :         &rpc->pd.purse_pub,
     435             :         &econtract);
     436           0 :       if (qs <= 0)
     437             :       {
     438           0 :         if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
     439           0 :           return qs;
     440           0 :         GNUNET_break (0 != qs);
     441           0 :         TALER_LOG_WARNING (
     442             :           "Failed to store fetch contract information from database\n");
     443           0 :         *mhd_ret = TALER_MHD_reply_with_error (connection,
     444             :                                                MHD_HTTP_INTERNAL_SERVER_ERROR,
     445             :                                                TALER_EC_GENERIC_DB_FETCH_FAILED,
     446             :                                                "select contract");
     447           0 :         return GNUNET_DB_STATUS_HARD_ERROR;
     448             :       }
     449           0 :       GNUNET_CRYPTO_hash (econtract.econtract,
     450             :                           econtract.econtract_size,
     451             :                           &h_econtract);
     452             :       *mhd_ret
     453           0 :         = TALER_MHD_REPLY_JSON_PACK (
     454             :             connection,
     455             :             MHD_HTTP_CONFLICT,
     456             :             TALER_JSON_pack_ec (
     457             :               TALER_EC_EXCHANGE_PURSE_ECONTRACT_CONFLICTING_META_DATA),
     458             :             GNUNET_JSON_pack_data_auto ("h_econtract",
     459             :                                         &h_econtract),
     460             :             GNUNET_JSON_pack_data_auto ("econtract_sig",
     461             :                                         &econtract.econtract_sig),
     462             :             GNUNET_JSON_pack_data_auto ("contract_pub",
     463             :                                         &econtract.contract_pub));
     464           0 :       GNUNET_free (econtract.econtract);
     465           0 :       return GNUNET_DB_STATUS_HARD_ERROR;
     466             :     }
     467             :   }
     468           0 :   return qs;
     469             : }
     470             : 
     471             : 
     472             : MHD_RESULT
     473           0 : TEH_handler_reserves_purse (
     474             :   struct TEH_RequestContext *rc,
     475             :   const struct TALER_ReservePublicKeyP *reserve_pub,
     476             :   const json_t *root)
     477             : {
     478           0 :   struct MHD_Connection *connection = rc->connection;
     479           0 :   struct ReservePurseContext rpc = {
     480             :     .reserve_pub = reserve_pub,
     481           0 :     .exchange_timestamp = GNUNET_TIME_timestamp_get ()
     482             :   };
     483           0 :   bool no_purse_fee = true;
     484             :   struct GNUNET_JSON_Specification spec[] = {
     485           0 :     TALER_JSON_spec_amount ("purse_value",
     486             :                             TEH_currency,
     487             :                             &rpc.pd.target_amount),
     488           0 :     GNUNET_JSON_spec_uint32 ("min_age",
     489             :                              &rpc.min_age),
     490           0 :     GNUNET_JSON_spec_mark_optional (
     491             :       TALER_JSON_spec_amount ("purse_fee",
     492             :                               TEH_currency,
     493             :                               &rpc.purse_fee),
     494             :       &no_purse_fee),
     495           0 :     GNUNET_JSON_spec_mark_optional (
     496             :       TALER_JSON_spec_econtract ("econtract",
     497             :                                  &rpc.econtract),
     498             :       &rpc.no_econtract),
     499           0 :     GNUNET_JSON_spec_fixed_auto ("merge_pub",
     500             :                                  &rpc.merge_pub),
     501           0 :     GNUNET_JSON_spec_fixed_auto ("merge_sig",
     502             :                                  &rpc.merge_sig),
     503           0 :     GNUNET_JSON_spec_fixed_auto ("reserve_sig",
     504             :                                  &rpc.reserve_sig),
     505           0 :     GNUNET_JSON_spec_fixed_auto ("purse_pub",
     506             :                                  &rpc.pd.purse_pub),
     507           0 :     GNUNET_JSON_spec_fixed_auto ("purse_sig",
     508             :                                  &rpc.purse_sig),
     509           0 :     GNUNET_JSON_spec_fixed_auto ("h_contract_terms",
     510             :                                  &rpc.pd.h_contract_terms),
     511           0 :     GNUNET_JSON_spec_timestamp ("merge_timestamp",
     512             :                                 &rpc.merge_timestamp),
     513           0 :     GNUNET_JSON_spec_timestamp ("purse_expiration",
     514             :                                 &rpc.pd.purse_expiration),
     515           0 :     GNUNET_JSON_spec_end ()
     516             :   };
     517             : 
     518             :   {
     519             :     enum GNUNET_GenericReturnValue res;
     520             : 
     521           0 :     res = TALER_MHD_parse_json_data (connection,
     522             :                                      root,
     523             :                                      spec);
     524           0 :     if (GNUNET_SYSERR == res)
     525             :     {
     526           0 :       GNUNET_break (0);
     527           0 :       return MHD_NO; /* hard failure */
     528             :     }
     529           0 :     if (GNUNET_NO == res)
     530             :     {
     531           0 :       GNUNET_break_op (0);
     532           0 :       return MHD_YES; /* failure */
     533             :     }
     534             :   }
     535             :   {
     536             :     char *payto_uri;
     537             : 
     538           0 :     payto_uri = TALER_reserve_make_payto (TEH_base_url,
     539             :                                           reserve_pub);
     540           0 :     TALER_payto_hash (payto_uri,
     541             :                       &rpc.h_payto);
     542           0 :     TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
     543           0 :     if (GNUNET_OK !=
     544           0 :         TALER_wallet_purse_merge_verify (payto_uri,
     545             :                                          rpc.merge_timestamp,
     546             :                                          &rpc.pd.purse_pub,
     547             :                                          &rpc.merge_pub,
     548             :                                          &rpc.merge_sig))
     549             :     {
     550           0 :       GNUNET_break_op (0);
     551           0 :       GNUNET_JSON_parse_free (spec);
     552           0 :       GNUNET_free (payto_uri);
     553           0 :       return TALER_MHD_reply_with_error (
     554             :         connection,
     555             :         MHD_HTTP_FORBIDDEN,
     556             :         TALER_EC_EXCHANGE_RESERVES_PURSE_MERGE_SIGNATURE_INVALID,
     557             :         NULL);
     558             :     }
     559           0 :     GNUNET_free (payto_uri);
     560             :   }
     561           0 :   GNUNET_assert (GNUNET_OK ==
     562             :                  TALER_amount_set_zero (TEH_currency,
     563             :                                         &rpc.deposit_total));
     564           0 :   if (GNUNET_TIME_timestamp_cmp (rpc.pd.purse_expiration,
     565             :                                  <,
     566             :                                  rpc.exchange_timestamp))
     567             :   {
     568           0 :     GNUNET_break_op (0);
     569           0 :     GNUNET_JSON_parse_free (spec);
     570           0 :     return TALER_MHD_reply_with_error (connection,
     571             :                                        MHD_HTTP_BAD_REQUEST,
     572             :                                        TALER_EC_EXCHANGE_RESERVES_PURSE_EXPIRATION_BEFORE_NOW,
     573             :                                        NULL);
     574             :   }
     575           0 :   if (GNUNET_TIME_absolute_is_never (rpc.pd.purse_expiration.abs_time))
     576             :   {
     577           0 :     GNUNET_break_op (0);
     578           0 :     GNUNET_JSON_parse_free (spec);
     579           0 :     return TALER_MHD_reply_with_error (connection,
     580             :                                        MHD_HTTP_BAD_REQUEST,
     581             :                                        TALER_EC_EXCHANGE_RESERVES_PURSE_EXPIRATION_IS_NEVER,
     582             :                                        NULL);
     583             :   }
     584             :   {
     585             :     struct TEH_KeyStateHandle *keys;
     586             : 
     587           0 :     keys = TEH_keys_get_state ();
     588           0 :     if (NULL == keys)
     589             :     {
     590           0 :       GNUNET_break (0);
     591           0 :       GNUNET_JSON_parse_free (spec);
     592           0 :       return TALER_MHD_reply_with_error (connection,
     593             :                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
     594             :                                          TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING,
     595             :                                          NULL);
     596             :     }
     597           0 :     rpc.gf = TEH_keys_global_fee_by_time (keys,
     598             :                                           rpc.exchange_timestamp);
     599             :   }
     600           0 :   if (NULL == rpc.gf)
     601             :   {
     602           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     603             :                 "Cannot purse purse: global fees not configured!\n");
     604           0 :     GNUNET_JSON_parse_free (spec);
     605           0 :     return TALER_MHD_reply_with_error (connection,
     606             :                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
     607             :                                        TALER_EC_EXCHANGE_GENERIC_GLOBAL_FEES_MISSING,
     608             :                                        NULL);
     609             :   }
     610           0 :   if (no_purse_fee)
     611             :   {
     612           0 :     rpc.flags = TALER_WAMF_MODE_CREATE_FROM_PURSE_QUOTA;
     613           0 :     TALER_amount_set_zero (TEH_currency,
     614             :                            &rpc.purse_fee);
     615             :   }
     616             :   else
     617             :   {
     618           0 :     rpc.flags = TALER_WAMF_MODE_CREATE_WITH_PURSE_FEE;
     619           0 :     if (-1 ==
     620           0 :         TALER_amount_cmp (&rpc.purse_fee,
     621           0 :                           &rpc.gf->fees.purse))
     622             :     {
     623             :       /* rpc.purse_fee is below gf.fees.purse! */
     624           0 :       GNUNET_break_op (0);
     625           0 :       GNUNET_JSON_parse_free (spec);
     626           0 :       return TALER_MHD_reply_with_error (connection,
     627             :                                          MHD_HTTP_BAD_REQUEST,
     628             :                                          TALER_EC_EXCHANGE_RESERVES_PURSE_FEE_TOO_LOW,
     629           0 :                                          TALER_amount2s (&rpc.gf->fees.purse));
     630             :     }
     631             :   }
     632           0 :   TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
     633           0 :   if (GNUNET_OK !=
     634           0 :       TALER_wallet_purse_create_verify (rpc.pd.purse_expiration,
     635             :                                         &rpc.pd.h_contract_terms,
     636             :                                         &rpc.merge_pub,
     637             :                                         rpc.min_age,
     638             :                                         &rpc.pd.target_amount,
     639             :                                         &rpc.pd.purse_pub,
     640             :                                         &rpc.purse_sig))
     641             :   {
     642           0 :     GNUNET_break_op (0);
     643           0 :     GNUNET_JSON_parse_free (spec);
     644           0 :     return TALER_MHD_reply_with_error (
     645             :       connection,
     646             :       MHD_HTTP_FORBIDDEN,
     647             :       TALER_EC_EXCHANGE_PURSE_CREATE_SIGNATURE_INVALID,
     648             :       NULL);
     649             :   }
     650           0 :   if (GNUNET_OK !=
     651           0 :       TALER_wallet_account_merge_verify (rpc.merge_timestamp,
     652             :                                          &rpc.pd.purse_pub,
     653             :                                          rpc.pd.purse_expiration,
     654             :                                          &rpc.pd.h_contract_terms,
     655             :                                          &rpc.pd.target_amount,
     656             :                                          &rpc.purse_fee,
     657             :                                          rpc.min_age,
     658             :                                          rpc.flags,
     659             :                                          rpc.reserve_pub,
     660             :                                          &rpc.reserve_sig))
     661             :   {
     662           0 :     GNUNET_break_op (0);
     663           0 :     GNUNET_JSON_parse_free (spec);
     664           0 :     return TALER_MHD_reply_with_error (
     665             :       connection,
     666             :       MHD_HTTP_FORBIDDEN,
     667             :       TALER_EC_EXCHANGE_RESERVES_PURSE_MERGE_SIGNATURE_INVALID,
     668             :       NULL);
     669             :   }
     670           0 :   if ( (! rpc.no_econtract) &&
     671             :        (GNUNET_OK !=
     672           0 :         TALER_wallet_econtract_upload_verify (rpc.econtract.econtract,
     673             :                                               rpc.econtract.econtract_size,
     674             :                                               &rpc.econtract.contract_pub,
     675             :                                               &rpc.pd.purse_pub,
     676             :                                               &rpc.econtract.econtract_sig)) )
     677             :   {
     678           0 :     TALER_LOG_WARNING ("Invalid signature on /reserves/$PID/purse request\n");
     679           0 :     GNUNET_JSON_parse_free (spec);
     680           0 :     return TALER_MHD_reply_with_error (connection,
     681             :                                        MHD_HTTP_FORBIDDEN,
     682             :                                        TALER_EC_EXCHANGE_PURSE_ECONTRACT_SIGNATURE_INVALID,
     683             :                                        NULL);
     684             :   }
     685             : 
     686             : 
     687           0 :   if (GNUNET_SYSERR ==
     688           0 :       TEH_plugin->preflight (TEH_plugin->cls))
     689             :   {
     690           0 :     GNUNET_break (0);
     691           0 :     GNUNET_JSON_parse_free (spec);
     692           0 :     return TALER_MHD_reply_with_error (connection,
     693             :                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
     694             :                                        TALER_EC_GENERIC_DB_START_FAILED,
     695             :                                        "preflight failure");
     696             :   }
     697             : 
     698             :   /* execute transaction */
     699             :   {
     700             :     MHD_RESULT mhd_ret;
     701             : 
     702           0 :     if (GNUNET_OK !=
     703           0 :         TEH_DB_run_transaction (connection,
     704             :                                 "execute purse purse",
     705             :                                 TEH_MT_REQUEST_RESERVE_PURSE,
     706             :                                 &mhd_ret,
     707             :                                 &purse_transaction,
     708             :                                 &rpc))
     709             :     {
     710           0 :       GNUNET_JSON_parse_free (spec);
     711           0 :       return mhd_ret;
     712             :     }
     713             :   }
     714             : 
     715           0 :   if (! rpc.kyc.ok)
     716           0 :     return TEH_RESPONSE_reply_kyc_required (connection,
     717             :                                             &rpc.h_payto,
     718             :                                             &rpc.kyc);
     719             :   /* generate regular response */
     720             :   {
     721             :     MHD_RESULT res;
     722             : 
     723           0 :     res = TEH_RESPONSE_reply_purse_created (connection,
     724             :                                             rpc.exchange_timestamp,
     725             :                                             &rpc.deposit_total,
     726             :                                             &rpc.pd);
     727           0 :     GNUNET_JSON_parse_free (spec);
     728           0 :     return res;
     729             :   }
     730             : }
     731             : 
     732             : 
     733             : /* end of taler-exchange-httpd_reserves_purse.c */

Generated by: LCOV version 1.14