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

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   Copyright (C) 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_deposit.c
      18             :  * @brief Handle /deposit requests; parses the POST and JSON and
      19             :  *        verifies the coin signature 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             :  * Send confirmation of deposit success to client.  This function
      41             :  * will create a signed message affirming the given information
      42             :  * and return it to the client.  By this, the exchange affirms that
      43             :  * the coin had sufficient (residual) value for the specified
      44             :  * transaction and that it will execute the requested deposit
      45             :  * operation with the given wiring details.
      46             :  *
      47             :  * @param connection connection to the client
      48             :  * @param coin_pub public key of the coin
      49             :  * @param h_wire hash of wire details
      50             :  * @param h_extensions hash of applicable extensions
      51             :  * @param h_contract_terms hash of contract details
      52             :  * @param exchange_timestamp exchange's timestamp
      53             :  * @param refund_deadline until when this deposit be refunded
      54             :  * @param wire_deadline until when will the exchange wire the funds
      55             :  * @param merchant merchant public key
      56             :  * @param amount_without_fee fraction of coin value to deposit, without the fee
      57             :  * @return MHD result code
      58             :  */
      59             : static MHD_RESULT
      60           0 : reply_deposit_success (
      61             :   struct MHD_Connection *connection,
      62             :   const struct TALER_CoinSpendPublicKeyP *coin_pub,
      63             :   const struct TALER_MerchantWireHashP *h_wire,
      64             :   const struct TALER_ExtensionContractHashP *h_extensions,
      65             :   const struct TALER_PrivateContractHashP *h_contract_terms,
      66             :   struct GNUNET_TIME_Timestamp exchange_timestamp,
      67             :   struct GNUNET_TIME_Timestamp refund_deadline,
      68             :   struct GNUNET_TIME_Timestamp wire_deadline,
      69             :   const struct TALER_MerchantPublicKeyP *merchant,
      70             :   const struct TALER_Amount *amount_without_fee)
      71             : {
      72             :   struct TALER_ExchangePublicKeyP pub;
      73             :   struct TALER_ExchangeSignatureP sig;
      74             :   enum TALER_ErrorCode ec;
      75             : 
      76           0 :   if (TALER_EC_NONE !=
      77           0 :       (ec = TALER_exchange_online_deposit_confirmation_sign (
      78             :          &TEH_keys_exchange_sign_,
      79             :          h_contract_terms,
      80             :          h_wire,
      81             :          h_extensions,
      82             :          exchange_timestamp,
      83             :          wire_deadline,
      84             :          refund_deadline,
      85             :          amount_without_fee,
      86             :          coin_pub,
      87             :          merchant,
      88             :          &pub,
      89             :          &sig)))
      90             :   {
      91           0 :     return TALER_MHD_reply_with_ec (connection,
      92             :                                     ec,
      93             :                                     NULL);
      94             :   }
      95           0 :   return TALER_MHD_REPLY_JSON_PACK (
      96             :     connection,
      97             :     MHD_HTTP_OK,
      98             :     GNUNET_JSON_pack_timestamp ("exchange_timestamp",
      99             :                                 exchange_timestamp),
     100             :     GNUNET_JSON_pack_data_auto ("exchange_sig",
     101             :                                 &sig),
     102             :     GNUNET_JSON_pack_data_auto ("exchange_pub",
     103             :                                 &pub));
     104             : }
     105             : 
     106             : 
     107             : /**
     108             :  * Closure for #deposit_transaction.
     109             :  */
     110             : struct DepositContext
     111             : {
     112             :   /**
     113             :    * Information about the deposit request.
     114             :    */
     115             :   const struct TALER_EXCHANGEDB_Deposit *deposit;
     116             : 
     117             :   /**
     118             :    * Our timestamp (when we received the request).
     119             :    * Possibly updated by the transaction if the
     120             :    * request is idempotent (was repeated).
     121             :    */
     122             :   struct GNUNET_TIME_Timestamp exchange_timestamp;
     123             : 
     124             :   /**
     125             :    * Hash of the payto URI.
     126             :    */
     127             :   struct TALER_PaytoHashP h_payto;
     128             : 
     129             :   /**
     130             :    * Row of of the coin in the known_coins table.
     131             :    */
     132             :   uint64_t known_coin_id;
     133             : 
     134             : };
     135             : 
     136             : 
     137             : /**
     138             :  * Execute database transaction for /deposit.  Runs the transaction
     139             :  * logic; IF it returns a non-error code, the transaction logic MUST
     140             :  * NOT queue a MHD response.  IF it returns an hard error, the
     141             :  * transaction logic MUST queue a MHD response and set @a mhd_ret.  IF
     142             :  * it returns the soft error code, the function MAY be called again to
     143             :  * retry and MUST not queue a MHD response.
     144             :  *
     145             :  * @param cls a `struct DepositContext`
     146             :  * @param connection MHD request context
     147             :  * @param[out] mhd_ret set to MHD status on error
     148             :  * @return transaction status
     149             :  */
     150             : static enum GNUNET_DB_QueryStatus
     151           0 : deposit_transaction (void *cls,
     152             :                      struct MHD_Connection *connection,
     153             :                      MHD_RESULT *mhd_ret)
     154             : {
     155           0 :   struct DepositContext *dc = cls;
     156             :   enum GNUNET_DB_QueryStatus qs;
     157             :   bool balance_ok;
     158             :   bool in_conflict;
     159             : 
     160           0 :   qs = TEH_make_coin_known (&dc->deposit->coin,
     161             :                             connection,
     162             :                             &dc->known_coin_id,
     163             :                             mhd_ret);
     164           0 :   if (qs < 0)
     165           0 :     return qs;
     166           0 :   qs = TEH_plugin->do_deposit (TEH_plugin->cls,
     167             :                                dc->deposit,
     168             :                                dc->known_coin_id,
     169           0 :                                &dc->h_payto,
     170             :                                false, /* FIXME-OEC: extension blocked #7270 */
     171             :                                &dc->exchange_timestamp,
     172             :                                &balance_ok,
     173             :                                &in_conflict);
     174           0 :   if (qs < 0)
     175             :   {
     176           0 :     if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
     177           0 :       return qs;
     178           0 :     TALER_LOG_WARNING ("Failed to store /deposit information in database\n");
     179           0 :     *mhd_ret = TALER_MHD_reply_with_error (connection,
     180             :                                            MHD_HTTP_INTERNAL_SERVER_ERROR,
     181             :                                            TALER_EC_GENERIC_DB_STORE_FAILED,
     182             :                                            "deposit");
     183           0 :     return qs;
     184             :   }
     185           0 :   if (in_conflict)
     186             :   {
     187             :     /* FIXME #7267: conflicting contract != insufficient funds */
     188             :     *mhd_ret
     189           0 :       = TEH_RESPONSE_reply_coin_insufficient_funds (
     190             :           connection,
     191             :           TALER_EC_EXCHANGE_DEPOSIT_CONFLICTING_CONTRACT,
     192           0 :           &dc->deposit->coin.denom_pub_hash,
     193           0 :           &dc->deposit->coin.coin_pub);
     194           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
     195             :   }
     196           0 :   if (! balance_ok)
     197             :   {
     198             :     *mhd_ret
     199           0 :       = TEH_RESPONSE_reply_coin_insufficient_funds (
     200             :           connection,
     201             :           TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS,
     202           0 :           &dc->deposit->coin.denom_pub_hash,
     203           0 :           &dc->deposit->coin.coin_pub);
     204           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
     205             :   }
     206           0 :   TEH_METRICS_num_success[TEH_MT_SUCCESS_DEPOSIT]++;
     207           0 :   return qs;
     208             : }
     209             : 
     210             : 
     211             : MHD_RESULT
     212           0 : TEH_handler_deposit (struct MHD_Connection *connection,
     213             :                      const struct TALER_CoinSpendPublicKeyP *coin_pub,
     214             :                      const json_t *root)
     215             : {
     216             :   struct DepositContext dc;
     217             :   struct TALER_EXCHANGEDB_Deposit deposit;
     218             :   const char *payto_uri;
     219             :   struct GNUNET_JSON_Specification spec[] = {
     220           0 :     GNUNET_JSON_spec_string ("merchant_payto_uri",
     221             :                              &payto_uri),
     222           0 :     GNUNET_JSON_spec_fixed_auto ("wire_salt",
     223             :                                  &deposit.wire_salt),
     224           0 :     TALER_JSON_spec_amount ("contribution",
     225             :                             TEH_currency,
     226             :                             &deposit.amount_with_fee),
     227           0 :     GNUNET_JSON_spec_fixed_auto ("denom_pub_hash",
     228             :                                  &deposit.coin.denom_pub_hash),
     229           0 :     TALER_JSON_spec_denom_sig ("ub_sig",
     230             :                                &deposit.coin.denom_sig),
     231           0 :     GNUNET_JSON_spec_fixed_auto ("merchant_pub",
     232             :                                  &deposit.merchant_pub),
     233           0 :     GNUNET_JSON_spec_fixed_auto ("h_contract_terms",
     234             :                                  &deposit.h_contract_terms),
     235           0 :     GNUNET_JSON_spec_mark_optional (
     236             :       GNUNET_JSON_spec_fixed_auto ("h_age_commitment",
     237             :                                    &deposit.coin.h_age_commitment),
     238             :       &deposit.coin.no_age_commitment),
     239           0 :     GNUNET_JSON_spec_fixed_auto ("coin_sig",
     240             :                                  &deposit.csig),
     241           0 :     GNUNET_JSON_spec_timestamp ("timestamp",
     242             :                                 &deposit.timestamp),
     243           0 :     GNUNET_JSON_spec_mark_optional (
     244             :       GNUNET_JSON_spec_timestamp ("refund_deadline",
     245             :                                   &deposit.refund_deadline),
     246             :       NULL),
     247           0 :     GNUNET_JSON_spec_timestamp ("wire_transfer_deadline",
     248             :                                 &deposit.wire_deadline),
     249           0 :     GNUNET_JSON_spec_end ()
     250             :   };
     251             :   struct TALER_MerchantWireHashP h_wire;
     252             : 
     253           0 :   memset (&deposit,
     254             :           0,
     255             :           sizeof (deposit));
     256           0 :   deposit.coin.coin_pub = *coin_pub;
     257             :   {
     258             :     enum GNUNET_GenericReturnValue res;
     259             : 
     260           0 :     res = TALER_MHD_parse_json_data (connection,
     261             :                                      root,
     262             :                                      spec);
     263           0 :     if (GNUNET_SYSERR == res)
     264             :     {
     265           0 :       GNUNET_break (0);
     266           0 :       return MHD_NO; /* hard failure */
     267             :     }
     268           0 :     if (GNUNET_NO == res)
     269             :     {
     270           0 :       GNUNET_break_op (0);
     271           0 :       return MHD_YES; /* failure */
     272             :     }
     273             :   }
     274             :   /* validate merchant's wire details (as far as we can) */
     275             :   {
     276             :     char *emsg;
     277             : 
     278           0 :     emsg = TALER_payto_validate (payto_uri);
     279           0 :     if (NULL != emsg)
     280             :     {
     281             :       MHD_RESULT ret;
     282             : 
     283           0 :       GNUNET_break_op (0);
     284           0 :       GNUNET_JSON_parse_free (spec);
     285           0 :       ret = TALER_MHD_reply_with_error (connection,
     286             :                                         MHD_HTTP_BAD_REQUEST,
     287             :                                         TALER_EC_GENERIC_PARAMETER_MALFORMED,
     288             :                                         emsg);
     289           0 :       GNUNET_free (emsg);
     290           0 :       return ret;
     291             :     }
     292             :   }
     293           0 :   if (GNUNET_TIME_timestamp_cmp (deposit.refund_deadline,
     294             :                                  >,
     295             :                                  deposit.wire_deadline))
     296             :   {
     297           0 :     GNUNET_break_op (0);
     298           0 :     GNUNET_JSON_parse_free (spec);
     299           0 :     return TALER_MHD_reply_with_error (connection,
     300             :                                        MHD_HTTP_BAD_REQUEST,
     301             :                                        TALER_EC_EXCHANGE_DEPOSIT_REFUND_DEADLINE_AFTER_WIRE_DEADLINE,
     302             :                                        NULL);
     303             :   }
     304           0 :   if (GNUNET_TIME_absolute_is_never (deposit.wire_deadline.abs_time))
     305             :   {
     306           0 :     GNUNET_break_op (0);
     307           0 :     GNUNET_JSON_parse_free (spec);
     308           0 :     return TALER_MHD_reply_with_error (connection,
     309             :                                        MHD_HTTP_BAD_REQUEST,
     310             :                                        TALER_EC_EXCHANGE_DEPOSIT_WIRE_DEADLINE_IS_NEVER,
     311             :                                        NULL);
     312             :   }
     313           0 :   deposit.receiver_wire_account = (char *) payto_uri;
     314           0 :   TALER_payto_hash (payto_uri,
     315             :                     &dc.h_payto);
     316           0 :   TALER_merchant_wire_signature_hash (payto_uri,
     317             :                                       &deposit.wire_salt,
     318             :                                       &h_wire);
     319           0 :   dc.deposit = &deposit;
     320             : 
     321             :   /* new deposit */
     322           0 :   dc.exchange_timestamp = GNUNET_TIME_timestamp_get ();
     323             :   /* check denomination exists and is valid */
     324             :   {
     325             :     struct TEH_DenominationKey *dk;
     326             :     MHD_RESULT mret;
     327             : 
     328           0 :     dk = TEH_keys_denomination_by_hash (&deposit.coin.denom_pub_hash,
     329             :                                         connection,
     330             :                                         &mret);
     331           0 :     if (NULL == dk)
     332             :     {
     333           0 :       GNUNET_JSON_parse_free (spec);
     334           0 :       return mret;
     335             :     }
     336           0 :     if (0 > TALER_amount_cmp (&dk->meta.value,
     337             :                               &deposit.amount_with_fee))
     338             :     {
     339           0 :       GNUNET_break_op (0);
     340           0 :       GNUNET_JSON_parse_free (spec);
     341           0 :       return TALER_MHD_reply_with_error (connection,
     342             :                                          MHD_HTTP_BAD_REQUEST,
     343             :                                          TALER_EC_EXCHANGE_GENERIC_AMOUNT_EXCEEDS_DENOMINATION_VALUE,
     344             :                                          NULL);
     345             :     }
     346           0 :     if (GNUNET_TIME_absolute_is_past (dk->meta.expire_deposit.abs_time))
     347             :     {
     348             :       /* This denomination is past the expiration time for deposits */
     349           0 :       GNUNET_JSON_parse_free (spec);
     350           0 :       return TEH_RESPONSE_reply_expired_denom_pub_hash (
     351             :         connection,
     352             :         &deposit.coin.denom_pub_hash,
     353             :         TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED,
     354             :         "DEPOSIT");
     355             :     }
     356           0 :     if (GNUNET_TIME_absolute_is_future (dk->meta.start.abs_time))
     357             :     {
     358             :       /* This denomination is not yet valid */
     359           0 :       GNUNET_JSON_parse_free (spec);
     360           0 :       return TEH_RESPONSE_reply_expired_denom_pub_hash (
     361             :         connection,
     362             :         &deposit.coin.denom_pub_hash,
     363             :         TALER_EC_EXCHANGE_GENERIC_DENOMINATION_VALIDITY_IN_FUTURE,
     364             :         "DEPOSIT");
     365             :     }
     366           0 :     if (dk->recoup_possible)
     367             :     {
     368             :       /* This denomination has been revoked */
     369           0 :       GNUNET_JSON_parse_free (spec);
     370           0 :       return TEH_RESPONSE_reply_expired_denom_pub_hash (
     371             :         connection,
     372             :         &deposit.coin.denom_pub_hash,
     373             :         TALER_EC_EXCHANGE_GENERIC_DENOMINATION_REVOKED,
     374             :         "DEPOSIT");
     375             :     }
     376           0 :     if (dk->denom_pub.cipher != deposit.coin.denom_sig.cipher)
     377             :     {
     378             :       /* denomination cipher and denomination signature cipher not the same */
     379           0 :       GNUNET_JSON_parse_free (spec);
     380           0 :       return TALER_MHD_reply_with_error (connection,
     381             :                                          MHD_HTTP_BAD_REQUEST,
     382             :                                          TALER_EC_EXCHANGE_GENERIC_CIPHER_MISMATCH,
     383             :                                          NULL);
     384             :     }
     385             : 
     386           0 :     deposit.deposit_fee = dk->meta.fees.deposit;
     387             :     /* check coin signature */
     388           0 :     switch (dk->denom_pub.cipher)
     389             :     {
     390           0 :     case TALER_DENOMINATION_RSA:
     391           0 :       TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_RSA]++;
     392           0 :       break;
     393           0 :     case TALER_DENOMINATION_CS:
     394           0 :       TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_CS]++;
     395           0 :       break;
     396           0 :     default:
     397           0 :       break;
     398             :     }
     399           0 :     if (GNUNET_YES !=
     400           0 :         TALER_test_coin_valid (&deposit.coin,
     401           0 :                                &dk->denom_pub))
     402             :     {
     403           0 :       TALER_LOG_WARNING ("Invalid coin passed for /deposit\n");
     404           0 :       GNUNET_JSON_parse_free (spec);
     405           0 :       return TALER_MHD_reply_with_error (connection,
     406             :                                          MHD_HTTP_FORBIDDEN,
     407             :                                          TALER_EC_EXCHANGE_DENOMINATION_SIGNATURE_INVALID,
     408             :                                          NULL);
     409             :     }
     410             :   }
     411           0 :   if (0 < TALER_amount_cmp (&deposit.deposit_fee,
     412             :                             &deposit.amount_with_fee))
     413             :   {
     414           0 :     GNUNET_break_op (0);
     415           0 :     GNUNET_JSON_parse_free (spec);
     416           0 :     return TALER_MHD_reply_with_error (connection,
     417             :                                        MHD_HTTP_BAD_REQUEST,
     418             :                                        TALER_EC_EXCHANGE_DEPOSIT_NEGATIVE_VALUE_AFTER_FEE,
     419             :                                        NULL);
     420             :   }
     421             : 
     422           0 :   TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
     423           0 :   if (GNUNET_OK !=
     424           0 :       TALER_wallet_deposit_verify (&deposit.amount_with_fee,
     425             :                                    &deposit.deposit_fee,
     426             :                                    &h_wire,
     427             :                                    &deposit.h_contract_terms,
     428             :                                    &deposit.coin.h_age_commitment,
     429             :                                    NULL /* FIXME: h_extensions! */,
     430             :                                    &deposit.coin.denom_pub_hash,
     431             :                                    deposit.timestamp,
     432             :                                    &deposit.merchant_pub,
     433             :                                    deposit.refund_deadline,
     434             :                                    &deposit.coin.coin_pub,
     435             :                                    &deposit.csig))
     436             :   {
     437           0 :     TALER_LOG_WARNING ("Invalid signature on /deposit request\n");
     438           0 :     GNUNET_JSON_parse_free (spec);
     439           0 :     return TALER_MHD_reply_with_error (connection,
     440             :                                        MHD_HTTP_FORBIDDEN,
     441             :                                        TALER_EC_EXCHANGE_DEPOSIT_COIN_SIGNATURE_INVALID,
     442             :                                        NULL);
     443             :   }
     444             : 
     445           0 :   if (GNUNET_SYSERR ==
     446           0 :       TEH_plugin->preflight (TEH_plugin->cls))
     447             :   {
     448           0 :     GNUNET_break (0);
     449           0 :     return TALER_MHD_reply_with_error (connection,
     450             :                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
     451             :                                        TALER_EC_GENERIC_DB_START_FAILED,
     452             :                                        "preflight failure");
     453             :   }
     454             : 
     455             :   /* execute transaction */
     456             :   {
     457             :     MHD_RESULT mhd_ret;
     458             : 
     459           0 :     if (GNUNET_OK !=
     460           0 :         TEH_DB_run_transaction (connection,
     461             :                                 "execute deposit",
     462             :                                 TEH_MT_REQUEST_DEPOSIT,
     463             :                                 &mhd_ret,
     464             :                                 &deposit_transaction,
     465             :                                 &dc))
     466             :     {
     467           0 :       GNUNET_JSON_parse_free (spec);
     468           0 :       return mhd_ret;
     469             :     }
     470             :   }
     471             : 
     472             :   /* generate regular response */
     473             :   {
     474             :     struct TALER_Amount amount_without_fee;
     475             :     MHD_RESULT res;
     476             : 
     477           0 :     GNUNET_assert (0 <=
     478             :                    TALER_amount_subtract (&amount_without_fee,
     479             :                                           &deposit.amount_with_fee,
     480             :                                           &deposit.deposit_fee));
     481           0 :     res = reply_deposit_success (connection,
     482             :                                  &deposit.coin.coin_pub,
     483             :                                  &h_wire,
     484             :                                  NULL /* FIXME: h_extensions! */,
     485             :                                  &deposit.h_contract_terms,
     486             :                                  dc.exchange_timestamp,
     487             :                                  deposit.refund_deadline,
     488             :                                  deposit.wire_deadline,
     489             :                                  &deposit.merchant_pub,
     490             :                                  &amount_without_fee);
     491           0 :     GNUNET_JSON_parse_free (spec);
     492           0 :     return res;
     493             :   }
     494             : }
     495             : 
     496             : 
     497             : /* end of taler-exchange-httpd_deposit.c */

Generated by: LCOV version 1.14