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


      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                  TALER_JSON_pack_amount ("accumulated_total_without_fee",
    273                                          &bdc->accumulated_total_without_fee),
    274                  GNUNET_JSON_pack_data_auto ("exchange_pub",
    275                                              &pub),
    276                  GNUNET_JSON_pack_data_auto ("exchange_sig",
    277                                              &sig)));
    278 }
    279 
    280 
    281 /**
    282  * Execute database transaction for /batch-deposit.  Runs the transaction
    283  * logic; IF it returns a non-error code, the transaction logic MUST
    284  * NOT queue a MHD response.  IF it returns an hard error, the
    285  * transaction logic MUST queue a MHD response and set @a mhd_ret.  IF
    286  * it returns the soft error code, the function MAY be called again to
    287  * retry and MUST not queue a MHD response.
    288  *
    289  * @param cls a `struct BatchDepositContext`
    290  * @param connection MHD request context
    291  * @param[out] mhd_ret set to MHD status on error
    292  * @return transaction status
    293  */
    294 static enum GNUNET_DB_QueryStatus
    295 batch_deposit_transaction (void *cls,
    296                            struct MHD_Connection *connection,
    297                            MHD_RESULT *mhd_ret)
    298 {
    299   struct BatchDepositContext *bdc = cls;
    300   const struct TALER_EXCHANGEDB_BatchDeposit *bd = &bdc->bd;
    301   enum GNUNET_DB_QueryStatus qs = GNUNET_DB_STATUS_HARD_ERROR;
    302   uint32_t bad_balance_coin_index = UINT32_MAX;
    303   bool balance_ok;
    304   bool in_conflict;
    305 
    306   /* If the deposit has a policy associated to it, persist it.  This will
    307    * insert or update the record. */
    308   if (! bdc->has_no_policy)
    309   {
    310     qs = TEH_plugin->persist_policy_details (
    311       TEH_plugin->cls,
    312       &bdc->policy_details,
    313       &bdc->bd.policy_details_serial_id,
    314       &bdc->accumulated_total_without_fee,
    315       &bdc->policy_details.fulfillment_state);
    316     if (qs < 0)
    317       return qs;
    318 
    319     bdc->bd.policy_blocked =
    320       bdc->policy_details.fulfillment_state != TALER_PolicyFulfillmentSuccess;
    321   }
    322 
    323   /* FIXME-#9373: replace by batch insert! */
    324   for (unsigned int i = 0; i<bdc->bd.num_cdis; i++)
    325   {
    326     const struct TALER_EXCHANGEDB_CoinDepositInformation *cdi
    327       = &bdc->cdis[i];
    328     uint64_t known_coin_id;
    329 
    330     qs = TEH_make_coin_known (&cdi->coin,
    331                               connection,
    332                               &known_coin_id,
    333                               mhd_ret);
    334     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    335                 "make coin known (%s) returned %d\n",
    336                 TALER_B2S (&cdi->coin.coin_pub),
    337                 qs);
    338     if (qs < 0)
    339       return qs;
    340   }
    341   qs = TEH_plugin->do_deposit (
    342     TEH_plugin->cls,
    343     bd,
    344     bdc->deposit_fees,
    345     &bdc->exchange_timestamp,
    346     &bdc->accumulated_total_without_fee,
    347     &balance_ok,
    348     &bad_balance_coin_index,
    349     &in_conflict);
    350   if (qs <= 0)
    351   {
    352     if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
    353       return qs;
    354     TALER_LOG_WARNING (
    355       "Failed to store /batch-deposit information in database\n");
    356     *mhd_ret = TALER_MHD_reply_with_error (
    357       connection,
    358       MHD_HTTP_INTERNAL_SERVER_ERROR,
    359       TALER_EC_GENERIC_DB_STORE_FAILED,
    360       "batch-deposit");
    361     return qs;
    362   }
    363   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    364               "do_deposit returned: %d / %s[%u] / %s\n",
    365               qs,
    366               balance_ok ? "balance ok" : "balance insufficient",
    367               (unsigned int) bad_balance_coin_index,
    368               in_conflict ? "in conflict" : "no conflict");
    369   if (in_conflict)
    370   {
    371     struct TALER_MerchantWireHashP h_wire;
    372 
    373     if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
    374         TEH_plugin->get_wire_hash_for_contract (
    375           TEH_plugin->cls,
    376           &bd->merchant_pub,
    377           &bd->h_contract_terms,
    378           &h_wire))
    379     {
    380       TALER_LOG_WARNING (
    381         "Failed to retrieve conflicting contract details from database\n");
    382       *mhd_ret = TALER_MHD_reply_with_error (
    383         connection,
    384         MHD_HTTP_INTERNAL_SERVER_ERROR,
    385         TALER_EC_GENERIC_DB_STORE_FAILED,
    386         "batch-deposit");
    387       return qs;
    388     }
    389 
    390     *mhd_ret
    391       = TEH_RESPONSE_reply_coin_conflicting_contract (
    392           connection,
    393           TALER_EC_EXCHANGE_DEPOSIT_CONFLICTING_CONTRACT,
    394           &h_wire);
    395     return GNUNET_DB_STATUS_HARD_ERROR;
    396   }
    397   if (! balance_ok)
    398   {
    399     GNUNET_assert (bad_balance_coin_index < bdc->bd.num_cdis);
    400     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    401                 "returning history of conflicting coin (%s)\n",
    402                 TALER_B2S (&bdc->cdis[bad_balance_coin_index].coin.coin_pub));
    403     *mhd_ret
    404       = TEH_RESPONSE_reply_coin_insufficient_funds (
    405           connection,
    406           TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS,
    407           &bdc->cdis[bad_balance_coin_index].coin.denom_pub_hash,
    408           &bdc->cdis[bad_balance_coin_index].coin.coin_pub);
    409     return GNUNET_DB_STATUS_HARD_ERROR;
    410   }
    411   TEH_METRICS_num_success[TEH_MT_SUCCESS_DEPOSIT]++;
    412   return qs;
    413 }
    414 
    415 
    416 /**
    417  * Run database transaction.
    418  *
    419  * @param[in,out] bdc request context
    420  */
    421 static void
    422 bdc_phase_transact (struct BatchDepositContext *bdc)
    423 {
    424   MHD_RESULT mhd_ret;
    425 
    426   if (GNUNET_SYSERR ==
    427       TEH_plugin->preflight (TEH_plugin->cls))
    428   {
    429     GNUNET_break (0);
    430     finish_loop (bdc,
    431                  TALER_MHD_reply_with_error (
    432                    bdc->rc->connection,
    433                    MHD_HTTP_INTERNAL_SERVER_ERROR,
    434                    TALER_EC_GENERIC_DB_START_FAILED,
    435                    "preflight failure"));
    436     return;
    437   }
    438 
    439   if (GNUNET_OK !=
    440       TEH_DB_run_transaction (bdc->rc->connection,
    441                               "execute batch deposit",
    442                               TEH_MT_REQUEST_BATCH_DEPOSIT,
    443                               &mhd_ret,
    444                               &batch_deposit_transaction,
    445                               bdc))
    446   {
    447     finish_loop (bdc,
    448                  mhd_ret);
    449     return;
    450   }
    451   bdc->phase++;
    452 }
    453 
    454 
    455 /**
    456  * Check if the @a bdc is replayed and we already have an
    457  * answer. If so, replay the existing answer and return the
    458  * HTTP response.
    459  *
    460  * @param bdc parsed request data
    461  * @return true if the request is idempotent with an existing request
    462  *    false if we did not find the request in the DB and did not set @a mret
    463  */
    464 static bool
    465 check_request_idempotent (
    466   struct BatchDepositContext *bdc)
    467 {
    468   const struct TEH_RequestContext *rc = bdc->rc;
    469   enum GNUNET_DB_QueryStatus qs;
    470   bool is_idempotent;
    471 
    472   qs = TEH_plugin->do_check_deposit_idempotent (
    473     TEH_plugin->cls,
    474     &bdc->bd,
    475     &bdc->exchange_timestamp,
    476     &is_idempotent);
    477   if (0 > qs)
    478   {
    479     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    480     finish_loop (bdc,
    481                  TALER_MHD_reply_with_error (
    482                    rc->connection,
    483                    MHD_HTTP_INTERNAL_SERVER_ERROR,
    484                    TALER_EC_GENERIC_DB_FETCH_FAILED,
    485                    "do_check_deposit_idempotent"));
    486     return true;
    487   }
    488   if ( (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) ||
    489        (! is_idempotent) )
    490     return false;
    491   bdc->phase = BDC_PHASE_REPLY_SUCCESS;
    492   return true;
    493 }
    494 
    495 
    496 /**
    497  * Check the KYC result.
    498  *
    499  * @param bdc storage for request processing
    500  */
    501 static void
    502 bdc_phase_check_kyc_result (struct BatchDepositContext *bdc)
    503 {
    504   /* return final positive response */
    505   if ( (! bdc->kyc.ok) ||
    506        (bdc->bad_kyc_auth) )
    507   {
    508     if (check_request_idempotent (bdc))
    509     {
    510       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    511                   "Request is idempotent!\n");
    512       return;
    513     }
    514     /* KYC required */
    515     finish_loop (bdc,
    516                  TEH_RESPONSE_reply_kyc_required (
    517                    bdc->rc->connection,
    518                    &bdc->nph,
    519                    &bdc->kyc,
    520                    bdc->bad_kyc_auth));
    521     return;
    522   }
    523   bdc->phase = BDC_PHASE_TRANSACT;
    524 }
    525 
    526 
    527 /**
    528  * Function called with the result of a legitimization
    529  * check.
    530  *
    531  * @param cls closure
    532  * @param lcr legitimization check result
    533  */
    534 static void
    535 deposit_legi_cb (
    536   void *cls,
    537   const struct TEH_LegitimizationCheckResult *lcr)
    538 {
    539   struct BatchDepositContext *bdc = cls;
    540 
    541   bdc->lch = NULL;
    542   GNUNET_assert (BDC_PHASE_SUSPENDED ==
    543                  bdc->phase);
    544   MHD_resume_connection (bdc->rc->connection);
    545   GNUNET_CONTAINER_DLL_remove (bdc_head,
    546                                bdc_tail,
    547                                bdc);
    548   TALER_MHD_daemon_trigger ();
    549   if (NULL != lcr->response)
    550   {
    551     bdc->response = lcr->response;
    552     bdc->http_status = lcr->http_status;
    553     bdc->phase = BDC_PHASE_GENERATE_REPLY_FAILURE;
    554     return;
    555   }
    556   bdc->kyc = lcr->kyc;
    557   bdc->bad_kyc_auth = lcr->bad_kyc_auth;
    558   bdc->phase = BDC_PHASE_CHECK_KYC_RESULT;
    559 }
    560 
    561 
    562 /**
    563  * Function called to iterate over KYC-relevant transaction amounts for a
    564  * particular time range. Called within a database transaction, so must
    565  * not start a new one.
    566  *
    567  * @param cls closure, identifies the event type and account to iterate
    568  *        over events for
    569  * @param limit maximum time-range for which events should be fetched
    570  *        (timestamp in the past)
    571  * @param cb function to call on each event found, events must be returned
    572  *        in reverse chronological order
    573  * @param cb_cls closure for @a cb, of type struct AgeWithdrawContext
    574  * @return transaction status
    575  */
    576 static enum GNUNET_DB_QueryStatus
    577 deposit_amount_cb (
    578   void *cls,
    579   struct GNUNET_TIME_Absolute limit,
    580   TALER_EXCHANGEDB_KycAmountCallback cb,
    581   void *cb_cls)
    582 {
    583   struct BatchDepositContext *bdc = cls;
    584   enum GNUNET_GenericReturnValue ret;
    585   enum GNUNET_DB_QueryStatus qs;
    586 
    587   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    588               "Signaling amount %s for KYC check during deposit\n",
    589               TALER_amount2s (&bdc->accumulated_total_without_fee));
    590   ret = cb (cb_cls,
    591             &bdc->accumulated_total_without_fee,
    592             bdc->exchange_timestamp.abs_time);
    593   GNUNET_break (GNUNET_SYSERR != ret);
    594   if (GNUNET_OK != ret)
    595     return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
    596   qs = TEH_plugin->select_deposit_amounts_for_kyc_check (
    597     TEH_plugin->cls,
    598     &bdc->nph,
    599     limit,
    600     cb,
    601     cb_cls);
    602   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    603               "Got %d additional transactions for this deposit and limit %llu\n",
    604               qs,
    605               (unsigned long long) limit.abs_value_us);
    606   GNUNET_break (qs >= 0);
    607   return qs;
    608 }
    609 
    610 
    611 /**
    612  * Run KYC check.
    613  *
    614  * @param[in,out] bdc request context
    615  */
    616 static void
    617 bdc_phase_kyc (struct BatchDepositContext *bdc)
    618 {
    619   if (GNUNET_YES != TEH_enable_kyc)
    620   {
    621     bdc->phase++;
    622     return;
    623   }
    624   TALER_full_payto_normalize_and_hash (bdc->bd.receiver_wire_account,
    625                                        &bdc->nph);
    626   bdc->lch = TEH_legitimization_check2 (
    627     &bdc->rc->async_scope_id,
    628     TALER_KYCLOGIC_KYC_TRIGGER_DEPOSIT,
    629     bdc->bd.receiver_wire_account,
    630     &bdc->nph,
    631     &bdc->bd.merchant_pub,
    632     &deposit_amount_cb,
    633     bdc,
    634     &deposit_legi_cb,
    635     bdc);
    636   GNUNET_assert (NULL != bdc->lch);
    637   GNUNET_CONTAINER_DLL_insert (bdc_head,
    638                                bdc_tail,
    639                                bdc);
    640   MHD_suspend_connection (bdc->rc->connection);
    641   bdc->phase = BDC_PHASE_SUSPENDED;
    642 }
    643 
    644 
    645 /**
    646  * Handle policy.
    647  *
    648  * @param[in,out] bdc request context
    649  */
    650 static void
    651 bdc_phase_policy (struct BatchDepositContext *bdc)
    652 {
    653   const char *error_hint = NULL;
    654 
    655   if (bdc->has_no_policy)
    656   {
    657     bdc->phase++;
    658     return;
    659   }
    660   if (GNUNET_OK !=
    661       TALER_extensions_create_policy_details (
    662         TEH_currency,
    663         bdc->policy_json,
    664         &bdc->policy_details,
    665         &error_hint))
    666   {
    667     GNUNET_break_op (0);
    668     finish_loop (bdc,
    669                  TALER_MHD_reply_with_error (
    670                    bdc->rc->connection,
    671                    MHD_HTTP_BAD_REQUEST,
    672                    TALER_EC_EXCHANGE_DEPOSITS_POLICY_NOT_ACCEPTED,
    673                    error_hint));
    674     return;
    675   }
    676 
    677   TALER_deposit_policy_hash (bdc->policy_json,
    678                              &bdc->h_policy);
    679   bdc->phase++;
    680 }
    681 
    682 
    683 /**
    684  * Parse per-coin deposit information from @a jcoin
    685  * into @a deposit. Fill in generic information from
    686  * @a ctx.
    687  *
    688  * @param bdc information about the overall batch
    689  * @param jcoin coin data to parse
    690  * @param[out] cdi where to store the result
    691  * @param[out] deposit_fee where to write the deposit fee
    692  * @return #GNUNET_OK on success, #GNUNET_NO if an error was returned,
    693  *         #GNUNET_SYSERR on failure and no error could be returned
    694  */
    695 static enum GNUNET_GenericReturnValue
    696 parse_coin (const struct BatchDepositContext *bdc,
    697             json_t *jcoin,
    698             struct TALER_EXCHANGEDB_CoinDepositInformation *cdi,
    699             struct TALER_Amount *deposit_fee)
    700 {
    701   const struct TALER_EXCHANGEDB_BatchDeposit *bd = &bdc->bd;
    702   struct GNUNET_JSON_Specification spec[] = {
    703     TALER_JSON_spec_amount ("contribution",
    704                             TEH_currency,
    705                             &cdi->amount_with_fee),
    706     GNUNET_JSON_spec_fixed_auto ("denom_pub_hash",
    707                                  &cdi->coin.denom_pub_hash),
    708     TALER_JSON_spec_denom_sig ("ub_sig",
    709                                &cdi->coin.denom_sig),
    710     GNUNET_JSON_spec_fixed_auto ("coin_pub",
    711                                  &cdi->coin.coin_pub),
    712     GNUNET_JSON_spec_mark_optional (
    713       GNUNET_JSON_spec_fixed_auto ("h_age_commitment",
    714                                    &cdi->coin.h_age_commitment),
    715       &cdi->coin.no_age_commitment),
    716     GNUNET_JSON_spec_fixed_auto ("coin_sig",
    717                                  &cdi->csig),
    718     GNUNET_JSON_spec_end ()
    719   };
    720   enum GNUNET_GenericReturnValue res;
    721 
    722   if (GNUNET_OK !=
    723       (res = TALER_MHD_parse_json_data (bdc->rc->connection,
    724                                         jcoin,
    725                                         spec)))
    726     return res;
    727   /* check denomination exists and is valid */
    728   {
    729     struct TEH_DenominationKey *dk;
    730     MHD_RESULT mret;
    731 
    732     dk = TEH_keys_denomination_by_hash (
    733       &cdi->coin.denom_pub_hash,
    734       bdc->rc->connection,
    735       &mret);
    736     if (NULL == dk)
    737     {
    738       GNUNET_JSON_parse_free (spec);
    739       return (MHD_YES == mret)
    740         ? GNUNET_NO
    741         : GNUNET_SYSERR;
    742     }
    743     if (0 > TALER_amount_cmp (&dk->meta.value,
    744                               &cdi->amount_with_fee))
    745     {
    746       GNUNET_break_op (0);
    747       GNUNET_JSON_parse_free (spec);
    748       return (MHD_YES ==
    749               TALER_MHD_reply_with_error (
    750                 bdc->rc->connection,
    751                 MHD_HTTP_BAD_REQUEST,
    752                 TALER_EC_EXCHANGE_GENERIC_AMOUNT_EXCEEDS_DENOMINATION_VALUE,
    753                 NULL))
    754         ? GNUNET_NO
    755         : GNUNET_SYSERR;
    756     }
    757     if (GNUNET_TIME_absolute_is_past (dk->meta.expire_deposit.abs_time))
    758     {
    759       /* This denomination is past the expiration time for deposits */
    760       GNUNET_JSON_parse_free (spec);
    761       return (MHD_YES ==
    762               TEH_RESPONSE_reply_expired_denom_pub_hash (
    763                 bdc->rc->connection,
    764                 &cdi->coin.denom_pub_hash,
    765                 TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED,
    766                 "DEPOSIT"))
    767         ? GNUNET_NO
    768         : GNUNET_SYSERR;
    769     }
    770     if (GNUNET_TIME_absolute_is_future (dk->meta.start.abs_time))
    771     {
    772       /* This denomination is not yet valid */
    773       GNUNET_JSON_parse_free (spec);
    774       return (MHD_YES ==
    775               TEH_RESPONSE_reply_expired_denom_pub_hash (
    776                 bdc->rc->connection,
    777                 &cdi->coin.denom_pub_hash,
    778                 TALER_EC_EXCHANGE_GENERIC_DENOMINATION_VALIDITY_IN_FUTURE,
    779                 "DEPOSIT"))
    780         ? GNUNET_NO
    781         : GNUNET_SYSERR;
    782     }
    783     if (dk->recoup_possible)
    784     {
    785       /* This denomination has been revoked */
    786       GNUNET_JSON_parse_free (spec);
    787       return (MHD_YES ==
    788               TEH_RESPONSE_reply_expired_denom_pub_hash (
    789                 bdc->rc->connection,
    790                 &cdi->coin.denom_pub_hash,
    791                 TALER_EC_EXCHANGE_GENERIC_DENOMINATION_REVOKED,
    792                 "DEPOSIT"))
    793         ? GNUNET_NO
    794         : GNUNET_SYSERR;
    795     }
    796     if (dk->denom_pub.bsign_pub_key->cipher !=
    797         cdi->coin.denom_sig.unblinded_sig->cipher)
    798     {
    799       /* denomination cipher and denomination signature cipher not the same */
    800       GNUNET_JSON_parse_free (spec);
    801       return (MHD_YES ==
    802               TALER_MHD_reply_with_error (
    803                 bdc->rc->connection,
    804                 MHD_HTTP_BAD_REQUEST,
    805                 TALER_EC_EXCHANGE_GENERIC_CIPHER_MISMATCH,
    806                 NULL))
    807         ? GNUNET_NO
    808         : GNUNET_SYSERR;
    809     }
    810 
    811     *deposit_fee = dk->meta.fees.deposit;
    812     /* check coin signature */
    813     switch (dk->denom_pub.bsign_pub_key->cipher)
    814     {
    815     case GNUNET_CRYPTO_BSA_RSA:
    816       TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_RSA]++;
    817       break;
    818     case GNUNET_CRYPTO_BSA_CS:
    819       TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_CS]++;
    820       break;
    821     default:
    822       break;
    823     }
    824     if (GNUNET_YES !=
    825         TALER_test_coin_valid (&cdi->coin,
    826                                &dk->denom_pub))
    827     {
    828       TALER_LOG_WARNING ("Invalid coin passed for /batch-deposit\n");
    829       GNUNET_JSON_parse_free (spec);
    830       return (MHD_YES ==
    831               TALER_MHD_reply_with_error (
    832                 bdc->rc->connection,
    833                 MHD_HTTP_FORBIDDEN,
    834                 TALER_EC_EXCHANGE_DENOMINATION_SIGNATURE_INVALID,
    835                 NULL))
    836         ? GNUNET_NO
    837         : GNUNET_SYSERR;
    838     }
    839   }
    840   if (0 < TALER_amount_cmp (deposit_fee,
    841                             &cdi->amount_with_fee))
    842   {
    843     GNUNET_break_op (0);
    844     GNUNET_JSON_parse_free (spec);
    845     return (MHD_YES ==
    846             TALER_MHD_reply_with_error (
    847               bdc->rc->connection,
    848               MHD_HTTP_BAD_REQUEST,
    849               TALER_EC_EXCHANGE_DEPOSIT_NEGATIVE_VALUE_AFTER_FEE,
    850               NULL))
    851         ? GNUNET_NO
    852         : GNUNET_SYSERR;
    853   }
    854 
    855   TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
    856   if (GNUNET_OK !=
    857       TALER_wallet_deposit_verify (
    858         &cdi->amount_with_fee,
    859         deposit_fee,
    860         &bdc->h_wire,
    861         &bd->h_contract_terms,
    862         &bd->wallet_data_hash,
    863         cdi->coin.no_age_commitment
    864         ? NULL
    865         : &cdi->coin.h_age_commitment,
    866         NULL != bdc->policy_json ? &bdc->h_policy : NULL,
    867         &cdi->coin.denom_pub_hash,
    868         bd->wallet_timestamp,
    869         &bd->merchant_pub,
    870         bd->refund_deadline,
    871         &cdi->coin.coin_pub,
    872         &cdi->csig))
    873   {
    874     TALER_LOG_WARNING ("Invalid signature on /batch-deposit request\n");
    875     GNUNET_JSON_parse_free (spec);
    876     return (MHD_YES ==
    877             TALER_MHD_reply_with_error (
    878               bdc->rc->connection,
    879               MHD_HTTP_FORBIDDEN,
    880               TALER_EC_EXCHANGE_DEPOSIT_COIN_SIGNATURE_INVALID,
    881               TALER_B2S (&cdi->coin.coin_pub)))
    882       ? GNUNET_NO
    883       : GNUNET_SYSERR;
    884   }
    885   return GNUNET_OK;
    886 }
    887 
    888 
    889 /**
    890  * Run processing phase that parses the request.
    891  *
    892  * @param[in,out] bdc request context
    893  * @param root JSON object that was POSTed
    894  */
    895 static void
    896 bdc_phase_parse (struct BatchDepositContext *bdc,
    897                  const json_t *root)
    898 {
    899   struct TALER_EXCHANGEDB_BatchDeposit *bd = &bdc->bd;
    900   const json_t *coins;
    901   const json_t *policy_json;
    902   bool no_refund_deadline = true;
    903   struct GNUNET_JSON_Specification spec[] = {
    904     TALER_JSON_spec_full_payto_uri ("merchant_payto_uri",
    905                                     &bd->receiver_wire_account),
    906     GNUNET_JSON_spec_fixed_auto ("wire_salt",
    907                                  &bd->wire_salt),
    908     GNUNET_JSON_spec_fixed_auto ("merchant_pub",
    909                                  &bd->merchant_pub),
    910     GNUNET_JSON_spec_fixed_auto ("merchant_sig",
    911                                  &bd->merchant_sig),
    912     GNUNET_JSON_spec_fixed_auto ("h_contract_terms",
    913                                  &bd->h_contract_terms),
    914     GNUNET_JSON_spec_mark_optional (
    915       GNUNET_JSON_spec_fixed_auto ("wallet_data_hash",
    916                                    &bd->wallet_data_hash),
    917       &bd->no_wallet_data_hash),
    918     GNUNET_JSON_spec_array_const ("coins",
    919                                   &coins),
    920     GNUNET_JSON_spec_mark_optional (
    921       GNUNET_JSON_spec_object_const ("policy",
    922                                      &policy_json),
    923       &bdc->has_no_policy),
    924     GNUNET_JSON_spec_timestamp ("timestamp",
    925                                 &bd->wallet_timestamp),
    926     GNUNET_JSON_spec_mark_optional (
    927       GNUNET_JSON_spec_string ("extra_wire_subject_metadata",
    928                                &bd->extra_wire_subject_metadata),
    929       &bd->no_wallet_data_hash),
    930     GNUNET_JSON_spec_mark_optional (
    931       GNUNET_JSON_spec_timestamp ("refund_deadline",
    932                                   &bd->refund_deadline),
    933       &no_refund_deadline),
    934     GNUNET_JSON_spec_timestamp ("wire_transfer_deadline",
    935                                 &bd->wire_deadline),
    936     GNUNET_JSON_spec_end ()
    937   };
    938 
    939   {
    940     enum GNUNET_GenericReturnValue res;
    941 
    942     res = TALER_MHD_parse_json_data (bdc->rc->connection,
    943                                      root,
    944                                      spec);
    945     if (GNUNET_SYSERR == res)
    946     {
    947       /* hard failure */
    948       GNUNET_break (0);
    949       finish_loop (bdc,
    950                    MHD_NO);
    951       return;
    952     }
    953     if (GNUNET_NO == res)
    954     {
    955       /* failure */
    956       GNUNET_break_op (0);
    957       finish_loop (bdc,
    958                    MHD_YES);
    959       return;
    960     }
    961   }
    962   if (! TALER_is_valid_subject_metadata_string (
    963         bd->extra_wire_subject_metadata))
    964   {
    965     GNUNET_break_op (0);
    966     GNUNET_JSON_parse_free (spec);
    967     finish_loop (bdc,
    968                  TALER_MHD_reply_with_error (
    969                    bdc->rc->connection,
    970                    MHD_HTTP_BAD_REQUEST,
    971                    TALER_EC_GENERIC_PARAMETER_MALFORMED,
    972                    "extra_wire_subject_metadata"));
    973     return;
    974   }
    975   if (GNUNET_OK !=
    976       TALER_merchant_contract_verify (
    977         &bd->h_contract_terms,
    978         &bd->merchant_pub,
    979         &bd->merchant_sig))
    980   {
    981     GNUNET_break_op (0);
    982     GNUNET_JSON_parse_free (spec);
    983     finish_loop (bdc,
    984                  TALER_MHD_reply_with_error (
    985                    bdc->rc->connection,
    986                    MHD_HTTP_BAD_REQUEST,
    987                    TALER_EC_GENERIC_PARAMETER_MALFORMED,
    988                    "merchant_sig"));
    989     return;
    990   }
    991   bdc->policy_json
    992     = json_incref ((json_t *) policy_json);
    993   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    994               "Batch deposit into contract %s\n",
    995               GNUNET_h2s (&bd->h_contract_terms.hash));
    996 
    997   /* validate merchant's wire details (as far as we can) */
    998   {
    999     char *emsg;
   1000 
   1001     emsg = TALER_payto_validate (bd->receiver_wire_account);
   1002     if (NULL != emsg)
   1003     {
   1004       MHD_RESULT ret;
   1005 
   1006       GNUNET_break_op (0);
   1007       GNUNET_JSON_parse_free (spec);
   1008       ret = TALER_MHD_reply_with_error (bdc->rc->connection,
   1009                                         MHD_HTTP_BAD_REQUEST,
   1010                                         TALER_EC_GENERIC_PARAMETER_MALFORMED,
   1011                                         emsg);
   1012       GNUNET_free (emsg);
   1013       finish_loop (bdc,
   1014                    ret);
   1015       return;
   1016     }
   1017   }
   1018   if (GNUNET_TIME_timestamp_cmp (bd->refund_deadline,
   1019                                  >,
   1020                                  bd->wire_deadline))
   1021   {
   1022     GNUNET_break_op (0);
   1023     GNUNET_JSON_parse_free (spec);
   1024     finish_loop (bdc,
   1025                  TALER_MHD_reply_with_error (
   1026                    bdc->rc->connection,
   1027                    MHD_HTTP_BAD_REQUEST,
   1028                    TALER_EC_EXCHANGE_DEPOSIT_REFUND_DEADLINE_AFTER_WIRE_DEADLINE,
   1029                    NULL));
   1030     return;
   1031   }
   1032   if (GNUNET_TIME_absolute_is_never (bd->wire_deadline.abs_time))
   1033   {
   1034     GNUNET_break_op (0);
   1035     GNUNET_JSON_parse_free (spec);
   1036     finish_loop (bdc,
   1037                  TALER_MHD_reply_with_error (
   1038                    bdc->rc->connection,
   1039                    MHD_HTTP_BAD_REQUEST,
   1040                    TALER_EC_EXCHANGE_DEPOSIT_WIRE_DEADLINE_IS_NEVER,
   1041                    NULL));
   1042     return;
   1043   }
   1044   TALER_full_payto_hash (bd->receiver_wire_account,
   1045                          &bd->wire_target_h_payto);
   1046   TALER_merchant_wire_signature_hash (bd->receiver_wire_account,
   1047                                       &bd->wire_salt,
   1048                                       &bdc->h_wire);
   1049   bd->num_cdis = json_array_size (coins);
   1050   if (0 == bd->num_cdis)
   1051   {
   1052     GNUNET_break_op (0);
   1053     GNUNET_JSON_parse_free (spec);
   1054     finish_loop (bdc,
   1055                  TALER_MHD_reply_with_error (
   1056                    bdc->rc->connection,
   1057                    MHD_HTTP_BAD_REQUEST,
   1058                    TALER_EC_GENERIC_PARAMETER_MALFORMED,
   1059                    "coins"));
   1060     return;
   1061   }
   1062   if (TALER_MAX_COINS < bd->num_cdis)
   1063   {
   1064     GNUNET_break_op (0);
   1065     GNUNET_JSON_parse_free (spec);
   1066     finish_loop (bdc,
   1067                  TALER_MHD_reply_with_error (
   1068                    bdc->rc->connection,
   1069                    MHD_HTTP_BAD_REQUEST,
   1070                    TALER_EC_GENERIC_PARAMETER_MALFORMED,
   1071                    "coins"));
   1072     return;
   1073   }
   1074 
   1075   bdc->cdis
   1076     = GNUNET_new_array (bd->num_cdis,
   1077                         struct TALER_EXCHANGEDB_CoinDepositInformation);
   1078   bdc->deposit_fees
   1079     = GNUNET_new_array (bd->num_cdis,
   1080                         struct TALER_Amount);
   1081   bd->cdis = bdc->cdis;
   1082   for (unsigned i = 0; i<bd->num_cdis; i++)
   1083   {
   1084     struct TALER_Amount amount_without_fee;
   1085     enum GNUNET_GenericReturnValue res;
   1086 
   1087     res = parse_coin (bdc,
   1088                       json_array_get (coins,
   1089                                       i),
   1090                       &bdc->cdis[i],
   1091                       &bdc->deposit_fees[i]);
   1092     if (GNUNET_OK != res)
   1093     {
   1094       finish_loop (bdc,
   1095                    (GNUNET_NO == res)
   1096                    ? MHD_YES
   1097                    : MHD_NO);
   1098       return;
   1099     }
   1100     GNUNET_assert (0 <=
   1101                    TALER_amount_subtract (
   1102                      &amount_without_fee,
   1103                      &bdc->cdis[i].amount_with_fee,
   1104                      &bdc->deposit_fees[i]));
   1105 
   1106     GNUNET_assert (0 <=
   1107                    TALER_amount_add (
   1108                      &bdc->accumulated_total_without_fee,
   1109                      &bdc->accumulated_total_without_fee,
   1110                      &amount_without_fee));
   1111   }
   1112 
   1113   GNUNET_JSON_parse_free (spec);
   1114   bdc->phase++;
   1115 }
   1116 
   1117 
   1118 /**
   1119  * Function called to clean up a context.
   1120  *
   1121  * @param rc request context with data to clean up
   1122  */
   1123 static void
   1124 bdc_cleaner (struct TEH_RequestContext *rc)
   1125 {
   1126   struct BatchDepositContext *bdc = rc->rh_ctx;
   1127 
   1128   if (NULL != bdc->lch)
   1129   {
   1130     TEH_legitimization_check_cancel (bdc->lch);
   1131     bdc->lch = NULL;
   1132   }
   1133   if (0 != bdc->cdis)
   1134   {
   1135     for (unsigned int i = 0; i<bdc->bd.num_cdis; i++)
   1136       TALER_denom_sig_free (&bdc->cdis[i].coin.denom_sig);
   1137     GNUNET_free (bdc->cdis);
   1138   }
   1139   GNUNET_free (bdc->deposit_fees);
   1140   json_decref (bdc->policy_json);
   1141   GNUNET_free (bdc);
   1142 }
   1143 
   1144 
   1145 MHD_RESULT
   1146 TEH_handler_batch_deposit (struct TEH_RequestContext *rc,
   1147                            const json_t *root,
   1148                            const char *const args[])
   1149 {
   1150   struct BatchDepositContext *bdc = rc->rh_ctx;
   1151 
   1152   (void) args;
   1153   if (NULL == bdc)
   1154   {
   1155     bdc = GNUNET_new (struct BatchDepositContext);
   1156     bdc->rc = rc;
   1157     rc->rh_ctx = bdc;
   1158     rc->rh_cleaner = &bdc_cleaner;
   1159     bdc->phase = BDC_PHASE_PARSE;
   1160     bdc->exchange_timestamp = GNUNET_TIME_timestamp_get ();
   1161     GNUNET_assert (GNUNET_OK ==
   1162                    TALER_amount_set_zero (TEH_currency,
   1163                                           &bdc->accumulated_total_without_fee));
   1164   }
   1165   while (1)
   1166   {
   1167     switch (bdc->phase)
   1168     {
   1169     case BDC_PHASE_INIT:
   1170       GNUNET_break (0);
   1171       bdc->phase = BDC_PHASE_RETURN_NO;
   1172       break;
   1173     case BDC_PHASE_PARSE:
   1174       bdc_phase_parse (bdc,
   1175                        root);
   1176       break;
   1177     case BDC_PHASE_POLICY:
   1178       bdc_phase_policy (bdc);
   1179       break;
   1180     case BDC_PHASE_KYC:
   1181       bdc_phase_kyc (bdc);
   1182       break;
   1183     case BDC_PHASE_TRANSACT:
   1184       bdc_phase_transact (bdc);
   1185       break;
   1186     case BDC_PHASE_REPLY_SUCCESS:
   1187       bdc_phase_reply_success (bdc);
   1188       break;
   1189     case BDC_PHASE_SUSPENDED:
   1190       return MHD_YES;
   1191     case BDC_PHASE_CHECK_KYC_RESULT:
   1192       bdc_phase_check_kyc_result (bdc);
   1193       break;
   1194     case BDC_PHASE_GENERATE_REPLY_FAILURE:
   1195       return MHD_queue_response (bdc->rc->connection,
   1196                                  bdc->http_status,
   1197                                  bdc->response);
   1198     case BDC_PHASE_RETURN_YES:
   1199       return MHD_YES;
   1200     case BDC_PHASE_RETURN_NO:
   1201       return MHD_NO;
   1202     }
   1203   }
   1204 }
   1205 
   1206 
   1207 /* end of taler-exchange-httpd_batch-deposit.c */