LCOV - code coverage report
Current view: top level - exchange-lib - exchange_api_track_transaction.c (source / functions) Hit Total Coverage
Test: rcoverage.info Lines: 76 104 73.1 %
Date: 2017-09-17 17:24:28 Functions: 4 4 100.0 %

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   Copyright (C) 2014, 2015, 2016 GNUnet e.V.
       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 exchange-lib/exchange_api_track_transaction.c
      19             :  * @brief Implementation of the /track/transaction request of the exchange's HTTP API
      20             :  * @author Christian Grothoff
      21             :  */
      22             : #include "platform.h"
      23             : #include <curl/curl.h>
      24             : #include <jansson.h>
      25             : #include <microhttpd.h> /* just for HTTP status codes */
      26             : #include <gnunet/gnunet_util_lib.h>
      27             : #include <gnunet/gnunet_json_lib.h>
      28             : #include <gnunet/gnunet_curl_lib.h>
      29             : #include "taler_json_lib.h"
      30             : #include "taler_exchange_service.h"
      31             : #include "exchange_api_handle.h"
      32             : #include "taler_signatures.h"
      33             : 
      34             : 
      35             : /**
      36             :  * @brief A Deposit Wtid Handle
      37             :  */
      38             : struct TALER_EXCHANGE_TrackTransactionHandle
      39             : {
      40             : 
      41             :   /**
      42             :    * The connection to exchange this request handle will use
      43             :    */
      44             :   struct TALER_EXCHANGE_Handle *exchange;
      45             : 
      46             :   /**
      47             :    * The url for this request.
      48             :    */
      49             :   char *url;
      50             : 
      51             :   /**
      52             :    * JSON encoding of the request to POST.
      53             :    */
      54             :   char *json_enc;
      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_TrackTransactionCallback cb;
      65             : 
      66             :   /**
      67             :    * Closure for @a cb.
      68             :    */
      69             :   void *cb_cls;
      70             : 
      71             :   /**
      72             :    * Information the exchange should sign in response.
      73             :    * (with pre-filled fields from the request).
      74             :    */
      75             :   struct TALER_ConfirmWirePS depconf;
      76             : 
      77             : };
      78             : 
      79             : 
      80             : /**
      81             :  * Verify that the signature on the "200 OK" response
      82             :  * from the exchange is valid.
      83             :  *
      84             :  * @param dwh deposit wtid handle
      85             :  * @param json json reply with the signature
      86             :  * @param[out] exchange_pub set to the exchange's public key
      87             :  * @return #GNUNET_OK if the signature is valid, #GNUNET_SYSERR if not
      88             :  */
      89             : static int
      90           1 : verify_deposit_wtid_signature_ok (const struct TALER_EXCHANGE_TrackTransactionHandle *dwh,
      91             :                                   const json_t *json,
      92             :                                   struct TALER_ExchangePublicKeyP *exchange_pub)
      93             : {
      94             :   struct TALER_ExchangeSignatureP exchange_sig;
      95             :   const struct TALER_EXCHANGE_Keys *key_state;
      96           1 :   struct GNUNET_JSON_Specification spec[] = {
      97             :     GNUNET_JSON_spec_fixed_auto ("exchange_sig", &exchange_sig),
      98             :     GNUNET_JSON_spec_fixed_auto ("exchange_pub", exchange_pub),
      99             :     GNUNET_JSON_spec_end()
     100             :   };
     101             : 
     102           1 :   if (GNUNET_OK !=
     103           1 :       GNUNET_JSON_parse (json,
     104             :                          spec,
     105             :                          NULL, NULL))
     106             :   {
     107           0 :     GNUNET_break_op (0);
     108           0 :     return GNUNET_SYSERR;
     109             :   }
     110           1 :   key_state = TALER_EXCHANGE_get_keys (dwh->exchange);
     111           1 :   if (GNUNET_OK !=
     112           1 :       TALER_EXCHANGE_test_signing_key (key_state,
     113             :                                        exchange_pub))
     114             :   {
     115           0 :     GNUNET_break_op (0);
     116           0 :     return GNUNET_SYSERR;
     117             :   }
     118           1 :   if (GNUNET_OK !=
     119           1 :       GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_EXCHANGE_CONFIRM_WIRE,
     120             :                                   &dwh->depconf.purpose,
     121             :                                   &exchange_sig.eddsa_signature,
     122           1 :                                   &exchange_pub->eddsa_pub))
     123             :   {
     124           0 :     GNUNET_break_op (0);
     125           0 :     return GNUNET_SYSERR;
     126             :   }
     127           1 :   return GNUNET_OK;
     128             : }
     129             : 
     130             : 
     131             : /**
     132             :  * Function called when we're done processing the
     133             :  * HTTP /track/transaction request.
     134             :  *
     135             :  * @param cls the `struct TALER_EXCHANGE_TrackTransactionHandle`
     136             :  * @param response_code HTTP response code, 0 on error
     137             :  * @param json parsed JSON result, NULL on error
     138             :  */
     139             : static void
     140           3 : handle_deposit_wtid_finished (void *cls,
     141             :                               long response_code,
     142             :                               const json_t *json)
     143             : {
     144           3 :   struct TALER_EXCHANGE_TrackTransactionHandle *dwh = cls;
     145           3 :   const struct TALER_WireTransferIdentifierRawP *wtid = NULL;
     146           3 :   struct GNUNET_TIME_Absolute execution_time = GNUNET_TIME_UNIT_FOREVER_ABS;
     147           3 :   const struct TALER_Amount *coin_contribution = NULL;
     148             :   struct TALER_Amount coin_contribution_s;
     149             :   struct TALER_ExchangePublicKeyP exchange_pub;
     150           3 :   struct TALER_ExchangePublicKeyP *ep = NULL;
     151             : 
     152           3 :   dwh->job = NULL;
     153           3 :   switch (response_code)
     154             :   {
     155             :   case 0:
     156           0 :     break;
     157             :   case MHD_HTTP_OK:
     158             :     {
     159           1 :       struct GNUNET_JSON_Specification spec[] = {
     160           1 :         GNUNET_JSON_spec_fixed_auto ("wtid", &dwh->depconf.wtid),
     161             :         GNUNET_JSON_spec_absolute_time ("execution_time", &execution_time),
     162             :         TALER_JSON_spec_amount ("coin_contribution", &coin_contribution_s),
     163             :         GNUNET_JSON_spec_end()
     164             :       };
     165             : 
     166           1 :       if (GNUNET_OK !=
     167           1 :           GNUNET_JSON_parse (json,
     168             :                              spec,
     169             :                              NULL, NULL))
     170             :       {
     171           0 :         GNUNET_break_op (0);
     172           0 :         response_code = 0;
     173           0 :         break;
     174             :       }
     175           1 :       wtid = &dwh->depconf.wtid;
     176           1 :       dwh->depconf.execution_time = GNUNET_TIME_absolute_hton (execution_time);
     177           1 :       TALER_amount_hton (&dwh->depconf.coin_contribution,
     178             :                          &coin_contribution_s);
     179           1 :       coin_contribution = &coin_contribution_s;
     180           1 :       if (GNUNET_OK !=
     181           1 :           verify_deposit_wtid_signature_ok (dwh,
     182             :                                             json,
     183             :                                             &exchange_pub))
     184             :       {
     185           0 :         GNUNET_break_op (0);
     186           0 :         response_code = 0;
     187             :       }
     188             :       else
     189             :       {
     190           1 :         ep = &exchange_pub;
     191             :       }
     192             :     }
     193           1 :     break;
     194             :   case MHD_HTTP_ACCEPTED:
     195             :     {
     196             :       /* Transaction known, but not executed yet */
     197           1 :       struct GNUNET_JSON_Specification spec[] = {
     198             :         GNUNET_JSON_spec_absolute_time ("execution_time", &execution_time),
     199             :         GNUNET_JSON_spec_end()
     200             :       };
     201             : 
     202           1 :       if (GNUNET_OK !=
     203           1 :           GNUNET_JSON_parse (json,
     204             :                              spec,
     205             :                              NULL, NULL))
     206             :       {
     207           0 :         GNUNET_break_op (0);
     208           0 :         response_code = 0;
     209           0 :         break;
     210             :       }
     211             :     }
     212           1 :     break;
     213             :   case MHD_HTTP_BAD_REQUEST:
     214             :     /* This should never happen, either us or the exchange is buggy
     215             :        (or API version conflict); just pass JSON reply to the application */
     216           0 :     break;
     217             :   case MHD_HTTP_UNAUTHORIZED:
     218             :     /* Nothing really to verify, exchange says one of the signatures is
     219             :        invalid; as we checked them, this should never happen, we
     220             :        should pass the JSON reply to the application */
     221           0 :     break;
     222             :   case MHD_HTTP_NOT_FOUND:
     223             :     /* Exchange does not know about transaction;
     224             :        we should pass the reply to the application */
     225           1 :     break;
     226             :   case MHD_HTTP_INTERNAL_SERVER_ERROR:
     227             :     /* Server had an internal issue; we should retry, but this API
     228             :        leaves this to the application */
     229           0 :     break;
     230             :   default:
     231             :     /* unexpected response code */
     232           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     233             :                 "Unexpected response code %u\n",
     234             :                 (unsigned int) response_code);
     235           0 :     GNUNET_break (0);
     236           0 :     response_code = 0;
     237           0 :     break;
     238             :   }
     239           3 :   dwh->cb (dwh->cb_cls,
     240             :            response_code,
     241             :            TALER_JSON_get_error_code (json),
     242             :            ep,
     243             :            json,
     244             :            wtid,
     245             :            execution_time,
     246             :            coin_contribution);
     247           3 :   TALER_EXCHANGE_track_transaction_cancel (dwh);
     248           3 : }
     249             : 
     250             : 
     251             : /**
     252             :  * Obtain wire transfer details about an existing deposit operation.
     253             :  *
     254             :  * @param exchange the exchange to query
     255             :  * @param merchant_priv the merchant's private key
     256             :  * @param h_wire hash of merchant's wire transfer details
     257             :  * @param h_contract_terms hash of the proposal data from the contract
     258             :  *                        between merchant and customer
     259             :  * @param coin_pub public key of the coin
     260             :  * @param cb function to call with the result
     261             :  * @param cb_cls closure for @a cb
     262             :  * @return handle to abort request
     263             :  */
     264             : struct TALER_EXCHANGE_TrackTransactionHandle *
     265           3 : TALER_EXCHANGE_track_transaction (struct TALER_EXCHANGE_Handle *exchange,
     266             :                              const struct TALER_MerchantPrivateKeyP *merchant_priv,
     267             :                              const struct GNUNET_HashCode *h_wire,
     268             :                              const struct GNUNET_HashCode *h_contract_terms,
     269             :                              const struct TALER_CoinSpendPublicKeyP *coin_pub,
     270             :                              TALER_EXCHANGE_TrackTransactionCallback cb,
     271             :                              void *cb_cls)
     272             : {
     273             :   struct TALER_DepositTrackPS dtp;
     274             :   struct TALER_MerchantSignatureP merchant_sig;
     275             :   struct TALER_EXCHANGE_TrackTransactionHandle *dwh;
     276             :   struct GNUNET_CURL_Context *ctx;
     277             :   json_t *deposit_wtid_obj;
     278             :   CURL *eh;
     279             : 
     280           3 :   if (GNUNET_YES !=
     281           3 :       MAH_handle_is_ready (exchange))
     282             :   {
     283           0 :     GNUNET_break (0);
     284           0 :     return NULL;
     285             :   }
     286           3 :   dtp.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_TRACK_TRANSACTION);
     287           3 :   dtp.purpose.size = htonl (sizeof (dtp));
     288           3 :   dtp.h_contract_terms = *h_contract_terms;
     289           3 :   dtp.h_wire = *h_wire;
     290           3 :   GNUNET_CRYPTO_eddsa_key_get_public (&merchant_priv->eddsa_priv,
     291             :                                       &dtp.merchant.eddsa_pub);
     292             : 
     293           3 :   dtp.coin_pub = *coin_pub;
     294           3 :   GNUNET_assert (GNUNET_OK ==
     295             :                  GNUNET_CRYPTO_eddsa_sign (&merchant_priv->eddsa_priv,
     296             :                                            &dtp.purpose,
     297             :                                            &merchant_sig.eddsa_sig));
     298           3 :   deposit_wtid_obj = json_pack ("{s:o, s:o," /* H_wire, h_contract_terms */
     299             :                                 " s:o," /* coin_pub */
     300             :                                 " s:o, s:o}", /* merchant_pub, merchant_sig */
     301             :                                 "H_wire", GNUNET_JSON_from_data_auto (h_wire),
     302             :                                 "h_contract_terms", GNUNET_JSON_from_data_auto (h_contract_terms),
     303             :                                 "coin_pub", GNUNET_JSON_from_data_auto (coin_pub),
     304             :                                 "merchant_pub", GNUNET_JSON_from_data_auto (&dtp.merchant),
     305             :                                 "merchant_sig", GNUNET_JSON_from_data_auto (&merchant_sig));
     306           3 :   if (NULL == deposit_wtid_obj)
     307             :   {
     308           0 :     GNUNET_break (0);
     309           0 :     return NULL;
     310             :   }
     311             : 
     312           3 :   dwh = GNUNET_new (struct TALER_EXCHANGE_TrackTransactionHandle);
     313           3 :   dwh->exchange = exchange;
     314           3 :   dwh->cb = cb;
     315           3 :   dwh->cb_cls = cb_cls;
     316           3 :   dwh->url = MAH_path_to_url (exchange, "/track/transaction");
     317           3 :   dwh->depconf.purpose.size = htonl (sizeof (struct TALER_ConfirmWirePS));
     318           3 :   dwh->depconf.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_WIRE);
     319           3 :   dwh->depconf.h_wire = *h_wire;
     320           3 :   dwh->depconf.h_contract_terms = *h_contract_terms;
     321           3 :   dwh->depconf.coin_pub = *coin_pub;
     322             : 
     323           3 :   eh = curl_easy_init ();
     324           3 :   GNUNET_assert (NULL != (dwh->json_enc =
     325             :                           json_dumps (deposit_wtid_obj,
     326             :                                       JSON_COMPACT)));
     327           3 :   json_decref (deposit_wtid_obj);
     328           3 :   GNUNET_assert (CURLE_OK ==
     329             :                  curl_easy_setopt (eh,
     330             :                                    CURLOPT_URL,
     331             :                                    dwh->url));
     332           3 :   GNUNET_assert (CURLE_OK ==
     333             :                  curl_easy_setopt (eh,
     334             :                                    CURLOPT_POSTFIELDS,
     335             :                                    dwh->json_enc));
     336           3 :   GNUNET_assert (CURLE_OK ==
     337             :                  curl_easy_setopt (eh,
     338             :                                    CURLOPT_POSTFIELDSIZE,
     339             :                                    strlen (dwh->json_enc)));
     340           3 :   ctx = MAH_handle_to_context (exchange);
     341           3 :   dwh->job = GNUNET_CURL_job_add (ctx,
     342             :                           eh,
     343             :                           GNUNET_YES,
     344             :                           &handle_deposit_wtid_finished,
     345             :                           dwh);
     346           3 :   return dwh;
     347             : }
     348             : 
     349             : 
     350             : /**
     351             :  * Cancel deposit wtid request.  This function cannot be used on a request
     352             :  * handle if a response is already served for it.
     353             :  *
     354             :  * @param dwh the wire deposits request handle
     355             :  */
     356             : void
     357           3 : TALER_EXCHANGE_track_transaction_cancel (struct TALER_EXCHANGE_TrackTransactionHandle *dwh)
     358             : {
     359           3 :   if (NULL != dwh->job)
     360             :   {
     361           0 :     GNUNET_CURL_job_cancel (dwh->job);
     362           0 :     dwh->job = NULL;
     363             :   }
     364           3 :   GNUNET_free (dwh->url);
     365           3 :   GNUNET_free (dwh->json_enc);
     366           3 :   GNUNET_free (dwh);
     367           3 : }
     368             : 
     369             : 
     370             : /* end of exchange_api_deposit_wtid.c */

Generated by: LCOV version 1.13