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_batch-deposit.c (34470B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2014-2024 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_batch-deposit.c
     18  * @brief Handle /batch-deposit requests; parses the POST and JSON and
     19  *        verifies the coin signatures before handing things off
     20  *        to the database.
     21  * @author Florian Dold
     22  * @author Benedikt Mueller
     23  * @author Christian Grothoff
     24  */
     25 #include "taler/platform.h"
     26 #include <gnunet/gnunet_util_lib.h>
     27 #include <gnunet/gnunet_json_lib.h>
     28 #include <jansson.h>
     29 #include <microhttpd.h>
     30 #include <pthread.h>
     31 #include "taler/taler_extensions_policy.h"
     32 #include "taler/taler_json_lib.h"
     33 #include "taler/taler_mhd_lib.h"
     34 #include "taler-exchange-httpd_common_kyc.h"
     35 #include "taler-exchange-httpd_batch-deposit.h"
     36 #include "taler-exchange-httpd_responses.h"
     37 #include "taler/taler_exchangedb_lib.h"
     38 #include "taler-exchange-httpd_keys.h"
     39 
     40 
     41 /**
     42  * Closure for #batch_deposit_transaction.
     43  */
     44 struct BatchDepositContext
     45 {
     46 
     47   /**
     48    * Kept in a DLL.
     49    */
     50   struct BatchDepositContext *next;
     51 
     52   /**
     53    * Kept in a DLL.
     54    */
     55   struct BatchDepositContext *prev;
     56 
     57   /**
     58    * The request we are working on.
     59    */
     60   struct TEH_RequestContext *rc;
     61 
     62   /**
     63    * Handle for the legitimization check.
     64    */
     65   struct TEH_LegitimizationCheckHandle *lch;
     66 
     67   /**
     68    * Array with the individual coin deposit fees.
     69    */
     70   struct TALER_Amount *deposit_fees;
     71 
     72   /**
     73    * Information about deposited coins.
     74    */
     75   struct TALER_EXCHANGEDB_CoinDepositInformation *cdis;
     76 
     77   /**
     78    * Additional details for policy extension relevant for this
     79    * deposit operation, possibly NULL!
     80    */
     81   json_t *policy_json;
     82 
     83   /**
     84    * Response to return, if set.
     85    */
     86   struct MHD_Response *response;
     87 
     88   /**
     89    * KYC status of the reserve used for the operation.
     90    */
     91   struct TALER_EXCHANGEDB_KycStatus kyc;
     92 
     93   /**
     94    * Hash over @e policy_details, might be all zero
     95    */
     96   struct TALER_ExtensionPolicyHashP h_policy;
     97 
     98   /**
     99    * Hash over the merchant's payto://-URI with the wire salt.
    100    */
    101   struct TALER_MerchantWireHashP h_wire;
    102 
    103   /**
    104    * When @e policy_details are persisted, this contains the id of the record
    105    * in the policy_details table.
    106    */
    107   uint64_t policy_details_serial_id;
    108 
    109   /**
    110    * Hash over the normalized payto://-URI of the account we are
    111    * depositing into.
    112    */
    113   struct TALER_NormalizedPaytoHashP nph;
    114 
    115   /**
    116    * Our timestamp (when we received the request).
    117    * Possibly updated by the transaction if the
    118    * request is idempotent (was repeated).
    119    */
    120   struct GNUNET_TIME_Timestamp exchange_timestamp;
    121 
    122   /**
    123    * Total amount that is accumulated with this deposit,
    124    * without fee.
    125    */
    126   struct TALER_Amount accumulated_total_without_fee;
    127 
    128   /**
    129    * Details about the batch deposit operation.
    130    */
    131   struct TALER_EXCHANGEDB_BatchDeposit bd;
    132 
    133   /**
    134    * If @e policy_json was present, the corresponding policy extension
    135    * calculates these details.  These will be persisted in the policy_details
    136    * table.
    137    */
    138   struct TALER_PolicyDetails policy_details;
    139 
    140   /**
    141    * HTTP status to return with @e response, or 0.
    142    */
    143   unsigned int http_status;
    144 
    145   /**
    146    * Our current state in the state machine.
    147    */
    148   enum
    149   {
    150     BDC_PHASE_INIT = 0,
    151     BDC_PHASE_PARSE = 1,
    152     BDC_PHASE_POLICY = 2,
    153     BDC_PHASE_KYC = 3,
    154     BDC_PHASE_TRANSACT = 4,
    155     BDC_PHASE_REPLY_SUCCESS = 5,
    156     BDC_PHASE_SUSPENDED,
    157     BDC_PHASE_CHECK_KYC_RESULT,
    158     BDC_PHASE_GENERATE_REPLY_FAILURE,
    159     BDC_PHASE_RETURN_YES,
    160     BDC_PHASE_RETURN_NO,
    161   } phase;
    162 
    163   /**
    164    * True, if no policy was present in the request. Then
    165    * @e policy_json is NULL and @e h_policy will be all zero.
    166    */
    167   bool has_no_policy;
    168 
    169   /**
    170    * KYC failed because a KYC auth transfer is needed
    171    * to establish the merchant_pub.
    172    */
    173   bool bad_kyc_auth;
    174 };
    175 
    176 
    177 /**
    178  * Head of list of suspended batch deposit operations.
    179  */
    180 static struct BatchDepositContext *bdc_head;
    181 
    182 /**
    183  * Tail of list of suspended batch deposit operations.
    184  */
    185 static struct BatchDepositContext *bdc_tail;
    186 
    187 
    188 void
    189 TEH_batch_deposit_cleanup ()
    190 {
    191   struct BatchDepositContext *bdc;
    192 
    193   while (NULL != (bdc = bdc_head))
    194   {
    195     GNUNET_assert (BDC_PHASE_SUSPENDED == bdc->phase);
    196     bdc->phase = BDC_PHASE_RETURN_NO;
    197     MHD_resume_connection (bdc->rc->connection);
    198     GNUNET_CONTAINER_DLL_remove (bdc_head,
    199                                  bdc_tail,
    200                                  bdc);
    201   }
    202 }
    203 
    204 
    205 /**
    206  * Terminate the main loop by returning the final
    207  * result.
    208  *
    209  * @param[in,out] bdc context to update phase for
    210  * @param mres MHD status to return
    211  */
    212 static void
    213 finish_loop (struct BatchDepositContext *bdc,
    214              MHD_RESULT mres)
    215 {
    216   bdc->phase = (MHD_YES == mres)
    217     ? BDC_PHASE_RETURN_YES
    218     : BDC_PHASE_RETURN_NO;
    219 }
    220 
    221 
    222 /**
    223  * Send confirmation of batch deposit success to client.  This function will
    224  * create a signed message affirming the given information and return it to
    225  * the client.  By this, the exchange affirms that the coins had sufficient
    226  * (residual) value for the specified transaction and that it will execute the
    227  * requested batch deposit operation with the given wiring details.
    228  *
    229  * @param[in,out] bdc information about the batch deposit
    230  */
    231 static void
    232 bdc_phase_reply_success (
    233   struct BatchDepositContext *bdc)
    234 {
    235   const struct TALER_EXCHANGEDB_BatchDeposit *bd = &bdc->bd;
    236   const struct TALER_CoinSpendSignatureP *csigs[GNUNET_NZL (bd->num_cdis)];
    237   enum TALER_ErrorCode ec;
    238   struct TALER_ExchangePublicKeyP pub;
    239   struct TALER_ExchangeSignatureP sig;
    240 
    241   for (unsigned int i = 0; i<bdc->bd.num_cdis; i++)
    242     csigs[i] = &bd->cdis[i].csig;
    243   if (TALER_EC_NONE !=
    244       (ec = TALER_exchange_online_deposit_confirmation_sign (
    245          &TEH_keys_exchange_sign_,
    246          &bd->h_contract_terms,
    247          &bdc->h_wire,
    248          bdc->has_no_policy ? NULL : &bdc->h_policy,
    249          bdc->exchange_timestamp,
    250          bd->wire_deadline,
    251          bd->refund_deadline,
    252          &bdc->accumulated_total_without_fee,
    253          bd->num_cdis,
    254          csigs,
    255          &bdc->bd.merchant_pub,
    256          &pub,
    257          &sig)))
    258   {
    259     GNUNET_break (0);
    260     finish_loop (bdc,
    261                  TALER_MHD_reply_with_ec (bdc->rc->connection,
    262                                           ec,
    263                                           NULL));
    264     return;
    265   }
    266   finish_loop (bdc,
    267                TALER_MHD_REPLY_JSON_PACK (
    268                  bdc->rc->connection,
    269                  MHD_HTTP_OK,
    270                  GNUNET_JSON_pack_timestamp ("exchange_timestamp",
    271                                              bdc->exchange_timestamp),
    272                  GNUNET_JSON_pack_data_auto ("exchange_pub",
    273                                              &pub),
    274                  GNUNET_JSON_pack_data_auto ("exchange_sig",
    275                                              &sig)));
    276 }
    277 
    278 
    279 /**
    280  * Execute database transaction for /batch-deposit.  Runs the transaction
    281  * logic; IF it returns a non-error code, the transaction logic MUST
    282  * NOT queue a MHD response.  IF it returns an hard error, the
    283  * transaction logic MUST queue a MHD response and set @a mhd_ret.  IF
    284  * it returns the soft error code, the function MAY be called again to
    285  * retry and MUST not queue a MHD response.
    286  *
    287  * @param cls a `struct BatchDepositContext`
    288  * @param connection MHD request context
    289  * @param[out] mhd_ret set to MHD status on error
    290  * @return transaction status
    291  */
    292 static enum GNUNET_DB_QueryStatus
    293 batch_deposit_transaction (void *cls,
    294                            struct MHD_Connection *connection,
    295                            MHD_RESULT *mhd_ret)
    296 {
    297   struct BatchDepositContext *bdc = cls;
    298   const struct TALER_EXCHANGEDB_BatchDeposit *bd = &bdc->bd;
    299   enum GNUNET_DB_QueryStatus qs = GNUNET_DB_STATUS_HARD_ERROR;
    300   uint32_t bad_balance_coin_index = UINT32_MAX;
    301   bool balance_ok;
    302   bool in_conflict;
    303 
    304   /* If the deposit has a policy associated to it, persist it.  This will
    305    * insert or update the record. */
    306   if (! bdc->has_no_policy)
    307   {
    308     qs = TEH_plugin->persist_policy_details (
    309       TEH_plugin->cls,
    310       &bdc->policy_details,
    311       &bdc->bd.policy_details_serial_id,
    312       &bdc->accumulated_total_without_fee,
    313       &bdc->policy_details.fulfillment_state);
    314     if (qs < 0)
    315       return qs;
    316 
    317     bdc->bd.policy_blocked =
    318       bdc->policy_details.fulfillment_state != TALER_PolicyFulfillmentSuccess;
    319   }
    320 
    321   /* FIXME-#9373: replace by batch insert! */
    322   for (unsigned int i = 0; i<bdc->bd.num_cdis; i++)
    323   {
    324     const struct TALER_EXCHANGEDB_CoinDepositInformation *cdi
    325       = &bdc->cdis[i];
    326     uint64_t known_coin_id;
    327 
    328     qs = TEH_make_coin_known (&cdi->coin,
    329                               connection,
    330                               &known_coin_id,
    331                               mhd_ret);
    332     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    333                 "make coin known (%s) returned %d\n",
    334                 TALER_B2S (&cdi->coin.coin_pub),
    335                 qs);
    336     if (qs < 0)
    337       return qs;
    338   }
    339 
    340   qs = TEH_plugin->do_deposit (
    341     TEH_plugin->cls,
    342     bd,
    343     &bdc->exchange_timestamp,
    344     &balance_ok,
    345     &bad_balance_coin_index,
    346     &in_conflict);
    347   if (qs <= 0)
    348   {
    349     if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
    350       return qs;
    351     TALER_LOG_WARNING (
    352       "Failed to store /batch-deposit information in database\n");
    353     *mhd_ret = TALER_MHD_reply_with_error (
    354       connection,
    355       MHD_HTTP_INTERNAL_SERVER_ERROR,
    356       TALER_EC_GENERIC_DB_STORE_FAILED,
    357       "batch-deposit");
    358     return qs;
    359   }
    360   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    361               "do_deposit returned: %d / %s[%u] / %s\n",
    362               qs,
    363               balance_ok ? "balance ok" : "balance insufficient",
    364               (unsigned int) bad_balance_coin_index,
    365               in_conflict ? "in conflict" : "no conflict");
    366   if (in_conflict)
    367   {
    368     struct TALER_MerchantWireHashP h_wire;
    369 
    370     if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    371         TEH_plugin->get_wire_hash_for_contract (
    372           TEH_plugin->cls,
    373           &bd->merchant_pub,
    374           &bd->h_contract_terms,
    375           &h_wire))
    376     {
    377       TALER_LOG_WARNING (
    378         "Failed to retrieve conflicting contract details from database\n");
    379       *mhd_ret = TALER_MHD_reply_with_error (
    380         connection,
    381         MHD_HTTP_INTERNAL_SERVER_ERROR,
    382         TALER_EC_GENERIC_DB_STORE_FAILED,
    383         "batch-deposit");
    384       return qs;
    385     }
    386 
    387     *mhd_ret
    388       = TEH_RESPONSE_reply_coin_conflicting_contract (
    389           connection,
    390           TALER_EC_EXCHANGE_DEPOSIT_CONFLICTING_CONTRACT,
    391           &h_wire);
    392     return GNUNET_DB_STATUS_HARD_ERROR;
    393   }
    394   if (! balance_ok)
    395   {
    396     GNUNET_assert (bad_balance_coin_index < bdc->bd.num_cdis);
    397     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    398                 "returning history of conflicting coin (%s)\n",
    399                 TALER_B2S (&bdc->cdis[bad_balance_coin_index].coin.coin_pub));
    400     *mhd_ret
    401       = TEH_RESPONSE_reply_coin_insufficient_funds (
    402           connection,
    403           TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS,
    404           &bdc->cdis[bad_balance_coin_index].coin.denom_pub_hash,
    405           &bdc->cdis[bad_balance_coin_index].coin.coin_pub);
    406     return GNUNET_DB_STATUS_HARD_ERROR;
    407   }
    408   TEH_METRICS_num_success[TEH_MT_SUCCESS_DEPOSIT]++;
    409   return qs;
    410 }
    411 
    412 
    413 /**
    414  * Run database transaction.
    415  *
    416  * @param[in,out] bdc request context
    417  */
    418 static void
    419 bdc_phase_transact (struct BatchDepositContext *bdc)
    420 {
    421   MHD_RESULT mhd_ret;
    422 
    423   if (GNUNET_SYSERR ==
    424       TEH_plugin->preflight (TEH_plugin->cls))
    425   {
    426     GNUNET_break (0);
    427     finish_loop (bdc,
    428                  TALER_MHD_reply_with_error (
    429                    bdc->rc->connection,
    430                    MHD_HTTP_INTERNAL_SERVER_ERROR,
    431                    TALER_EC_GENERIC_DB_START_FAILED,
    432                    "preflight failure"));
    433     return;
    434   }
    435 
    436   if (GNUNET_OK !=
    437       TEH_DB_run_transaction (bdc->rc->connection,
    438                               "execute batch deposit",
    439                               TEH_MT_REQUEST_BATCH_DEPOSIT,
    440                               &mhd_ret,
    441                               &batch_deposit_transaction,
    442                               bdc))
    443   {
    444     finish_loop (bdc,
    445                  mhd_ret);
    446     return;
    447   }
    448   bdc->phase++;
    449 }
    450 
    451 
    452 /**
    453  * Check if the @a bdc is replayed and we already have an
    454  * answer. If so, replay the existing answer and return the
    455  * HTTP response.
    456  *
    457  * @param bdc parsed request data
    458  * @return true if the request is idempotent with an existing request
    459  *    false if we did not find the request in the DB and did not set @a mret
    460  */
    461 static bool
    462 check_request_idempotent (
    463   struct BatchDepositContext *bdc)
    464 {
    465   const struct TEH_RequestContext *rc = bdc->rc;
    466   enum GNUNET_DB_QueryStatus qs;
    467   bool is_idempotent;
    468 
    469   qs = TEH_plugin->do_check_deposit_idempotent (
    470     TEH_plugin->cls,
    471     &bdc->bd,
    472     &bdc->exchange_timestamp,
    473     &is_idempotent);
    474   if (0 > qs)
    475   {
    476     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    477     finish_loop (bdc,
    478                  TALER_MHD_reply_with_error (
    479                    rc->connection,
    480                    MHD_HTTP_INTERNAL_SERVER_ERROR,
    481                    TALER_EC_GENERIC_DB_FETCH_FAILED,
    482                    "do_check_deposit_idempotent"));
    483     return true;
    484   }
    485   if ( (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) ||
    486        (! is_idempotent) )
    487     return false;
    488   bdc->phase = BDC_PHASE_REPLY_SUCCESS;
    489   return true;
    490 }
    491 
    492 
    493 /**
    494  * Check the KYC result.
    495  *
    496  * @param bdc storage for request processing
    497  */
    498 static void
    499 bdc_phase_check_kyc_result (struct BatchDepositContext *bdc)
    500 {
    501   /* return final positive response */
    502   if ( (! bdc->kyc.ok) ||
    503        (bdc->bad_kyc_auth) )
    504   {
    505     if (check_request_idempotent (bdc))
    506     {
    507       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    508                   "Request is idempotent!\n");
    509       return;
    510     }
    511     /* KYC required */
    512     finish_loop (bdc,
    513                  TEH_RESPONSE_reply_kyc_required (
    514                    bdc->rc->connection,
    515                    &bdc->nph,
    516                    &bdc->kyc,
    517                    bdc->bad_kyc_auth));
    518     return;
    519   }
    520   bdc->phase = BDC_PHASE_TRANSACT;
    521 }
    522 
    523 
    524 /**
    525  * Function called with the result of a legitimization
    526  * check.
    527  *
    528  * @param cls closure
    529  * @param lcr legitimization check result
    530  */
    531 static void
    532 deposit_legi_cb (
    533   void *cls,
    534   const struct TEH_LegitimizationCheckResult *lcr)
    535 {
    536   struct BatchDepositContext *bdc = cls;
    537 
    538   bdc->lch = NULL;
    539   GNUNET_assert (BDC_PHASE_SUSPENDED ==
    540                  bdc->phase);
    541   MHD_resume_connection (bdc->rc->connection);
    542   GNUNET_CONTAINER_DLL_remove (bdc_head,
    543                                bdc_tail,
    544                                bdc);
    545   TALER_MHD_daemon_trigger ();
    546   if (NULL != lcr->response)
    547   {
    548     bdc->response = lcr->response;
    549     bdc->http_status = lcr->http_status;
    550     bdc->phase = BDC_PHASE_GENERATE_REPLY_FAILURE;
    551     return;
    552   }
    553   bdc->kyc = lcr->kyc;
    554   bdc->bad_kyc_auth = lcr->bad_kyc_auth;
    555   bdc->phase = BDC_PHASE_CHECK_KYC_RESULT;
    556 }
    557 
    558 
    559 /**
    560  * Function called to iterate over KYC-relevant transaction amounts for a
    561  * particular time range. Called within a database transaction, so must
    562  * not start a new one.
    563  *
    564  * @param cls closure, identifies the event type and account to iterate
    565  *        over events for
    566  * @param limit maximum time-range for which events should be fetched
    567  *        (timestamp in the past)
    568  * @param cb function to call on each event found, events must be returned
    569  *        in reverse chronological order
    570  * @param cb_cls closure for @a cb, of type struct AgeWithdrawContext
    571  * @return transaction status
    572  */
    573 static enum GNUNET_DB_QueryStatus
    574 deposit_amount_cb (
    575   void *cls,
    576   struct GNUNET_TIME_Absolute limit,
    577   TALER_EXCHANGEDB_KycAmountCallback cb,
    578   void *cb_cls)
    579 {
    580   struct BatchDepositContext *bdc = cls;
    581   enum GNUNET_GenericReturnValue ret;
    582   enum GNUNET_DB_QueryStatus qs;
    583 
    584   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    585               "Signaling amount %s for KYC check during deposit\n",
    586               TALER_amount2s (&bdc->accumulated_total_without_fee));
    587   ret = cb (cb_cls,
    588             &bdc->accumulated_total_without_fee,
    589             bdc->exchange_timestamp.abs_time);
    590   GNUNET_break (GNUNET_SYSERR != ret);
    591   if (GNUNET_OK != ret)
    592     return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
    593   qs = TEH_plugin->select_deposit_amounts_for_kyc_check (
    594     TEH_plugin->cls,
    595     &bdc->nph,
    596     limit,
    597     cb,
    598     cb_cls);
    599   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    600               "Got %d additional transactions for this deposit and limit %llu\n",
    601               qs,
    602               (unsigned long long) limit.abs_value_us);
    603   GNUNET_break (qs >= 0);
    604   return qs;
    605 }
    606 
    607 
    608 /**
    609  * Run KYC check.
    610  *
    611  * @param[in,out] bdc request context
    612  */
    613 static void
    614 bdc_phase_kyc (struct BatchDepositContext *bdc)
    615 {
    616   if (GNUNET_YES != TEH_enable_kyc)
    617   {
    618     bdc->phase++;
    619     return;
    620   }
    621   TALER_full_payto_normalize_and_hash (bdc->bd.receiver_wire_account,
    622                                        &bdc->nph);
    623   bdc->lch = TEH_legitimization_check2 (
    624     &bdc->rc->async_scope_id,
    625     TALER_KYCLOGIC_KYC_TRIGGER_DEPOSIT,
    626     bdc->bd.receiver_wire_account,
    627     &bdc->nph,
    628     &bdc->bd.merchant_pub,
    629     &deposit_amount_cb,
    630     bdc,
    631     &deposit_legi_cb,
    632     bdc);
    633   GNUNET_assert (NULL != bdc->lch);
    634   GNUNET_CONTAINER_DLL_insert (bdc_head,
    635                                bdc_tail,
    636                                bdc);
    637   MHD_suspend_connection (bdc->rc->connection);
    638   bdc->phase = BDC_PHASE_SUSPENDED;
    639 }
    640 
    641 
    642 /**
    643  * Handle policy.
    644  *
    645  * @param[in,out] bdc request context
    646  */
    647 static void
    648 bdc_phase_policy (struct BatchDepositContext *bdc)
    649 {
    650   const char *error_hint = NULL;
    651 
    652   if (bdc->has_no_policy)
    653   {
    654     bdc->phase++;
    655     return;
    656   }
    657   if (GNUNET_OK !=
    658       TALER_extensions_create_policy_details (
    659         TEH_currency,
    660         bdc->policy_json,
    661         &bdc->policy_details,
    662         &error_hint))
    663   {
    664     GNUNET_break_op (0);
    665     finish_loop (bdc,
    666                  TALER_MHD_reply_with_error (
    667                    bdc->rc->connection,
    668                    MHD_HTTP_BAD_REQUEST,
    669                    TALER_EC_EXCHANGE_DEPOSITS_POLICY_NOT_ACCEPTED,
    670                    error_hint));
    671     return;
    672   }
    673 
    674   TALER_deposit_policy_hash (bdc->policy_json,
    675                              &bdc->h_policy);
    676   bdc->phase++;
    677 }
    678 
    679 
    680 /**
    681  * Parse per-coin deposit information from @a jcoin
    682  * into @a deposit. Fill in generic information from
    683  * @a ctx.
    684  *
    685  * @param bdc information about the overall batch
    686  * @param jcoin coin data to parse
    687  * @param[out] cdi where to store the result
    688  * @param[out] deposit_fee where to write the deposit fee
    689  * @return #GNUNET_OK on success, #GNUNET_NO if an error was returned,
    690  *         #GNUNET_SYSERR on failure and no error could be returned
    691  */
    692 static enum GNUNET_GenericReturnValue
    693 parse_coin (const struct BatchDepositContext *bdc,
    694             json_t *jcoin,
    695             struct TALER_EXCHANGEDB_CoinDepositInformation *cdi,
    696             struct TALER_Amount *deposit_fee)
    697 {
    698   const struct TALER_EXCHANGEDB_BatchDeposit *bd = &bdc->bd;
    699   struct GNUNET_JSON_Specification spec[] = {
    700     TALER_JSON_spec_amount ("contribution",
    701                             TEH_currency,
    702                             &cdi->amount_with_fee),
    703     GNUNET_JSON_spec_fixed_auto ("denom_pub_hash",
    704                                  &cdi->coin.denom_pub_hash),
    705     TALER_JSON_spec_denom_sig ("ub_sig",
    706                                &cdi->coin.denom_sig),
    707     GNUNET_JSON_spec_fixed_auto ("coin_pub",
    708                                  &cdi->coin.coin_pub),
    709     GNUNET_JSON_spec_mark_optional (
    710       GNUNET_JSON_spec_fixed_auto ("h_age_commitment",
    711                                    &cdi->coin.h_age_commitment),
    712       &cdi->coin.no_age_commitment),
    713     GNUNET_JSON_spec_fixed_auto ("coin_sig",
    714                                  &cdi->csig),
    715     GNUNET_JSON_spec_end ()
    716   };
    717   enum GNUNET_GenericReturnValue res;
    718 
    719   if (GNUNET_OK !=
    720       (res = TALER_MHD_parse_json_data (bdc->rc->connection,
    721                                         jcoin,
    722                                         spec)))
    723     return res;
    724   /* check denomination exists and is valid */
    725   {
    726     struct TEH_DenominationKey *dk;
    727     MHD_RESULT mret;
    728 
    729     dk = TEH_keys_denomination_by_hash (
    730       &cdi->coin.denom_pub_hash,
    731       bdc->rc->connection,
    732       &mret);
    733     if (NULL == dk)
    734     {
    735       GNUNET_JSON_parse_free (spec);
    736       return (MHD_YES == mret)
    737         ? GNUNET_NO
    738         : GNUNET_SYSERR;
    739     }
    740     if (0 > TALER_amount_cmp (&dk->meta.value,
    741                               &cdi->amount_with_fee))
    742     {
    743       GNUNET_break_op (0);
    744       GNUNET_JSON_parse_free (spec);
    745       return (MHD_YES ==
    746               TALER_MHD_reply_with_error (
    747                 bdc->rc->connection,
    748                 MHD_HTTP_BAD_REQUEST,
    749                 TALER_EC_EXCHANGE_GENERIC_AMOUNT_EXCEEDS_DENOMINATION_VALUE,
    750                 NULL))
    751         ? GNUNET_NO
    752         : GNUNET_SYSERR;
    753     }
    754     if (GNUNET_TIME_absolute_is_past (dk->meta.expire_deposit.abs_time))
    755     {
    756       /* This denomination is past the expiration time for deposits */
    757       GNUNET_JSON_parse_free (spec);
    758       return (MHD_YES ==
    759               TEH_RESPONSE_reply_expired_denom_pub_hash (
    760                 bdc->rc->connection,
    761                 &cdi->coin.denom_pub_hash,
    762                 TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED,
    763                 "DEPOSIT"))
    764         ? GNUNET_NO
    765         : GNUNET_SYSERR;
    766     }
    767     if (GNUNET_TIME_absolute_is_future (dk->meta.start.abs_time))
    768     {
    769       /* This denomination is not yet valid */
    770       GNUNET_JSON_parse_free (spec);
    771       return (MHD_YES ==
    772               TEH_RESPONSE_reply_expired_denom_pub_hash (
    773                 bdc->rc->connection,
    774                 &cdi->coin.denom_pub_hash,
    775                 TALER_EC_EXCHANGE_GENERIC_DENOMINATION_VALIDITY_IN_FUTURE,
    776                 "DEPOSIT"))
    777         ? GNUNET_NO
    778         : GNUNET_SYSERR;
    779     }
    780     if (dk->recoup_possible)
    781     {
    782       /* This denomination has been revoked */
    783       GNUNET_JSON_parse_free (spec);
    784       return (MHD_YES ==
    785               TEH_RESPONSE_reply_expired_denom_pub_hash (
    786                 bdc->rc->connection,
    787                 &cdi->coin.denom_pub_hash,
    788                 TALER_EC_EXCHANGE_GENERIC_DENOMINATION_REVOKED,
    789                 "DEPOSIT"))
    790         ? GNUNET_NO
    791         : GNUNET_SYSERR;
    792     }
    793     if (dk->denom_pub.bsign_pub_key->cipher !=
    794         cdi->coin.denom_sig.unblinded_sig->cipher)
    795     {
    796       /* denomination cipher and denomination signature cipher not the same */
    797       GNUNET_JSON_parse_free (spec);
    798       return (MHD_YES ==
    799               TALER_MHD_reply_with_error (
    800                 bdc->rc->connection,
    801                 MHD_HTTP_BAD_REQUEST,
    802                 TALER_EC_EXCHANGE_GENERIC_CIPHER_MISMATCH,
    803                 NULL))
    804         ? GNUNET_NO
    805         : GNUNET_SYSERR;
    806     }
    807 
    808     *deposit_fee = dk->meta.fees.deposit;
    809     /* check coin signature */
    810     switch (dk->denom_pub.bsign_pub_key->cipher)
    811     {
    812     case GNUNET_CRYPTO_BSA_RSA:
    813       TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_RSA]++;
    814       break;
    815     case GNUNET_CRYPTO_BSA_CS:
    816       TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_CS]++;
    817       break;
    818     default:
    819       break;
    820     }
    821     if (GNUNET_YES !=
    822         TALER_test_coin_valid (&cdi->coin,
    823                                &dk->denom_pub))
    824     {
    825       TALER_LOG_WARNING ("Invalid coin passed for /batch-deposit\n");
    826       GNUNET_JSON_parse_free (spec);
    827       return (MHD_YES ==
    828               TALER_MHD_reply_with_error (
    829                 bdc->rc->connection,
    830                 MHD_HTTP_FORBIDDEN,
    831                 TALER_EC_EXCHANGE_DENOMINATION_SIGNATURE_INVALID,
    832                 NULL))
    833         ? GNUNET_NO
    834         : GNUNET_SYSERR;
    835     }
    836   }
    837   if (0 < TALER_amount_cmp (deposit_fee,
    838                             &cdi->amount_with_fee))
    839   {
    840     GNUNET_break_op (0);
    841     GNUNET_JSON_parse_free (spec);
    842     return (MHD_YES ==
    843             TALER_MHD_reply_with_error (
    844               bdc->rc->connection,
    845               MHD_HTTP_BAD_REQUEST,
    846               TALER_EC_EXCHANGE_DEPOSIT_NEGATIVE_VALUE_AFTER_FEE,
    847               NULL))
    848         ? GNUNET_NO
    849         : GNUNET_SYSERR;
    850   }
    851 
    852   TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
    853   if (GNUNET_OK !=
    854       TALER_wallet_deposit_verify (
    855         &cdi->amount_with_fee,
    856         deposit_fee,
    857         &bdc->h_wire,
    858         &bd->h_contract_terms,
    859         &bd->wallet_data_hash,
    860         cdi->coin.no_age_commitment
    861         ? NULL
    862         : &cdi->coin.h_age_commitment,
    863         NULL != bdc->policy_json ? &bdc->h_policy : NULL,
    864         &cdi->coin.denom_pub_hash,
    865         bd->wallet_timestamp,
    866         &bd->merchant_pub,
    867         bd->refund_deadline,
    868         &cdi->coin.coin_pub,
    869         &cdi->csig))
    870   {
    871     TALER_LOG_WARNING ("Invalid signature on /batch-deposit request\n");
    872     GNUNET_JSON_parse_free (spec);
    873     return (MHD_YES ==
    874             TALER_MHD_reply_with_error (
    875               bdc->rc->connection,
    876               MHD_HTTP_FORBIDDEN,
    877               TALER_EC_EXCHANGE_DEPOSIT_COIN_SIGNATURE_INVALID,
    878               TALER_B2S (&cdi->coin.coin_pub)))
    879       ? GNUNET_NO
    880       : GNUNET_SYSERR;
    881   }
    882   return GNUNET_OK;
    883 }
    884 
    885 
    886 /**
    887  * Run processing phase that parses the request.
    888  *
    889  * @param[in,out] bdc request context
    890  * @param root JSON object that was POSTed
    891  */
    892 static void
    893 bdc_phase_parse (struct BatchDepositContext *bdc,
    894                  const json_t *root)
    895 {
    896   struct TALER_EXCHANGEDB_BatchDeposit *bd = &bdc->bd;
    897   const json_t *coins;
    898   const json_t *policy_json;
    899   bool no_refund_deadline = true;
    900   struct GNUNET_JSON_Specification spec[] = {
    901     TALER_JSON_spec_full_payto_uri ("merchant_payto_uri",
    902                                     &bd->receiver_wire_account),
    903     GNUNET_JSON_spec_fixed_auto ("wire_salt",
    904                                  &bd->wire_salt),
    905     GNUNET_JSON_spec_fixed_auto ("merchant_pub",
    906                                  &bd->merchant_pub),
    907     GNUNET_JSON_spec_fixed_auto ("merchant_sig",
    908                                  &bd->merchant_sig),
    909     GNUNET_JSON_spec_fixed_auto ("h_contract_terms",
    910                                  &bd->h_contract_terms),
    911     GNUNET_JSON_spec_mark_optional (
    912       GNUNET_JSON_spec_fixed_auto ("wallet_data_hash",
    913                                    &bd->wallet_data_hash),
    914       &bd->no_wallet_data_hash),
    915     GNUNET_JSON_spec_array_const ("coins",
    916                                   &coins),
    917     GNUNET_JSON_spec_mark_optional (
    918       GNUNET_JSON_spec_object_const ("policy",
    919                                      &policy_json),
    920       &bdc->has_no_policy),
    921     GNUNET_JSON_spec_timestamp ("timestamp",
    922                                 &bd->wallet_timestamp),
    923     GNUNET_JSON_spec_mark_optional (
    924       GNUNET_JSON_spec_timestamp ("refund_deadline",
    925                                   &bd->refund_deadline),
    926       &no_refund_deadline),
    927     GNUNET_JSON_spec_timestamp ("wire_transfer_deadline",
    928                                 &bd->wire_deadline),
    929     GNUNET_JSON_spec_end ()
    930   };
    931 
    932   {
    933     enum GNUNET_GenericReturnValue res;
    934 
    935     res = TALER_MHD_parse_json_data (bdc->rc->connection,
    936                                      root,
    937                                      spec);
    938     if (GNUNET_SYSERR == res)
    939     {
    940       /* hard failure */
    941       GNUNET_break (0);
    942       finish_loop (bdc,
    943                    MHD_NO);
    944       return;
    945     }
    946     if (GNUNET_NO == res)
    947     {
    948       /* failure */
    949       GNUNET_break_op (0);
    950       finish_loop (bdc,
    951                    MHD_YES);
    952       return;
    953     }
    954   }
    955   if (GNUNET_OK !=
    956       TALER_merchant_contract_verify (
    957         &bd->h_contract_terms,
    958         &bd->merchant_pub,
    959         &bd->merchant_sig))
    960   {
    961     GNUNET_break_op (0);
    962     GNUNET_JSON_parse_free (spec);
    963     finish_loop (bdc,
    964                  TALER_MHD_reply_with_error (
    965                    bdc->rc->connection,
    966                    MHD_HTTP_BAD_REQUEST,
    967                    TALER_EC_GENERIC_PARAMETER_MALFORMED,
    968                    "merchant_sig"));
    969     return;
    970   }
    971   bdc->policy_json
    972     = json_incref ((json_t *) policy_json);
    973   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    974               "Batch deposit into contract %s\n",
    975               GNUNET_h2s (&bd->h_contract_terms.hash));
    976 
    977   /* validate merchant's wire details (as far as we can) */
    978   {
    979     char *emsg;
    980 
    981     emsg = TALER_payto_validate (bd->receiver_wire_account);
    982     if (NULL != emsg)
    983     {
    984       MHD_RESULT ret;
    985 
    986       GNUNET_break_op (0);
    987       GNUNET_JSON_parse_free (spec);
    988       ret = TALER_MHD_reply_with_error (bdc->rc->connection,
    989                                         MHD_HTTP_BAD_REQUEST,
    990                                         TALER_EC_GENERIC_PARAMETER_MALFORMED,
    991                                         emsg);
    992       GNUNET_free (emsg);
    993       finish_loop (bdc,
    994                    ret);
    995       return;
    996     }
    997   }
    998   if (GNUNET_TIME_timestamp_cmp (bd->refund_deadline,
    999                                  >,
   1000                                  bd->wire_deadline))
   1001   {
   1002     GNUNET_break_op (0);
   1003     GNUNET_JSON_parse_free (spec);
   1004     finish_loop (bdc,
   1005                  TALER_MHD_reply_with_error (
   1006                    bdc->rc->connection,
   1007                    MHD_HTTP_BAD_REQUEST,
   1008                    TALER_EC_EXCHANGE_DEPOSIT_REFUND_DEADLINE_AFTER_WIRE_DEADLINE,
   1009                    NULL));
   1010     return;
   1011   }
   1012   if (GNUNET_TIME_absolute_is_never (bd->wire_deadline.abs_time))
   1013   {
   1014     GNUNET_break_op (0);
   1015     GNUNET_JSON_parse_free (spec);
   1016     finish_loop (bdc,
   1017                  TALER_MHD_reply_with_error (
   1018                    bdc->rc->connection,
   1019                    MHD_HTTP_BAD_REQUEST,
   1020                    TALER_EC_EXCHANGE_DEPOSIT_WIRE_DEADLINE_IS_NEVER,
   1021                    NULL));
   1022     return;
   1023   }
   1024   TALER_full_payto_hash (bd->receiver_wire_account,
   1025                          &bd->wire_target_h_payto);
   1026   TALER_merchant_wire_signature_hash (bd->receiver_wire_account,
   1027                                       &bd->wire_salt,
   1028                                       &bdc->h_wire);
   1029   bd->num_cdis = json_array_size (coins);
   1030   if (0 == bd->num_cdis)
   1031   {
   1032     GNUNET_break_op (0);
   1033     GNUNET_JSON_parse_free (spec);
   1034     finish_loop (bdc,
   1035                  TALER_MHD_reply_with_error (
   1036                    bdc->rc->connection,
   1037                    MHD_HTTP_BAD_REQUEST,
   1038                    TALER_EC_GENERIC_PARAMETER_MALFORMED,
   1039                    "coins"));
   1040     return;
   1041   }
   1042   if (TALER_MAX_COINS < bd->num_cdis)
   1043   {
   1044     GNUNET_break_op (0);
   1045     GNUNET_JSON_parse_free (spec);
   1046     finish_loop (bdc,
   1047                  TALER_MHD_reply_with_error (
   1048                    bdc->rc->connection,
   1049                    MHD_HTTP_BAD_REQUEST,
   1050                    TALER_EC_GENERIC_PARAMETER_MALFORMED,
   1051                    "coins"));
   1052     return;
   1053   }
   1054 
   1055   bdc->cdis
   1056     = GNUNET_new_array (bd->num_cdis,
   1057                         struct TALER_EXCHANGEDB_CoinDepositInformation);
   1058   bdc->deposit_fees
   1059     = GNUNET_new_array (bd->num_cdis,
   1060                         struct TALER_Amount);
   1061   bd->cdis = bdc->cdis;
   1062   for (unsigned i = 0; i<bd->num_cdis; i++)
   1063   {
   1064     struct TALER_Amount amount_without_fee;
   1065     enum GNUNET_GenericReturnValue res;
   1066 
   1067     res = parse_coin (bdc,
   1068                       json_array_get (coins,
   1069                                       i),
   1070                       &bdc->cdis[i],
   1071                       &bdc->deposit_fees[i]);
   1072     if (GNUNET_OK != res)
   1073     {
   1074       finish_loop (bdc,
   1075                    (GNUNET_NO == res)
   1076                    ? MHD_YES
   1077                    : MHD_NO);
   1078       return;
   1079     }
   1080     GNUNET_assert (0 <=
   1081                    TALER_amount_subtract (
   1082                      &amount_without_fee,
   1083                      &bdc->cdis[i].amount_with_fee,
   1084                      &bdc->deposit_fees[i]));
   1085 
   1086     GNUNET_assert (0 <=
   1087                    TALER_amount_add (
   1088                      &bdc->accumulated_total_without_fee,
   1089                      &bdc->accumulated_total_without_fee,
   1090                      &amount_without_fee));
   1091   }
   1092 
   1093   GNUNET_JSON_parse_free (spec);
   1094   bdc->phase++;
   1095 }
   1096 
   1097 
   1098 /**
   1099  * Function called to clean up a context.
   1100  *
   1101  * @param rc request context with data to clean up
   1102  */
   1103 static void
   1104 bdc_cleaner (struct TEH_RequestContext *rc)
   1105 {
   1106   struct BatchDepositContext *bdc = rc->rh_ctx;
   1107 
   1108   if (NULL != bdc->lch)
   1109   {
   1110     TEH_legitimization_check_cancel (bdc->lch);
   1111     bdc->lch = NULL;
   1112   }
   1113   if (0 != bdc->cdis)
   1114   {
   1115     for (unsigned int i = 0; i<bdc->bd.num_cdis; i++)
   1116       TALER_denom_sig_free (&bdc->cdis[i].coin.denom_sig);
   1117     GNUNET_free (bdc->cdis);
   1118   }
   1119   GNUNET_free (bdc->deposit_fees);
   1120   json_decref (bdc->policy_json);
   1121   GNUNET_free (bdc);
   1122 }
   1123 
   1124 
   1125 MHD_RESULT
   1126 TEH_handler_batch_deposit (struct TEH_RequestContext *rc,
   1127                            const json_t *root,
   1128                            const char *const args[])
   1129 {
   1130   struct BatchDepositContext *bdc = rc->rh_ctx;
   1131 
   1132   (void) args;
   1133   if (NULL == bdc)
   1134   {
   1135     bdc = GNUNET_new (struct BatchDepositContext);
   1136     bdc->rc = rc;
   1137     rc->rh_ctx = bdc;
   1138     rc->rh_cleaner = &bdc_cleaner;
   1139     bdc->phase = BDC_PHASE_PARSE;
   1140     bdc->exchange_timestamp = GNUNET_TIME_timestamp_get ();
   1141     GNUNET_assert (GNUNET_OK ==
   1142                    TALER_amount_set_zero (TEH_currency,
   1143                                           &bdc->accumulated_total_without_fee));
   1144   }
   1145   while (1)
   1146   {
   1147     switch (bdc->phase)
   1148     {
   1149     case BDC_PHASE_INIT:
   1150       GNUNET_break (0);
   1151       bdc->phase = BDC_PHASE_RETURN_NO;
   1152       break;
   1153     case BDC_PHASE_PARSE:
   1154       bdc_phase_parse (bdc,
   1155                        root);
   1156       break;
   1157     case BDC_PHASE_POLICY:
   1158       bdc_phase_policy (bdc);
   1159       break;
   1160     case BDC_PHASE_KYC:
   1161       bdc_phase_kyc (bdc);
   1162       break;
   1163     case BDC_PHASE_TRANSACT:
   1164       bdc_phase_transact (bdc);
   1165       break;
   1166     case BDC_PHASE_REPLY_SUCCESS:
   1167       bdc_phase_reply_success (bdc);
   1168       break;
   1169     case BDC_PHASE_SUSPENDED:
   1170       return MHD_YES;
   1171     case BDC_PHASE_CHECK_KYC_RESULT:
   1172       bdc_phase_check_kyc_result (bdc);
   1173       break;
   1174     case BDC_PHASE_GENERATE_REPLY_FAILURE:
   1175       return MHD_queue_response (bdc->rc->connection,
   1176                                  bdc->http_status,
   1177                                  bdc->response);
   1178     case BDC_PHASE_RETURN_YES:
   1179       return MHD_YES;
   1180     case BDC_PHASE_RETURN_NO:
   1181       return MHD_NO;
   1182     }
   1183   }
   1184 }
   1185 
   1186 
   1187 /* end of taler-exchange-httpd_batch-deposit.c */