exchange

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

exchange_api_get-deposits-H_WIRE-MERCHANT_PUB-H_CONTRACT_TERMS-COIN_PUB.c (14366B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2014-2026 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_get-deposits-H_WIRE-MERCHANT_PUB-H_CONTRACT_TERMS-COIN_PUB.c
     19  * @brief Implementation of the GET /deposits/... 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 GET /deposits/... Handle
     37  */
     38 struct TALER_EXCHANGE_GetDepositsHandle
     39 {
     40 
     41   /**
     42    * Base URL of the exchange.
     43    */
     44   char *base_url;
     45 
     46   /**
     47    * The url for this request.
     48    */
     49   char *url;
     50 
     51   /**
     52    * The keys of this request handle will use.
     53    */
     54   struct TALER_EXCHANGE_Keys *keys;
     55 
     56   /**
     57    * Handle for the request.
     58    */
     59   struct GNUNET_CURL_Job *job;
     60 
     61   /**
     62    * Function to call with the result.
     63    */
     64   TALER_EXCHANGE_GetDepositsCallback cb;
     65 
     66   /**
     67    * Closure for @e cb.
     68    */
     69   TALER_EXCHANGE_GET_DEPOSITS_RESULT_CLOSURE *cb_cls;
     70 
     71   /**
     72    * CURL context to use.
     73    */
     74   struct GNUNET_CURL_Context *ctx;
     75 
     76   /**
     77    * Private key of the merchant.
     78    */
     79   struct TALER_MerchantPrivateKeyP merchant_priv;
     80 
     81   /**
     82    * Hash over the wiring information of the merchant.
     83    */
     84   struct TALER_MerchantWireHashP h_wire;
     85 
     86   /**
     87    * Hash over the contract for which this deposit is made.
     88    */
     89   struct TALER_PrivateContractHashP h_contract_terms;
     90 
     91   /**
     92    * The coin's public key.
     93    */
     94   struct TALER_CoinSpendPublicKeyP coin_pub;
     95 
     96   /**
     97    * Options set for this request.
     98    */
     99   struct
    100   {
    101     /**
    102      * How long to wait for the wire transfer, enabling long polling.
    103      * Default: zero (no long polling).
    104      */
    105     struct GNUNET_TIME_Relative timeout;
    106   } options;
    107 
    108 };
    109 
    110 
    111 /**
    112  * Function called when we're done processing the
    113  * HTTP GET /deposits/... request.
    114  *
    115  * @param cls the `struct TALER_EXCHANGE_GetDepositsHandle`
    116  * @param response_code HTTP response code, 0 on error
    117  * @param response parsed JSON result, NULL on error
    118  */
    119 static void
    120 handle_deposit_wtid_finished (void *cls,
    121                               long response_code,
    122                               const void *response)
    123 {
    124   struct TALER_EXCHANGE_GetDepositsHandle *gdh = cls;
    125   const json_t *j = response;
    126   struct TALER_EXCHANGE_GetDepositsResponse dr = {
    127     .hr.reply = j,
    128     .hr.http_status = (unsigned int) response_code
    129   };
    130 
    131   gdh->job = NULL;
    132   switch (response_code)
    133   {
    134   case 0:
    135     dr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    136     break;
    137   case MHD_HTTP_OK:
    138     {
    139       struct GNUNET_JSON_Specification spec[] = {
    140         GNUNET_JSON_spec_fixed_auto (
    141           "wtid",
    142           &dr.details.ok.wtid),
    143         GNUNET_JSON_spec_timestamp (
    144           "execution_time",
    145           &dr.details.ok.execution_time),
    146         TALER_JSON_spec_amount_any (
    147           "coin_contribution",
    148           &dr.details.ok.coin_contribution),
    149         GNUNET_JSON_spec_fixed_auto (
    150           "exchange_sig",
    151           &dr.details.ok.exchange_sig),
    152         GNUNET_JSON_spec_fixed_auto (
    153           "exchange_pub",
    154           &dr.details.ok.exchange_pub),
    155         GNUNET_JSON_spec_end ()
    156       };
    157 
    158       GNUNET_assert (NULL != gdh->keys);
    159       if (GNUNET_OK !=
    160           GNUNET_JSON_parse (j,
    161                              spec,
    162                              NULL, NULL))
    163       {
    164         GNUNET_break_op (0);
    165         dr.hr.http_status = 0;
    166         dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
    167         break;
    168       }
    169       if (GNUNET_OK !=
    170           TALER_EXCHANGE_test_signing_key (gdh->keys,
    171                                            &dr.details.ok.exchange_pub))
    172       {
    173         GNUNET_break_op (0);
    174         dr.hr.http_status = 0;
    175         dr.hr.ec = TALER_EC_EXCHANGE_DEPOSITS_GET_INVALID_SIGNATURE_BY_EXCHANGE;
    176         break;
    177       }
    178       if (GNUNET_OK !=
    179           TALER_exchange_online_confirm_wire_verify (
    180             &gdh->h_wire,
    181             &gdh->h_contract_terms,
    182             &dr.details.ok.wtid,
    183             &gdh->coin_pub,
    184             dr.details.ok.execution_time,
    185             &dr.details.ok.coin_contribution,
    186             &dr.details.ok.exchange_pub,
    187             &dr.details.ok.exchange_sig))
    188       {
    189         GNUNET_break_op (0);
    190         dr.hr.http_status = 0;
    191         dr.hr.ec = TALER_EC_EXCHANGE_DEPOSITS_GET_INVALID_SIGNATURE_BY_EXCHANGE;
    192         break;
    193       }
    194       gdh->cb (gdh->cb_cls,
    195                &dr);
    196       gdh->cb = NULL;
    197       TALER_EXCHANGE_get_deposits_cancel (gdh);
    198       return;
    199     }
    200   case MHD_HTTP_ACCEPTED:
    201     {
    202       /* Transaction known, but not executed yet */
    203       bool no_legi = false;
    204       struct GNUNET_JSON_Specification spec[] = {
    205         GNUNET_JSON_spec_timestamp (
    206           "execution_time",
    207           &dr.details.accepted.execution_time),
    208         GNUNET_JSON_spec_mark_optional (
    209           GNUNET_JSON_spec_fixed_auto (
    210             "account_pub",
    211             &dr.details.accepted.account_pub),
    212           NULL),
    213         GNUNET_JSON_spec_mark_optional (
    214           GNUNET_JSON_spec_uint64 (
    215             "requirement_row",
    216             &dr.details.accepted.requirement_row),
    217           &no_legi),
    218         GNUNET_JSON_spec_bool (
    219           "kyc_ok",
    220           &dr.details.accepted.kyc_ok),
    221         GNUNET_JSON_spec_end ()
    222       };
    223 
    224       if (GNUNET_OK !=
    225           GNUNET_JSON_parse (j,
    226                              spec,
    227                              NULL, NULL))
    228       {
    229         GNUNET_break_op (0);
    230         dr.hr.http_status = 0;
    231         dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
    232         break;
    233       }
    234       if (no_legi)
    235         dr.details.accepted.requirement_row = 0;
    236       gdh->cb (gdh->cb_cls,
    237                &dr);
    238       gdh->cb = NULL;
    239       TALER_EXCHANGE_get_deposits_cancel (gdh);
    240       return;
    241     }
    242   case MHD_HTTP_BAD_REQUEST:
    243     dr.hr.ec = TALER_JSON_get_error_code (j);
    244     dr.hr.hint = TALER_JSON_get_error_hint (j);
    245     /* This should never happen, either us or the exchange is buggy
    246        (or API version conflict); just pass JSON reply to the application */
    247     break;
    248   case MHD_HTTP_FORBIDDEN:
    249     dr.hr.ec = TALER_JSON_get_error_code (j);
    250     dr.hr.hint = TALER_JSON_get_error_hint (j);
    251     /* Nothing really to verify, exchange says one of the signatures is
    252        invalid; as we checked them, this should never happen, we
    253        should pass the JSON reply to the application */
    254     break;
    255   case MHD_HTTP_NOT_FOUND:
    256     dr.hr.ec = TALER_JSON_get_error_code (j);
    257     dr.hr.hint = TALER_JSON_get_error_hint (j);
    258     /* Exchange does not know about transaction;
    259        we should pass the reply to the application */
    260     break;
    261   case MHD_HTTP_INTERNAL_SERVER_ERROR:
    262     dr.hr.ec = TALER_JSON_get_error_code (j);
    263     dr.hr.hint = TALER_JSON_get_error_hint (j);
    264     /* Server had an internal issue; we should retry, but this API
    265        leaves this to the application */
    266     break;
    267   default:
    268     /* unexpected response code */
    269     dr.hr.ec = TALER_JSON_get_error_code (j);
    270     dr.hr.hint = TALER_JSON_get_error_hint (j);
    271     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    272                 "Unexpected response code %u/%d for exchange GET deposits\n",
    273                 (unsigned int) response_code,
    274                 (int) dr.hr.ec);
    275     GNUNET_break_op (0);
    276     break;
    277   }
    278   if (NULL != gdh->cb)
    279     gdh->cb (gdh->cb_cls,
    280              &dr);
    281   TALER_EXCHANGE_get_deposits_cancel (gdh);
    282 }
    283 
    284 
    285 struct TALER_EXCHANGE_GetDepositsHandle *
    286 TALER_EXCHANGE_get_deposits_create (
    287   struct GNUNET_CURL_Context *ctx,
    288   const char *url,
    289   struct TALER_EXCHANGE_Keys *keys,
    290   const struct TALER_MerchantPrivateKeyP *merchant_priv,
    291   const struct TALER_MerchantWireHashP *h_wire,
    292   const struct TALER_PrivateContractHashP *h_contract_terms,
    293   const struct TALER_CoinSpendPublicKeyP *coin_pub)
    294 {
    295   struct TALER_EXCHANGE_GetDepositsHandle *gdh;
    296   struct TALER_MerchantPublicKeyP merchant;
    297 
    298   gdh = GNUNET_new (struct TALER_EXCHANGE_GetDepositsHandle);
    299   gdh->ctx = ctx;
    300   gdh->base_url = GNUNET_strdup (url);
    301   gdh->keys = TALER_EXCHANGE_keys_incref (keys);
    302   gdh->merchant_priv = *merchant_priv;
    303   gdh->h_wire = *h_wire;
    304   gdh->h_contract_terms = *h_contract_terms;
    305   GNUNET_CRYPTO_eddsa_key_get_public (&merchant_priv->eddsa_priv,
    306                                       &merchant.eddsa_pub);
    307   gdh->coin_pub = *coin_pub;
    308   return gdh;
    309 }
    310 
    311 
    312 enum GNUNET_GenericReturnValue
    313 TALER_EXCHANGE_get_deposits_set_options_ (
    314   struct TALER_EXCHANGE_GetDepositsHandle *gdh,
    315   unsigned int num_options,
    316   const struct TALER_EXCHANGE_GetDepositsOptionValue *options)
    317 {
    318   for (unsigned int i = 0; i < num_options; i++)
    319   {
    320     const struct TALER_EXCHANGE_GetDepositsOptionValue *opt = &options[i];
    321 
    322     switch (opt->option)
    323     {
    324     case TALER_EXCHANGE_GET_DEPOSITS_OPTION_END:
    325       return GNUNET_OK;
    326     case TALER_EXCHANGE_GET_DEPOSITS_OPTION_TIMEOUT:
    327       gdh->options.timeout = opt->details.timeout;
    328       break;
    329     }
    330   }
    331   return GNUNET_OK;
    332 }
    333 
    334 
    335 enum TALER_ErrorCode
    336 TALER_EXCHANGE_get_deposits_start (
    337   struct TALER_EXCHANGE_GetDepositsHandle *gdh,
    338   TALER_EXCHANGE_GetDepositsCallback cb,
    339   TALER_EXCHANGE_GET_DEPOSITS_RESULT_CLOSURE *cb_cls)
    340 {
    341   struct TALER_MerchantPublicKeyP merchant;
    342   struct TALER_MerchantSignatureP merchant_sig;
    343   char arg_str[(sizeof (struct TALER_CoinSpendPublicKeyP)
    344                 + sizeof (struct TALER_MerchantWireHashP)
    345                 + sizeof (struct TALER_MerchantPublicKeyP)
    346                 + sizeof (struct TALER_PrivateContractHashP)
    347                 + sizeof (struct TALER_MerchantSignatureP)) * 2 + 48];
    348   unsigned int tms;
    349   CURL *eh;
    350 
    351   if (NULL != gdh->job)
    352   {
    353     GNUNET_break (0);
    354     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
    355   }
    356   gdh->cb = cb;
    357   gdh->cb_cls = cb_cls;
    358   tms = (unsigned int) gdh->options.timeout.rel_value_us
    359         / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us;
    360   GNUNET_CRYPTO_eddsa_key_get_public (&gdh->merchant_priv.eddsa_priv,
    361                                       &merchant.eddsa_pub);
    362   TALER_merchant_deposit_sign (&gdh->h_contract_terms,
    363                                &gdh->h_wire,
    364                                &gdh->coin_pub,
    365                                &gdh->merchant_priv,
    366                                &merchant_sig);
    367   {
    368     char cpub_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2];
    369     char mpub_str[sizeof (struct TALER_MerchantPublicKeyP) * 2];
    370     char msig_str[sizeof (struct TALER_MerchantSignatureP) * 2];
    371     char chash_str[sizeof (struct TALER_PrivateContractHashP) * 2];
    372     char whash_str[sizeof (struct TALER_MerchantWireHashP) * 2];
    373     char timeout_str[24];
    374     char *end;
    375 
    376     end = GNUNET_STRINGS_data_to_string (&gdh->h_wire,
    377                                          sizeof (gdh->h_wire),
    378                                          whash_str,
    379                                          sizeof (whash_str));
    380     *end = '\0';
    381     end = GNUNET_STRINGS_data_to_string (&merchant,
    382                                          sizeof (merchant),
    383                                          mpub_str,
    384                                          sizeof (mpub_str));
    385     *end = '\0';
    386     end = GNUNET_STRINGS_data_to_string (&gdh->h_contract_terms,
    387                                          sizeof (gdh->h_contract_terms),
    388                                          chash_str,
    389                                          sizeof (chash_str));
    390     *end = '\0';
    391     end = GNUNET_STRINGS_data_to_string (&gdh->coin_pub,
    392                                          sizeof (gdh->coin_pub),
    393                                          cpub_str,
    394                                          sizeof (cpub_str));
    395     *end = '\0';
    396     end = GNUNET_STRINGS_data_to_string (&merchant_sig,
    397                                          sizeof (merchant_sig),
    398                                          msig_str,
    399                                          sizeof (msig_str));
    400     *end = '\0';
    401     if (0 == tms)
    402     {
    403       timeout_str[0] = '\0';
    404     }
    405     else
    406     {
    407       GNUNET_snprintf (
    408         timeout_str,
    409         sizeof (timeout_str),
    410         "%u",
    411         tms);
    412     }
    413     GNUNET_snprintf (arg_str,
    414                      sizeof (arg_str),
    415                      "deposits/%s/%s/%s/%s?merchant_sig=%s%s%s",
    416                      whash_str,
    417                      mpub_str,
    418                      chash_str,
    419                      cpub_str,
    420                      msig_str,
    421                      0 == tms
    422                      ? ""
    423                      : "&timeout_ms=",
    424                      timeout_str);
    425   }
    426   gdh->url = TALER_url_join (gdh->base_url,
    427                              arg_str,
    428                              NULL);
    429   if (NULL == gdh->url)
    430     return TALER_EC_GENERIC_CONFIGURATION_INVALID;
    431   eh = TALER_EXCHANGE_curl_easy_get_ (gdh->url);
    432   if (NULL == eh)
    433     return TALER_EC_GENERIC_CONFIGURATION_INVALID;
    434   if (0 != tms)
    435   {
    436     GNUNET_break (CURLE_OK ==
    437                   curl_easy_setopt (eh,
    438                                     CURLOPT_TIMEOUT_MS,
    439                                     (long) (tms + 100L)));
    440   }
    441   gdh->job = GNUNET_CURL_job_add (gdh->ctx,
    442                                   eh,
    443                                   &handle_deposit_wtid_finished,
    444                                   gdh);
    445   if (NULL == gdh->job)
    446     return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
    447   return TALER_EC_NONE;
    448 }
    449 
    450 
    451 void
    452 TALER_EXCHANGE_get_deposits_cancel (
    453   struct TALER_EXCHANGE_GetDepositsHandle *gdh)
    454 {
    455   if (NULL != gdh->job)
    456   {
    457     GNUNET_CURL_job_cancel (gdh->job);
    458     gdh->job = NULL;
    459   }
    460   GNUNET_free (gdh->url);
    461   GNUNET_free (gdh->base_url);
    462   TALER_EXCHANGE_keys_decref (gdh->keys);
    463   GNUNET_free (gdh);
    464 }
    465 
    466 
    467 /* end of exchange_api_get-deposits-H_WIRE-MERCHANT_PUB-H_CONTRACT_TERMS-COIN_PUB.c */