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

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   Copyright (C) 2014-2022 Taler Systems SA
       4             : 
       5             :   TALER is free software; you can redistribute it and/or modify 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 "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_json_lib.h"
      32             : #include "taler_mhd_lib.h"
      33             : #include "taler-exchange-httpd_deposit.h"
      34             : #include "taler-exchange-httpd_responses.h"
      35             : #include "taler_exchangedb_lib.h"
      36             : #include "taler-exchange-httpd_keys.h"
      37             : 
      38             : 
      39             : /**
      40             :  * Closure for #batch_deposit_transaction.
      41             :  */
      42             : struct BatchDepositContext
      43             : {
      44             :   /**
      45             :    * Information about the individual coin deposits.
      46             :    */
      47             :   struct TALER_EXCHANGEDB_Deposit *deposits;
      48             : 
      49             :   /**
      50             :    * Our timestamp (when we received the request).
      51             :    * Possibly updated by the transaction if the
      52             :    * request is idempotent (was repeated).
      53             :    */
      54             :   struct GNUNET_TIME_Timestamp exchange_timestamp;
      55             : 
      56             :   /**
      57             :    * Hash over the proposal data between merchant and customer
      58             :    * (remains unknown to the Exchange).
      59             :    */
      60             :   struct TALER_PrivateContractHashP h_contract_terms;
      61             : 
      62             :   /**
      63             :    * Public key of the merchant.  Enables later identification
      64             :    * of the merchant in case of a need to rollback transactions.
      65             :    */
      66             :   struct TALER_MerchantPublicKeyP merchant_pub;
      67             : 
      68             :   /**
      69             :    * Salt used by the merchant to compute @e h_wire.
      70             :    */
      71             :   struct TALER_WireSaltP wire_salt;
      72             : 
      73             :   /**
      74             :    * Hash over the wire details (with @e wire_salt).
      75             :    */
      76             :   struct TALER_MerchantWireHashP h_wire;
      77             : 
      78             :   /**
      79             :    * Hash of the payto URI.
      80             :    */
      81             :   struct TALER_PaytoHashP h_payto;
      82             : 
      83             :   /**
      84             :    * Information about the receiver for executing the transaction.  URI in
      85             :    * payto://-format.
      86             :    */
      87             :   const char *payto_uri;
      88             : 
      89             :   /**
      90             :    * Additional details for extensions relevant for this
      91             :    * deposit operation, possibly NULL!
      92             :    */
      93             :   json_t *extension_details;
      94             : 
      95             :   /**
      96             :    * Hash over @e extension_details.
      97             :    */
      98             :   struct TALER_ExtensionContractHashP h_extensions;
      99             : 
     100             :   /**
     101             :    * Time when this request was generated.  Used, for example, to
     102             :    * assess when (roughly) the income was achieved for tax purposes.
     103             :    * Note that the Exchange will only check that the timestamp is not "too
     104             :    * far" into the future (i.e. several days).  The fact that the
     105             :    * timestamp falls within the validity period of the coin's
     106             :    * denomination key is irrelevant for the validity of the deposit
     107             :    * request, as obviously the customer and merchant could conspire to
     108             :    * set any timestamp.  Also, the Exchange must accept very old deposit
     109             :    * requests, as the merchant might have been unable to transmit the
     110             :    * deposit request in a timely fashion (so back-dating is not
     111             :    * prevented).
     112             :    */
     113             :   struct GNUNET_TIME_Timestamp timestamp;
     114             : 
     115             :   /**
     116             :    * How much time does the merchant have to issue a refund request?
     117             :    * Zero if refunds are not allowed.  After this time, the coin
     118             :    * cannot be refunded.
     119             :    */
     120             :   struct GNUNET_TIME_Timestamp refund_deadline;
     121             : 
     122             :   /**
     123             :    * How much time does the merchant have to execute the wire transfer?
     124             :    * This time is advisory for aggregating transactions, not a hard
     125             :    * constraint (as the merchant can theoretically pick any time,
     126             :    * including one in the past).
     127             :    */
     128             :   struct GNUNET_TIME_Timestamp wire_deadline;
     129             : 
     130             :   /**
     131             :    * Number of coins in the batch.
     132             :    */
     133             :   unsigned int num_coins;
     134             : };
     135             : 
     136             : 
     137             : /**
     138             :  * Send confirmation of batch deposit success to client.  This function will
     139             :  * create a signed message affirming the given information and return it to
     140             :  * the client.  By this, the exchange affirms that the coins had sufficient
     141             :  * (residual) value for the specified transaction and that it will execute the
     142             :  * requested batch deposit operation with the given wiring details.
     143             :  *
     144             :  * @param connection connection to the client
     145             :  * @param bdc information about the batch deposit
     146             :  * @return MHD result code
     147             :  */
     148             : static MHD_RESULT
     149           0 : reply_batch_deposit_success (
     150             :   struct MHD_Connection *connection,
     151             :   const struct BatchDepositContext *bdc)
     152             : {
     153             :   json_t *arr;
     154             :   struct TALER_ExchangePublicKeyP pub;
     155             : 
     156           0 : again:
     157           0 :   arr = json_array ();
     158           0 :   GNUNET_assert (NULL != arr);
     159           0 :   for (unsigned int i = 0; i<bdc->num_coins; i++)
     160             :   {
     161           0 :     const struct TALER_EXCHANGEDB_Deposit *deposit = &bdc->deposits[i];
     162             :     struct TALER_ExchangePublicKeyP pubi;
     163             :     struct TALER_ExchangeSignatureP sig;
     164             :     enum TALER_ErrorCode ec;
     165             :     struct TALER_Amount amount_without_fee;
     166             : 
     167           0 :     GNUNET_assert (0 <=
     168             :                    TALER_amount_subtract (&amount_without_fee,
     169             :                                           &deposit->amount_with_fee,
     170             :                                           &deposit->deposit_fee));
     171           0 :     if (TALER_EC_NONE !=
     172           0 :         (ec = TALER_exchange_online_deposit_confirmation_sign (
     173             :            &TEH_keys_exchange_sign_,
     174             :            &bdc->h_contract_terms,
     175             :            &bdc->h_wire,
     176             :            &bdc->h_extensions,
     177             :            bdc->exchange_timestamp,
     178             :            bdc->wire_deadline,
     179             :            bdc->refund_deadline,
     180             :            &amount_without_fee,
     181             :            &deposit->coin.coin_pub,
     182             :            &bdc->merchant_pub,
     183             :            &pubi,
     184             :            &sig)))
     185             :     {
     186           0 :       GNUNET_break (0);
     187           0 :       return TALER_MHD_reply_with_ec (connection,
     188             :                                       ec,
     189             :                                       NULL);
     190             :     }
     191           0 :     if (0 == i)
     192           0 :       pub = pubi;
     193           0 :     if (0 !=
     194           0 :         GNUNET_memcmp (&pub,
     195             :                        &pubi))
     196             :     {
     197             :       /* note: in the future, maybe have batch
     198             :          sign API to avoid having to handle
     199             :          key rollover... */
     200           0 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     201             :                   "Exchange public key changed during batch deposit, trying again\n");
     202           0 :       json_decref (arr);
     203           0 :       goto again;
     204             :     }
     205           0 :     GNUNET_assert (
     206             :       0 ==
     207             :       json_array_append_new (arr,
     208             :                              GNUNET_JSON_PACK (
     209             :                                GNUNET_JSON_pack_data_auto (
     210             :                                  "exchange_sig",
     211             :                                  &sig))));
     212             :   }
     213           0 :   return TALER_MHD_REPLY_JSON_PACK (
     214             :     connection,
     215             :     MHD_HTTP_OK,
     216             :     GNUNET_JSON_pack_timestamp ("exchange_timestamp",
     217             :                                 bdc->exchange_timestamp),
     218             :     GNUNET_JSON_pack_data_auto (
     219             :       "exchange_pub",
     220             :       &pub),
     221             :     GNUNET_JSON_pack_array_steal ("exchange_sigs",
     222             :                                   arr));
     223             : }
     224             : 
     225             : 
     226             : /**
     227             :  * Execute database transaction for /batch-deposit.  Runs the transaction
     228             :  * logic; IF it returns a non-error code, the transaction logic MUST
     229             :  * NOT queue a MHD response.  IF it returns an hard error, the
     230             :  * transaction logic MUST queue a MHD response and set @a mhd_ret.  IF
     231             :  * it returns the soft error code, the function MAY be called again to
     232             :  * retry and MUST not queue a MHD response.
     233             :  *
     234             :  * @param cls a `struct BatchDepositContext`
     235             :  * @param connection MHD request context
     236             :  * @param[out] mhd_ret set to MHD status on error
     237             :  * @return transaction status
     238             :  */
     239             : static enum GNUNET_DB_QueryStatus
     240           0 : batch_deposit_transaction (void *cls,
     241             :                            struct MHD_Connection *connection,
     242             :                            MHD_RESULT *mhd_ret)
     243             : {
     244           0 :   struct BatchDepositContext *dc = cls;
     245             :   enum GNUNET_DB_QueryStatus qs;
     246             :   bool balance_ok;
     247             :   bool in_conflict;
     248             : 
     249           0 :   for (unsigned int i = 0; i<dc->num_coins; i++)
     250             :   {
     251           0 :     const struct TALER_EXCHANGEDB_Deposit *deposit = &dc->deposits[i];
     252             :     uint64_t known_coin_id;
     253             : 
     254           0 :     qs = TEH_make_coin_known (&deposit->coin,
     255             :                               connection,
     256             :                               &known_coin_id,
     257             :                               mhd_ret);
     258           0 :     if (qs < 0)
     259           0 :       return qs;
     260           0 :     qs = TEH_plugin->do_deposit (TEH_plugin->cls,
     261             :                                  deposit,
     262             :                                  known_coin_id,
     263           0 :                                  &dc->h_payto,
     264             :                                  false, /* FIXME-OEC: #7270 extension blocked */
     265             :                                  &dc->exchange_timestamp,
     266             :                                  &balance_ok,
     267             :                                  &in_conflict);
     268           0 :     if (qs < 0)
     269             :     {
     270           0 :       if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
     271           0 :         return qs;
     272           0 :       TALER_LOG_WARNING (
     273             :         "Failed to store /batch-deposit information in database\n");
     274           0 :       *mhd_ret = TALER_MHD_reply_with_error (connection,
     275             :                                              MHD_HTTP_INTERNAL_SERVER_ERROR,
     276             :                                              TALER_EC_GENERIC_DB_STORE_FAILED,
     277             :                                              "batch-deposit");
     278           0 :       return qs;
     279             :     }
     280           0 :     if (in_conflict)
     281             :     {
     282             :       /* FIXME: #7267 conficting contract != insufficient funds */
     283             :       *mhd_ret
     284           0 :         = TEH_RESPONSE_reply_coin_insufficient_funds (
     285             :             connection,
     286             :             TALER_EC_EXCHANGE_DEPOSIT_CONFLICTING_CONTRACT,
     287             :             &deposit->coin.denom_pub_hash,
     288             :             &deposit->coin.coin_pub);
     289           0 :       return GNUNET_DB_STATUS_HARD_ERROR;
     290             :     }
     291           0 :     if (! balance_ok)
     292             :     {
     293             :       *mhd_ret
     294           0 :         = TEH_RESPONSE_reply_coin_insufficient_funds (
     295             :             connection,
     296             :             TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS,
     297             :             &deposit->coin.denom_pub_hash,
     298             :             &deposit->coin.coin_pub);
     299           0 :       return GNUNET_DB_STATUS_HARD_ERROR;
     300             :     }
     301             :   }
     302           0 :   TEH_METRICS_num_success[TEH_MT_SUCCESS_DEPOSIT]++;
     303           0 :   return qs;
     304             : }
     305             : 
     306             : 
     307             : /**
     308             :  * Parse per-coin deposit information from @a jcoin
     309             :  * into @a deposit. Fill in generic information from
     310             :  * @a ctx.
     311             :  *
     312             :  * @param connection connection we are handling
     313             :  * @param jcoin coin data to parse
     314             :  * @param dc overall batch deposit context information to use
     315             :  * @param[out] deposit where to store the result
     316             :  * @return #GNUNET_OK on success, #GNUNET_NO if an error was returned,
     317             :  *         #GNUNET_SYSERR on failure and no error could be returned
     318             :  */
     319             : static enum GNUNET_GenericReturnValue
     320           0 : parse_coin (struct MHD_Connection *connection,
     321             :             json_t *jcoin,
     322             :             const struct BatchDepositContext *dc,
     323             :             struct TALER_EXCHANGEDB_Deposit *deposit)
     324             : {
     325             :   struct GNUNET_JSON_Specification spec[] = {
     326           0 :     TALER_JSON_spec_amount ("contribution",
     327             :                             TEH_currency,
     328             :                             &deposit->amount_with_fee),
     329           0 :     GNUNET_JSON_spec_fixed_auto ("denom_pub_hash",
     330             :                                  &deposit->coin.denom_pub_hash),
     331           0 :     TALER_JSON_spec_denom_sig ("ub_sig",
     332             :                                &deposit->coin.denom_sig),
     333           0 :     GNUNET_JSON_spec_fixed_auto ("coin_pub",
     334             :                                  &deposit->coin.coin_pub),
     335           0 :     GNUNET_JSON_spec_mark_optional (
     336           0 :       GNUNET_JSON_spec_fixed_auto ("h_age_commitment",
     337             :                                    &deposit->coin.h_age_commitment),
     338             :       &deposit->coin.no_age_commitment),
     339           0 :     GNUNET_JSON_spec_fixed_auto ("coin_sig",
     340             :                                  &deposit->csig),
     341           0 :     GNUNET_JSON_spec_end ()
     342             :   };
     343             :   enum GNUNET_GenericReturnValue res;
     344             : 
     345           0 :   if (GNUNET_OK !=
     346           0 :       (res = TALER_MHD_parse_json_data (connection,
     347             :                                         jcoin,
     348             :                                         spec)))
     349           0 :     return res;
     350             :   /* check denomination exists and is valid */
     351             :   {
     352             :     struct TEH_DenominationKey *dk;
     353             :     MHD_RESULT mret;
     354             : 
     355           0 :     dk = TEH_keys_denomination_by_hash (&deposit->coin.denom_pub_hash,
     356             :                                         connection,
     357             :                                         &mret);
     358           0 :     if (NULL == dk)
     359             :     {
     360           0 :       GNUNET_JSON_parse_free (spec);
     361           0 :       return mret;
     362             :     }
     363           0 :     if (0 > TALER_amount_cmp (&dk->meta.value,
     364           0 :                               &deposit->amount_with_fee))
     365             :     {
     366           0 :       GNUNET_break_op (0);
     367           0 :       GNUNET_JSON_parse_free (spec);
     368             :       return (MHD_YES ==
     369           0 :               TALER_MHD_reply_with_error (connection,
     370             :                                           MHD_HTTP_BAD_REQUEST,
     371             :                                           TALER_EC_EXCHANGE_GENERIC_AMOUNT_EXCEEDS_DENOMINATION_VALUE,
     372             :                                           NULL))
     373             :         ? GNUNET_NO
     374           0 :         : GNUNET_SYSERR;
     375             :     }
     376           0 :     if (GNUNET_TIME_absolute_is_past (dk->meta.expire_deposit.abs_time))
     377             :     {
     378             :       /* This denomination is past the expiration time for deposits */
     379           0 :       GNUNET_JSON_parse_free (spec);
     380             :       return (MHD_YES ==
     381           0 :               TEH_RESPONSE_reply_expired_denom_pub_hash (
     382             :                 connection,
     383           0 :                 &deposit->coin.denom_pub_hash,
     384             :                 TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED,
     385             :                 "DEPOSIT"))
     386             :         ? GNUNET_NO
     387           0 :         : GNUNET_SYSERR;
     388             :     }
     389           0 :     if (GNUNET_TIME_absolute_is_future (dk->meta.start.abs_time))
     390             :     {
     391             :       /* This denomination is not yet valid */
     392           0 :       GNUNET_JSON_parse_free (spec);
     393             :       return (MHD_YES ==
     394           0 :               TEH_RESPONSE_reply_expired_denom_pub_hash (
     395             :                 connection,
     396           0 :                 &deposit->coin.denom_pub_hash,
     397             :                 TALER_EC_EXCHANGE_GENERIC_DENOMINATION_VALIDITY_IN_FUTURE,
     398             :                 "DEPOSIT"))
     399             :         ? GNUNET_NO
     400           0 :         : GNUNET_SYSERR;
     401             :     }
     402           0 :     if (dk->recoup_possible)
     403             :     {
     404             :       /* This denomination has been revoked */
     405           0 :       GNUNET_JSON_parse_free (spec);
     406             :       return (MHD_YES ==
     407           0 :               TEH_RESPONSE_reply_expired_denom_pub_hash (
     408             :                 connection,
     409           0 :                 &deposit->coin.denom_pub_hash,
     410             :                 TALER_EC_EXCHANGE_GENERIC_DENOMINATION_REVOKED,
     411             :                 "DEPOSIT"))
     412             :         ? GNUNET_NO
     413           0 :         : GNUNET_SYSERR;
     414             :     }
     415           0 :     if (dk->denom_pub.cipher != deposit->coin.denom_sig.cipher)
     416             :     {
     417             :       /* denomination cipher and denomination signature cipher not the same */
     418           0 :       GNUNET_JSON_parse_free (spec);
     419             :       return (MHD_YES ==
     420           0 :               TALER_MHD_reply_with_error (connection,
     421             :                                           MHD_HTTP_BAD_REQUEST,
     422             :                                           TALER_EC_EXCHANGE_GENERIC_CIPHER_MISMATCH,
     423             :                                           NULL))
     424             :         ? GNUNET_NO
     425           0 :         : GNUNET_SYSERR;
     426             :     }
     427             : 
     428           0 :     deposit->deposit_fee = dk->meta.fees.deposit;
     429             :     /* check coin signature */
     430           0 :     switch (dk->denom_pub.cipher)
     431             :     {
     432           0 :     case TALER_DENOMINATION_RSA:
     433           0 :       TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_RSA]++;
     434           0 :       break;
     435           0 :     case TALER_DENOMINATION_CS:
     436           0 :       TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_CS]++;
     437           0 :       break;
     438           0 :     default:
     439           0 :       break;
     440             :     }
     441           0 :     if (GNUNET_YES !=
     442           0 :         TALER_test_coin_valid (&deposit->coin,
     443           0 :                                &dk->denom_pub))
     444             :     {
     445           0 :       TALER_LOG_WARNING ("Invalid coin passed for /batch-deposit\n");
     446           0 :       GNUNET_JSON_parse_free (spec);
     447             :       return (MHD_YES ==
     448           0 :               TALER_MHD_reply_with_error (connection,
     449             :                                           MHD_HTTP_FORBIDDEN,
     450             :                                           TALER_EC_EXCHANGE_DENOMINATION_SIGNATURE_INVALID,
     451             :                                           NULL))
     452             :         ? GNUNET_NO
     453           0 :         : GNUNET_SYSERR;
     454             :     }
     455             :   }
     456           0 :   if (0 < TALER_amount_cmp (&deposit->deposit_fee,
     457           0 :                             &deposit->amount_with_fee))
     458             :   {
     459           0 :     GNUNET_break_op (0);
     460           0 :     GNUNET_JSON_parse_free (spec);
     461             :     return (MHD_YES ==
     462           0 :             TALER_MHD_reply_with_error (connection,
     463             :                                         MHD_HTTP_BAD_REQUEST,
     464             :                                         TALER_EC_EXCHANGE_DEPOSIT_NEGATIVE_VALUE_AFTER_FEE,
     465             :                                         NULL))
     466             :         ? GNUNET_NO
     467           0 :         : GNUNET_SYSERR;
     468             :   }
     469             : 
     470           0 :   TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
     471           0 :   if (GNUNET_OK !=
     472           0 :       TALER_wallet_deposit_verify (&deposit->amount_with_fee,
     473           0 :                                    &deposit->deposit_fee,
     474             :                                    &dc->h_wire,
     475             :                                    &dc->h_contract_terms,
     476           0 :                                    &deposit->coin.h_age_commitment,
     477             :                                    &dc->h_extensions,
     478           0 :                                    &deposit->coin.denom_pub_hash,
     479             :                                    dc->timestamp,
     480             :                                    &dc->merchant_pub,
     481             :                                    dc->refund_deadline,
     482           0 :                                    &deposit->coin.coin_pub,
     483           0 :                                    &deposit->csig))
     484             :   {
     485           0 :     TALER_LOG_WARNING ("Invalid signature on /batch-deposit request\n");
     486           0 :     GNUNET_JSON_parse_free (spec);
     487             :     return (MHD_YES ==
     488           0 :             TALER_MHD_reply_with_error (connection,
     489             :                                         MHD_HTTP_FORBIDDEN,
     490             :                                         TALER_EC_EXCHANGE_DEPOSIT_COIN_SIGNATURE_INVALID,
     491             :                                         NULL))
     492             :       ? GNUNET_NO
     493           0 :       : GNUNET_SYSERR;
     494             :   }
     495           0 :   deposit->merchant_pub = dc->merchant_pub;
     496           0 :   deposit->h_contract_terms = dc->h_contract_terms;
     497           0 :   deposit->wire_salt = dc->wire_salt;
     498           0 :   deposit->receiver_wire_account = (char *) dc->payto_uri;
     499             :   /* FIXME-OEC: #7270 should NOT insert the extension details N times,
     500             :      but rather insert them ONCE and then per-coin only use
     501             :      the resulting extension UUID/serial; so the data structure
     502             :      here should be changed once we look at extensions in earnest.  */
     503           0 :   deposit->extension_details = dc->extension_details;
     504           0 :   deposit->timestamp = dc->timestamp;
     505           0 :   deposit->refund_deadline = dc->refund_deadline;
     506           0 :   deposit->wire_deadline = dc->wire_deadline;
     507           0 :   return GNUNET_OK;
     508             : }
     509             : 
     510             : 
     511             : MHD_RESULT
     512           0 : TEH_handler_batch_deposit (struct TEH_RequestContext *rc,
     513             :                            const json_t *root,
     514             :                            const char *const args[])
     515             : {
     516           0 :   struct MHD_Connection *connection = rc->connection;
     517             :   struct BatchDepositContext dc;
     518             :   json_t *coins;
     519           0 :   bool no_refund_deadline = true;
     520           0 :   bool no_extensions = true;
     521             :   struct GNUNET_JSON_Specification spec[] = {
     522           0 :     GNUNET_JSON_spec_string ("merchant_payto_uri",
     523             :                              &dc.payto_uri),
     524           0 :     GNUNET_JSON_spec_fixed_auto ("wire_salt",
     525             :                                  &dc.wire_salt),
     526           0 :     GNUNET_JSON_spec_fixed_auto ("merchant_pub",
     527             :                                  &dc.merchant_pub),
     528           0 :     GNUNET_JSON_spec_fixed_auto ("h_contract_terms",
     529             :                                  &dc.h_contract_terms),
     530           0 :     GNUNET_JSON_spec_json ("coins",
     531             :                            &coins),
     532           0 :     GNUNET_JSON_spec_mark_optional (
     533             :       GNUNET_JSON_spec_json ("extension_details",
     534             :                              &dc.extension_details),
     535             :       &no_extensions),
     536           0 :     GNUNET_JSON_spec_timestamp ("timestamp",
     537             :                                 &dc.timestamp),
     538           0 :     GNUNET_JSON_spec_mark_optional (
     539             :       GNUNET_JSON_spec_timestamp ("refund_deadline",
     540             :                                   &dc.refund_deadline),
     541             :       &no_refund_deadline),
     542           0 :     GNUNET_JSON_spec_timestamp ("wire_transfer_deadline",
     543             :                                 &dc.wire_deadline),
     544           0 :     GNUNET_JSON_spec_end ()
     545             :   };
     546             :   enum GNUNET_GenericReturnValue res;
     547             : 
     548             :   (void) args;
     549           0 :   memset (&dc,
     550             :           0,
     551             :           sizeof (dc));
     552           0 :   res = TALER_MHD_parse_json_data (connection,
     553             :                                    root,
     554             :                                    spec);
     555           0 :   if (GNUNET_SYSERR == res)
     556             :   {
     557           0 :     GNUNET_break (0);
     558           0 :     return MHD_NO;   /* hard failure */
     559             :   }
     560           0 :   if (GNUNET_NO == res)
     561             :   {
     562           0 :     GNUNET_break_op (0);
     563           0 :     return MHD_YES;   /* failure */
     564             :   }
     565             : 
     566             :   /* validate merchant's wire details (as far as we can) */
     567             :   {
     568             :     char *emsg;
     569             : 
     570           0 :     emsg = TALER_payto_validate (dc.payto_uri);
     571           0 :     if (NULL != emsg)
     572             :     {
     573             :       MHD_RESULT ret;
     574             : 
     575           0 :       GNUNET_break_op (0);
     576           0 :       GNUNET_JSON_parse_free (spec);
     577           0 :       ret = TALER_MHD_reply_with_error (connection,
     578             :                                         MHD_HTTP_BAD_REQUEST,
     579             :                                         TALER_EC_GENERIC_PARAMETER_MALFORMED,
     580             :                                         emsg);
     581           0 :       GNUNET_free (emsg);
     582           0 :       return ret;
     583             :     }
     584             :   }
     585           0 :   if (GNUNET_TIME_timestamp_cmp (dc.refund_deadline,
     586             :                                  >,
     587             :                                  dc.wire_deadline))
     588             :   {
     589           0 :     GNUNET_break_op (0);
     590           0 :     GNUNET_JSON_parse_free (spec);
     591           0 :     return TALER_MHD_reply_with_error (connection,
     592             :                                        MHD_HTTP_BAD_REQUEST,
     593             :                                        TALER_EC_EXCHANGE_DEPOSIT_REFUND_DEADLINE_AFTER_WIRE_DEADLINE,
     594             :                                        NULL);
     595             :   }
     596           0 :   if (GNUNET_TIME_absolute_is_never (dc.wire_deadline.abs_time))
     597             :   {
     598           0 :     GNUNET_break_op (0);
     599           0 :     GNUNET_JSON_parse_free (spec);
     600           0 :     return TALER_MHD_reply_with_error (connection,
     601             :                                        MHD_HTTP_BAD_REQUEST,
     602             :                                        TALER_EC_EXCHANGE_DEPOSIT_WIRE_DEADLINE_IS_NEVER,
     603             :                                        NULL);
     604             :   }
     605           0 :   TALER_payto_hash (dc.payto_uri,
     606             :                     &dc.h_payto);
     607           0 :   TALER_merchant_wire_signature_hash (dc.payto_uri,
     608             :                                       &dc.wire_salt,
     609             :                                       &dc.h_wire);
     610             :   /* FIXME-OEC: #7270 hash actual extension JSON object here */
     611             :   // if (! no_extensions)
     612           0 :   memset (&dc.h_extensions,
     613             :           0,
     614             :           sizeof (dc.h_extensions));
     615           0 :   dc.num_coins = json_array_size (coins);
     616           0 :   if (0 == dc.num_coins)
     617             :   {
     618           0 :     GNUNET_break_op (0);
     619           0 :     GNUNET_JSON_parse_free (spec);
     620           0 :     return TALER_MHD_reply_with_error (connection,
     621             :                                        MHD_HTTP_BAD_REQUEST,
     622             :                                        TALER_EC_GENERIC_PARAMETER_MALFORMED,
     623             :                                        "coins");
     624             :   }
     625           0 :   if (TALER_MAX_FRESH_COINS < dc.num_coins)
     626             :   {
     627           0 :     GNUNET_break_op (0);
     628           0 :     GNUNET_JSON_parse_free (spec);
     629           0 :     return TALER_MHD_reply_with_error (connection,
     630             :                                        MHD_HTTP_BAD_REQUEST,
     631             :                                        TALER_EC_GENERIC_PARAMETER_MALFORMED,
     632             :                                        "coins");
     633             :   }
     634           0 :   dc.deposits = GNUNET_new_array (dc.num_coins,
     635             :                                   struct TALER_EXCHANGEDB_Deposit);
     636           0 :   for (unsigned int i = 0; i<dc.num_coins; i++)
     637             :   {
     638           0 :     if (GNUNET_OK !=
     639           0 :         (res = parse_coin (connection,
     640             :                            json_array_get (coins,
     641             :                                            i),
     642             :                            &dc,
     643           0 :                            &dc.deposits[i])))
     644             :     {
     645           0 :       for (unsigned int j = 0; j<i; j++)
     646           0 :         TALER_denom_sig_free (&dc.deposits[j].coin.denom_sig);
     647           0 :       GNUNET_free (dc.deposits);
     648           0 :       GNUNET_JSON_parse_free (spec);
     649           0 :       return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
     650             :     }
     651             :   }
     652             : 
     653           0 :   dc.exchange_timestamp = GNUNET_TIME_timestamp_get ();
     654           0 :   if (GNUNET_SYSERR ==
     655           0 :       TEH_plugin->preflight (TEH_plugin->cls))
     656             :   {
     657           0 :     GNUNET_break (0);
     658           0 :     GNUNET_JSON_parse_free (spec);
     659           0 :     return TALER_MHD_reply_with_error (connection,
     660             :                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
     661             :                                        TALER_EC_GENERIC_DB_START_FAILED,
     662             :                                        "preflight failure");
     663             :   }
     664             : 
     665             :   /* execute transaction */
     666             :   {
     667             :     MHD_RESULT mhd_ret;
     668             : 
     669           0 :     if (GNUNET_OK !=
     670           0 :         TEH_DB_run_transaction (connection,
     671             :                                 "execute batch deposit",
     672             :                                 TEH_MT_REQUEST_BATCH_DEPOSIT,
     673             :                                 &mhd_ret,
     674             :                                 &batch_deposit_transaction,
     675             :                                 &dc))
     676             :     {
     677           0 :       GNUNET_JSON_parse_free (spec);
     678           0 :       for (unsigned int j = 0; j<dc.num_coins; j++)
     679           0 :         TALER_denom_sig_free (&dc.deposits[j].coin.denom_sig);
     680           0 :       GNUNET_free (dc.deposits);
     681           0 :       GNUNET_JSON_parse_free (spec);
     682           0 :       return mhd_ret;
     683             :     }
     684             :   }
     685             : 
     686             :   /* generate regular response */
     687             :   {
     688             :     MHD_RESULT res;
     689             : 
     690           0 :     res = reply_batch_deposit_success (connection,
     691             :                                        &dc);
     692           0 :     for (unsigned int j = 0; j<dc.num_coins; j++)
     693           0 :       TALER_denom_sig_free (&dc.deposits[j].coin.denom_sig);
     694           0 :     GNUNET_free (dc.deposits);
     695           0 :     GNUNET_JSON_parse_free (spec);
     696           0 :     return res;
     697             :   }
     698             : }
     699             : 
     700             : 
     701             : /* end of taler-exchange-httpd_batch-deposit.c */

Generated by: LCOV version 1.14