exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

exchange_api_deposits_get.c (12790B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2014-2023 Taler Systems SA
      4 
      5   TALER is free software; you can redistribute it and/or modify it under the
      6   terms of the GNU 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 General Public License for more details.
     12 
     13   You should have received a copy of the GNU General Public License along with
     14   TALER; see the file COPYING.  If not, see
     15   <http://www.gnu.org/licenses/>
     16 */
     17 /**
     18  * @file lib/exchange_api_deposits_get.c
     19  * @brief Implementation of the /deposits/ GET request
     20  * @author Christian Grothoff
     21  */
     22 #include "taler/platform.h"
     23 #include <jansson.h>
     24 #include <microhttpd.h> /* just for HTTP status codes */
     25 #include <gnunet/gnunet_util_lib.h>
     26 #include <gnunet/gnunet_json_lib.h>
     27 #include <gnunet/gnunet_curl_lib.h>
     28 #include "taler/taler_json_lib.h"
     29 #include "taler/taler_exchange_service.h"
     30 #include "exchange_api_handle.h"
     31 #include "taler/taler_signatures.h"
     32 #include "exchange_api_curl_defaults.h"
     33 
     34 
     35 /**
     36  * @brief A Deposit Get Handle
     37  */
     38 struct TALER_EXCHANGE_DepositGetHandle
     39 {
     40 
     41   /**
     42    * The keys of the this request handle will use
     43    */
     44   struct TALER_EXCHANGE_Keys *keys;
     45 
     46   /**
     47    * The url for this request.
     48    */
     49   char *url;
     50 
     51   /**
     52    * Context for #TEH_curl_easy_post(). Keeps the data that must
     53    * persist for Curl to make the upload.
     54    */
     55   struct TALER_CURL_PostContext ctx;
     56 
     57   /**
     58    * Handle for the request.
     59    */
     60   struct GNUNET_CURL_Job *job;
     61 
     62   /**
     63    * Function to call with the result.
     64    */
     65   TALER_EXCHANGE_DepositGetCallback cb;
     66 
     67   /**
     68    * Closure for @a cb.
     69    */
     70   void *cb_cls;
     71 
     72   /**
     73    * Hash over the wiring information of the merchant.
     74    */
     75   struct TALER_MerchantWireHashP h_wire;
     76 
     77   /**
     78    * Hash over the contract for which this deposit is made.
     79    */
     80   struct TALER_PrivateContractHashP h_contract_terms;
     81 
     82   /**
     83    * The coin's public key.  This is the value that must have been
     84    * signed (blindly) by the Exchange.
     85    */
     86   struct TALER_CoinSpendPublicKeyP coin_pub;
     87 
     88 };
     89 
     90 
     91 /**
     92  * Function called when we're done processing the
     93  * HTTP /track/transaction request.
     94  *
     95  * @param cls the `struct TALER_EXCHANGE_DepositGetHandle`
     96  * @param response_code HTTP response code, 0 on error
     97  * @param response parsed JSON result, NULL on error
     98  */
     99 static void
    100 handle_deposit_wtid_finished (void *cls,
    101                               long response_code,
    102                               const void *response)
    103 {
    104   struct TALER_EXCHANGE_DepositGetHandle *dwh = cls;
    105   const json_t *j = response;
    106   struct TALER_EXCHANGE_GetDepositResponse dr = {
    107     .hr.reply = j,
    108     .hr.http_status = (unsigned int) response_code
    109   };
    110 
    111   dwh->job = NULL;
    112   switch (response_code)
    113   {
    114   case 0:
    115     dr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    116     break;
    117   case MHD_HTTP_OK:
    118     {
    119       struct GNUNET_JSON_Specification spec[] = {
    120         GNUNET_JSON_spec_fixed_auto (
    121           "wtid",
    122           &dr.details.ok.wtid),
    123         GNUNET_JSON_spec_timestamp (
    124           "execution_time",
    125           &dr.details.ok.execution_time),
    126         TALER_JSON_spec_amount_any (
    127           "coin_contribution",
    128           &dr.details.ok.coin_contribution),
    129         GNUNET_JSON_spec_fixed_auto (
    130           "exchange_sig",
    131           &dr.details.ok.exchange_sig),
    132         GNUNET_JSON_spec_fixed_auto (
    133           "exchange_pub",
    134           &dr.details.ok.exchange_pub),
    135         GNUNET_JSON_spec_end ()
    136       };
    137       const struct TALER_EXCHANGE_Keys *key_state;
    138 
    139       key_state = dwh->keys;
    140       GNUNET_assert (NULL != key_state);
    141       if (GNUNET_OK !=
    142           GNUNET_JSON_parse (j,
    143                              spec,
    144                              NULL, NULL))
    145       {
    146         GNUNET_break_op (0);
    147         dr.hr.http_status = 0;
    148         dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
    149         break;
    150       }
    151       if (GNUNET_OK !=
    152           TALER_EXCHANGE_test_signing_key (key_state,
    153                                            &dr.details.ok.exchange_pub))
    154       {
    155         GNUNET_break_op (0);
    156         dr.hr.http_status = 0;
    157         dr.hr.ec = TALER_EC_EXCHANGE_DEPOSITS_GET_INVALID_SIGNATURE_BY_EXCHANGE;
    158         break;
    159       }
    160       if (GNUNET_OK !=
    161           TALER_exchange_online_confirm_wire_verify (
    162             &dwh->h_wire,
    163             &dwh->h_contract_terms,
    164             &dr.details.ok.wtid,
    165             &dwh->coin_pub,
    166             dr.details.ok.execution_time,
    167             &dr.details.ok.coin_contribution,
    168             &dr.details.ok.exchange_pub,
    169             &dr.details.ok.exchange_sig))
    170       {
    171         GNUNET_break_op (0);
    172         dr.hr.http_status = 0;
    173         dr.hr.ec = TALER_EC_EXCHANGE_DEPOSITS_GET_INVALID_SIGNATURE_BY_EXCHANGE;
    174         break;
    175       }
    176       dwh->cb (dwh->cb_cls,
    177                &dr);
    178       TALER_EXCHANGE_deposits_get_cancel (dwh);
    179       return;
    180     }
    181   case MHD_HTTP_ACCEPTED:
    182     {
    183       /* Transaction known, but not executed yet */
    184       bool no_legi = false;
    185       struct GNUNET_JSON_Specification spec[] = {
    186         GNUNET_JSON_spec_timestamp (
    187           "execution_time",
    188           &dr.details.accepted.execution_time),
    189         GNUNET_JSON_spec_mark_optional (
    190           GNUNET_JSON_spec_fixed_auto (
    191             "account_pub",
    192             &dr.details.accepted.account_pub),
    193           NULL),
    194         GNUNET_JSON_spec_mark_optional (
    195           GNUNET_JSON_spec_uint64 (
    196             "requirement_row",
    197             &dr.details.accepted.requirement_row),
    198           &no_legi),
    199         GNUNET_JSON_spec_bool (
    200           "kyc_ok",
    201           &dr.details.accepted.kyc_ok),
    202         GNUNET_JSON_spec_end ()
    203       };
    204 
    205       if (GNUNET_OK !=
    206           GNUNET_JSON_parse (j,
    207                              spec,
    208                              NULL, NULL))
    209       {
    210         GNUNET_break_op (0);
    211         dr.hr.http_status = 0;
    212         dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
    213         break;
    214       }
    215       if (no_legi)
    216         dr.details.accepted.requirement_row = 0;
    217       dwh->cb (dwh->cb_cls,
    218                &dr);
    219       TALER_EXCHANGE_deposits_get_cancel (dwh);
    220       return;
    221     }
    222   case MHD_HTTP_BAD_REQUEST:
    223     dr.hr.ec = TALER_JSON_get_error_code (j);
    224     dr.hr.hint = TALER_JSON_get_error_hint (j);
    225     /* This should never happen, either us or the exchange is buggy
    226        (or API version conflict); just pass JSON reply to the application */
    227     break;
    228   case MHD_HTTP_FORBIDDEN:
    229     dr.hr.ec = TALER_JSON_get_error_code (j);
    230     dr.hr.hint = TALER_JSON_get_error_hint (j);
    231     /* Nothing really to verify, exchange says one of the signatures is
    232        invalid; as we checked them, this should never happen, we
    233        should pass the JSON reply to the application */
    234     break;
    235   case MHD_HTTP_NOT_FOUND:
    236     dr.hr.ec = TALER_JSON_get_error_code (j);
    237     dr.hr.hint = TALER_JSON_get_error_hint (j);
    238     /* Exchange does not know about transaction;
    239        we should pass the reply to the application */
    240     break;
    241   case MHD_HTTP_INTERNAL_SERVER_ERROR:
    242     dr.hr.ec = TALER_JSON_get_error_code (j);
    243     dr.hr.hint = TALER_JSON_get_error_hint (j);
    244     /* Server had an internal issue; we should retry, but this API
    245        leaves this to the application */
    246     break;
    247   default:
    248     /* unexpected response code */
    249     dr.hr.ec = TALER_JSON_get_error_code (j);
    250     dr.hr.hint = TALER_JSON_get_error_hint (j);
    251     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    252                 "Unexpected response code %u/%d for exchange GET deposits\n",
    253                 (unsigned int) response_code,
    254                 (int) dr.hr.ec);
    255     GNUNET_break_op (0);
    256     break;
    257   }
    258   dwh->cb (dwh->cb_cls,
    259            &dr);
    260   TALER_EXCHANGE_deposits_get_cancel (dwh);
    261 }
    262 
    263 
    264 struct TALER_EXCHANGE_DepositGetHandle *
    265 TALER_EXCHANGE_deposits_get (
    266   struct GNUNET_CURL_Context *ctx,
    267   const char *url,
    268   struct TALER_EXCHANGE_Keys *keys,
    269   const struct TALER_MerchantPrivateKeyP *merchant_priv,
    270   const struct TALER_MerchantWireHashP *h_wire,
    271   const struct TALER_PrivateContractHashP *h_contract_terms,
    272   const struct TALER_CoinSpendPublicKeyP *coin_pub,
    273   struct GNUNET_TIME_Relative timeout,
    274   TALER_EXCHANGE_DepositGetCallback cb,
    275   void *cb_cls)
    276 {
    277   struct TALER_MerchantPublicKeyP merchant;
    278   struct TALER_MerchantSignatureP merchant_sig;
    279   struct TALER_EXCHANGE_DepositGetHandle *dwh;
    280   CURL *eh;
    281   char arg_str[(sizeof (struct TALER_CoinSpendPublicKeyP)
    282                 + sizeof (struct TALER_MerchantWireHashP)
    283                 + sizeof (struct TALER_MerchantPublicKeyP)
    284                 + sizeof (struct TALER_PrivateContractHashP)
    285                 + sizeof (struct TALER_MerchantSignatureP)) * 2 + 48];
    286   unsigned int tms
    287     = (unsigned int) timeout.rel_value_us
    288       / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us;
    289 
    290   GNUNET_CRYPTO_eddsa_key_get_public (&merchant_priv->eddsa_priv,
    291                                       &merchant.eddsa_pub);
    292   TALER_merchant_deposit_sign (h_contract_terms,
    293                                h_wire,
    294                                coin_pub,
    295                                merchant_priv,
    296                                &merchant_sig);
    297   {
    298     char cpub_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2];
    299     char mpub_str[sizeof (struct TALER_MerchantPublicKeyP) * 2];
    300     char msig_str[sizeof (struct TALER_MerchantSignatureP) * 2];
    301     char chash_str[sizeof (struct TALER_PrivateContractHashP) * 2];
    302     char whash_str[sizeof (struct TALER_MerchantWireHashP) * 2];
    303     char timeout_str[24];
    304     char *end;
    305 
    306     end = GNUNET_STRINGS_data_to_string (h_wire,
    307                                          sizeof (*h_wire),
    308                                          whash_str,
    309                                          sizeof (whash_str));
    310     *end = '\0';
    311     end = GNUNET_STRINGS_data_to_string (&merchant,
    312                                          sizeof (merchant),
    313                                          mpub_str,
    314                                          sizeof (mpub_str));
    315     *end = '\0';
    316     end = GNUNET_STRINGS_data_to_string (h_contract_terms,
    317                                          sizeof (*h_contract_terms),
    318                                          chash_str,
    319                                          sizeof (chash_str));
    320     *end = '\0';
    321     end = GNUNET_STRINGS_data_to_string (coin_pub,
    322                                          sizeof (*coin_pub),
    323                                          cpub_str,
    324                                          sizeof (cpub_str));
    325     *end = '\0';
    326     end = GNUNET_STRINGS_data_to_string (&merchant_sig,
    327                                          sizeof (merchant_sig),
    328                                          msig_str,
    329                                          sizeof (msig_str));
    330     *end = '\0';
    331     if (GNUNET_TIME_relative_is_zero (timeout))
    332     {
    333       timeout_str[0] = '\0';
    334     }
    335     else
    336     {
    337       GNUNET_snprintf (
    338         timeout_str,
    339         sizeof (timeout_str),
    340         "%u",
    341         tms);
    342     }
    343 
    344     GNUNET_snprintf (arg_str,
    345                      sizeof (arg_str),
    346                      "deposits/%s/%s/%s/%s?merchant_sig=%s%s%s",
    347                      whash_str,
    348                      mpub_str,
    349                      chash_str,
    350                      cpub_str,
    351                      msig_str,
    352                      0 == tms
    353                      ? ""
    354                      : "&timeout_ms=",
    355                      timeout_str);
    356   }
    357 
    358   dwh = GNUNET_new (struct TALER_EXCHANGE_DepositGetHandle);
    359   dwh->cb = cb;
    360   dwh->cb_cls = cb_cls;
    361   dwh->url = TALER_url_join (url,
    362                              arg_str,
    363                              NULL);
    364   if (NULL == dwh->url)
    365   {
    366     GNUNET_free (dwh);
    367     return NULL;
    368   }
    369   dwh->h_wire = *h_wire;
    370   dwh->h_contract_terms = *h_contract_terms;
    371   dwh->coin_pub = *coin_pub;
    372   eh = TALER_EXCHANGE_curl_easy_get_ (dwh->url);
    373   if (NULL == eh)
    374   {
    375     GNUNET_break (0);
    376     GNUNET_free (dwh->url);
    377     GNUNET_free (dwh);
    378     return NULL;
    379   }
    380   if (0 != tms)
    381   {
    382     GNUNET_break (CURLE_OK ==
    383                   curl_easy_setopt (eh,
    384                                     CURLOPT_TIMEOUT_MS,
    385                                     (long) (tms + 100L)));
    386   }
    387   dwh->job = GNUNET_CURL_job_add (ctx,
    388                                   eh,
    389                                   &handle_deposit_wtid_finished,
    390                                   dwh);
    391   dwh->keys = TALER_EXCHANGE_keys_incref (keys);
    392   return dwh;
    393 }
    394 
    395 
    396 void
    397 TALER_EXCHANGE_deposits_get_cancel (struct TALER_EXCHANGE_DepositGetHandle *dwh)
    398 {
    399   if (NULL != dwh->job)
    400   {
    401     GNUNET_CURL_job_cancel (dwh->job);
    402     dwh->job = NULL;
    403   }
    404   GNUNET_free (dwh->url);
    405   TALER_curl_easy_post_finished (&dwh->ctx);
    406   TALER_EXCHANGE_keys_decref (dwh->keys);
    407   GNUNET_free (dwh);
    408 }
    409 
    410 
    411 /* end of exchange_api_deposits_get.c */