LCOV - code coverage report
Current view: top level - exchange - taler-exchange-httpd_track_transfer.c (source / functions) Hit Total Coverage
Test: rcoverage.info Lines: 79 120 65.8 %
Date: 2017-11-25 11:31:41 Functions: 5 5 100.0 %

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   Copyright (C) 2014-2017 GNUnet e.V.
       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_track_transfer.c
      18             :  * @brief Handle wire transfer /track/transfer requests
      19             :  * @author Christian Grothoff
      20             :  */
      21             : #include "platform.h"
      22             : #include <gnunet/gnunet_util_lib.h>
      23             : #include <jansson.h>
      24             : #include <microhttpd.h>
      25             : #include <pthread.h>
      26             : #include "taler_signatures.h"
      27             : #include "taler-exchange-httpd_parsing.h"
      28             : #include "taler-exchange-httpd_keystate.h"
      29             : #include "taler-exchange-httpd_track_transfer.h"
      30             : #include "taler-exchange-httpd_responses.h"
      31             : 
      32             : 
      33             : /**
      34             :  * Detail for /wire/deposit response.
      35             :  */
      36             : struct TEH_TrackTransferDetail
      37             : {
      38             : 
      39             :   /**
      40             :    * We keep deposit details in a DLL.
      41             :    */
      42             :   struct TEH_TrackTransferDetail *next;
      43             : 
      44             :   /**
      45             :    * We keep deposit details in a DLL.
      46             :    */
      47             :   struct TEH_TrackTransferDetail *prev;
      48             : 
      49             :   /**
      50             :    * Hash of the proposal data.
      51             :    */
      52             :   struct GNUNET_HashCode h_contract_terms;
      53             : 
      54             :   /**
      55             :    * Coin's public key.
      56             :    */
      57             :   struct TALER_CoinSpendPublicKeyP coin_pub;
      58             : 
      59             :   /**
      60             :    * Total value of the coin.
      61             :    */
      62             :   struct TALER_Amount deposit_value;
      63             : 
      64             :   /**
      65             :    * Fees charged by the exchange for the deposit.
      66             :    */
      67             :   struct TALER_Amount deposit_fee;
      68             : };
      69             : 
      70             : 
      71             : /**
      72             :  * A merchant asked for transaction details about a wire transfer.
      73             :  * Provide them. Generates the 200 reply.
      74             :  *
      75             :  * @param connection connection to the client
      76             :  * @param total total amount that was transferred
      77             :  * @param merchant_pub public key of the merchant
      78             :  * @param h_wire destination account
      79             :  * @param wire_fee wire fee that was charged
      80             :  * @param exec_time execution time of the wire transfer
      81             :  * @param wdd_head linked list with details about the combined deposits
      82             :  * @return MHD result code
      83             :  */
      84             : static int
      85           2 : reply_track_transfer_details (struct MHD_Connection *connection,
      86             :                               const struct TALER_Amount *total,
      87             :                               const struct TALER_MerchantPublicKeyP *merchant_pub,
      88             :                               const struct GNUNET_HashCode *h_wire,
      89             :                               const struct TALER_Amount *wire_fee,
      90             :                               struct GNUNET_TIME_Absolute exec_time,
      91             :                               const struct TEH_TrackTransferDetail *wdd_head)
      92             : {
      93             :   const struct TEH_TrackTransferDetail *wdd_pos;
      94             :   json_t *deposits;
      95             :   struct TALER_WireDepositDetailP dd;
      96             :   struct GNUNET_HashContext *hash_context;
      97             :   struct TALER_WireDepositDataPS wdp;
      98             :   struct TALER_ExchangePublicKeyP pub;
      99             :   struct TALER_ExchangeSignatureP sig;
     100             : 
     101           2 :   GNUNET_TIME_round_abs (&exec_time);
     102           2 :   deposits = json_array ();
     103           2 :   hash_context = GNUNET_CRYPTO_hash_context_start ();
     104           4 :   for (wdd_pos = wdd_head; NULL != wdd_pos; wdd_pos = wdd_pos->next)
     105             :   {
     106           2 :     dd.h_contract_terms = wdd_pos->h_contract_terms;
     107           2 :     dd.execution_time = GNUNET_TIME_absolute_hton (exec_time);
     108           2 :     dd.coin_pub = wdd_pos->coin_pub;
     109           2 :     TALER_amount_hton (&dd.deposit_value,
     110             :                        &wdd_pos->deposit_value);
     111           2 :     TALER_amount_hton (&dd.deposit_fee,
     112             :                        &wdd_pos->deposit_fee);
     113           2 :     GNUNET_CRYPTO_hash_context_read (hash_context,
     114             :                                      &dd,
     115             :                                      sizeof (struct TALER_WireDepositDetailP));
     116           2 :     GNUNET_assert (0 ==
     117             :                    json_array_append_new (deposits,
     118             :                                           json_pack ("{s:o, s:o, s:o, s:o}",
     119             :                                                      "h_contract_terms", GNUNET_JSON_from_data_auto (&wdd_pos->h_contract_terms),
     120             :                                                      "coin_pub", GNUNET_JSON_from_data_auto (&wdd_pos->coin_pub),
     121             :                                                      "deposit_value", TALER_JSON_from_amount (&wdd_pos->deposit_value),
     122             :                                                      "deposit_fee", TALER_JSON_from_amount (&wdd_pos->deposit_fee))));
     123             :   }
     124           2 :   wdp.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_WIRE_DEPOSIT);
     125           2 :   wdp.purpose.size = htonl (sizeof (struct TALER_WireDepositDataPS));
     126           2 :   TALER_amount_hton (&wdp.total,
     127             :                      total);
     128           2 :   TALER_amount_hton (&wdp.wire_fee,
     129             :                      wire_fee);
     130           2 :   wdp.merchant_pub = *merchant_pub;
     131           2 :   wdp.h_wire = *h_wire;
     132           2 :   GNUNET_CRYPTO_hash_context_finish (hash_context,
     133             :                                      &wdp.h_details);
     134           2 :   if (GNUNET_OK !=
     135           2 :       TEH_KS_sign (&wdp.purpose,
     136             :                    &pub,
     137             :                    &sig))
     138             :   {
     139           0 :     json_decref (deposits);
     140           0 :     return TEH_RESPONSE_reply_internal_error (connection,
     141             :                                               TALER_EC_EXCHANGE_BAD_CONFIGURATION,
     142             :                                               "no keys");
     143             :   }
     144             :     
     145           2 :   return TEH_RESPONSE_reply_json_pack (connection,
     146             :                                        MHD_HTTP_OK,
     147             :                                        "{s:o, s:o, s:o, s:o, s:o, s:o, s:o, s:o}",
     148             :                                        "total", TALER_JSON_from_amount (total),
     149             :                                        "wire_fee", TALER_JSON_from_amount (wire_fee),
     150             :                                        "merchant_pub", GNUNET_JSON_from_data_auto (merchant_pub),
     151             :                                        "H_wire", GNUNET_JSON_from_data_auto (h_wire),
     152             :                                        "execution_time", GNUNET_JSON_from_time_abs (exec_time),
     153             :                                        "deposits", deposits,
     154             :                                        "exchange_sig", GNUNET_JSON_from_data_auto (&sig),
     155             :                                        "exchange_pub", GNUNET_JSON_from_data_auto (&pub));
     156             : }
     157             : 
     158             : 
     159             : /**
     160             :  * Closure for #handle_transaction_data.
     161             :  */
     162             : struct WtidTransactionContext
     163             : {
     164             : 
     165             :   /**
     166             :    * Identifier of the wire transfer to track.
     167             :    */
     168             :   struct TALER_WireTransferIdentifierRawP wtid;
     169             : 
     170             :   /**
     171             :    * Total amount of the wire transfer, as calculated by
     172             :    * summing up the individual amounts. To be rounded down
     173             :    * to calculate the real transfer amount at the end.
     174             :    * Only valid if @e is_valid is #GNUNET_YES.
     175             :    */
     176             :   struct TALER_Amount total;
     177             : 
     178             :   /**
     179             :    * Public key of the merchant, only valid if @e is_valid
     180             :    * is #GNUNET_YES.
     181             :    */
     182             :   struct TALER_MerchantPublicKeyP merchant_pub;
     183             : 
     184             :   /**
     185             :    * Which method was used to wire the funds?
     186             :    */
     187             :   char *wire_method;
     188             : 
     189             :   /**
     190             :    * Hash of the wire details of the merchant (identical for all
     191             :    * deposits), only valid if @e is_valid is #GNUNET_YES.
     192             :    */
     193             :   struct GNUNET_HashCode h_wire;
     194             : 
     195             :   /**
     196             :    * Wire fee applicable at @e exec_time.
     197             :    */
     198             :   struct TALER_Amount wire_fee;
     199             : 
     200             :   /**
     201             :    * Execution time of the wire transfer
     202             :    */
     203             :   struct GNUNET_TIME_Absolute exec_time;
     204             : 
     205             :   /**
     206             :    * Head of DLL with details for /wire/deposit response.
     207             :    */
     208             :   struct TEH_TrackTransferDetail *wdd_head;
     209             : 
     210             :   /**
     211             :    * Head of DLL with details for /wire/deposit response.
     212             :    */
     213             :   struct TEH_TrackTransferDetail *wdd_tail;
     214             : 
     215             :   /**
     216             :    * JSON array with details about the individual deposits.
     217             :    */
     218             :   json_t *deposits;
     219             : 
     220             :   /**
     221             :    * Initially #GNUNET_NO, if we found no deposits so far.  Set to
     222             :    * #GNUNET_YES if we got transaction data, and the database replies
     223             :    * remained consistent with respect to @e merchant_pub and @e h_wire
     224             :    * (as they should).  Set to #GNUNET_SYSERR if we encountered an
     225             :    * internal error.
     226             :    */
     227             :   int is_valid;
     228             : 
     229             : };
     230             : 
     231             : 
     232             : /**
     233             :  * Function called with the results of the lookup of the
     234             :  * transaction data for the given wire transfer identifier.
     235             :  *
     236             :  * @param cls our context for transmission
     237             :  * @param rowid which row in the DB is the information from (for diagnostics)
     238             :  * @param merchant_pub public key of the merchant (should be same for all callbacks with the same @e cls)
     239             :  * @param wire_method which wire plugin was used
     240             :  * @param h_wire hash of wire transfer details of the merchant (should be same for all callbacks with the same @e cls)
     241             :  * @param exec_time execution time of the wire transfer (should be same for all callbacks with the same @e cls)
     242             :  * @param h_contract_terms which proposal was this payment about
     243             :  * @param coin_pub which public key was this payment about
     244             :  * @param deposit_value amount contributed by this coin in total
     245             :  * @param deposit_fee deposit fee charged by exchange for this coin
     246             :  */
     247             : static void
     248           2 : handle_transaction_data (void *cls,
     249             :                          uint64_t rowid,
     250             :                          const struct TALER_MerchantPublicKeyP *merchant_pub,
     251             :                          const char *wire_method,
     252             :                          const struct GNUNET_HashCode *h_wire,
     253             :                          struct GNUNET_TIME_Absolute exec_time,
     254             :                          const struct GNUNET_HashCode *h_contract_terms,
     255             :                          const struct TALER_CoinSpendPublicKeyP *coin_pub,
     256             :                          const struct TALER_Amount *deposit_value,
     257             :                          const struct TALER_Amount *deposit_fee)
     258             : {
     259           2 :   struct WtidTransactionContext *ctx = cls;
     260             :   struct TALER_Amount delta;
     261             :   struct TEH_TrackTransferDetail *wdd;
     262             : 
     263           2 :   if (GNUNET_SYSERR == ctx->is_valid)
     264           0 :     return;
     265           2 :   if (GNUNET_NO == ctx->is_valid)
     266             :   {
     267           2 :     ctx->merchant_pub = *merchant_pub;
     268           2 :     ctx->h_wire = *h_wire;
     269           2 :     ctx->exec_time = exec_time;
     270           2 :     ctx->wire_method = GNUNET_strdup (wire_method);
     271           2 :     ctx->is_valid = GNUNET_YES;
     272           2 :     if (GNUNET_OK !=
     273           2 :         TALER_amount_subtract (&ctx->total,
     274             :                                deposit_value,
     275             :                                deposit_fee))
     276             :     {
     277           0 :       GNUNET_break (0);
     278           0 :       ctx->is_valid = GNUNET_SYSERR;
     279           0 :       return;
     280             :     }
     281             :   }
     282             :   else
     283             :   {
     284           0 :     if ( (0 != memcmp (&ctx->merchant_pub,
     285             :                        merchant_pub,
     286           0 :                        sizeof (struct TALER_MerchantPublicKeyP))) ||
     287           0 :          (0 != strcmp (wire_method,
     288           0 :                        ctx->wire_method)) ||
     289           0 :          (0 != memcmp (&ctx->h_wire,
     290             :                        h_wire,
     291             :                        sizeof (struct GNUNET_HashCode))) )
     292             :     {
     293           0 :       GNUNET_break (0);
     294           0 :       ctx->is_valid = GNUNET_SYSERR;
     295           0 :       return;
     296             :     }
     297           0 :     if (GNUNET_OK !=
     298           0 :         TALER_amount_subtract (&delta,
     299             :                                deposit_value,
     300             :                                deposit_fee))
     301             :     {
     302           0 :       GNUNET_break (0);
     303           0 :       ctx->is_valid = GNUNET_SYSERR;
     304           0 :       return;
     305             :     }
     306           0 :     if (GNUNET_OK !=
     307           0 :         TALER_amount_add (&ctx->total,
     308           0 :                           &ctx->total,
     309             :                           &delta))
     310             :     {
     311           0 :       GNUNET_break (0);
     312           0 :       ctx->is_valid = GNUNET_SYSERR;
     313           0 :       return;
     314             :     }
     315             :   }
     316           2 :   wdd = GNUNET_new (struct TEH_TrackTransferDetail);
     317           2 :   wdd->deposit_value = *deposit_value;
     318           2 :   wdd->deposit_fee = *deposit_fee;
     319           2 :   wdd->h_contract_terms = *h_contract_terms;
     320           2 :   wdd->coin_pub = *coin_pub;
     321           2 :   GNUNET_CONTAINER_DLL_insert (ctx->wdd_head,
     322             :                                ctx->wdd_tail,
     323             :                                wdd);
     324             : }
     325             : 
     326             : 
     327             : /**
     328             :  * Execute a "/track/transfer".  Returns the transaction information
     329             :  * associated with the given wire transfer identifier.
     330             :  * 
     331             :  * If it returns a non-error code, the transaction logic MUST
     332             :  * NOT queue a MHD response.  IF it returns an hard error, the
     333             :  * transaction logic MUST queue a MHD response and set @a mhd_ret.  IF
     334             :  * it returns the soft error code, the function MAY be called again to
     335             :  * retry and MUST not queue a MHD response.
     336             :  *
     337             :  * @param cls closure
     338             :  * @param connection MHD request which triggered the transaction
     339             :  * @param session database session to use
     340             :  * @param[out] mhd_ret set to MHD response status for @a connection,
     341             :  *             if transaction failed (!)
     342             :  * @return transaction status
     343             :  */
     344             : static enum GNUNET_DB_QueryStatus
     345           3 : track_transfer_transaction (void *cls,
     346             :                             struct MHD_Connection *connection,
     347             :                             struct TALER_EXCHANGEDB_Session *session,
     348             :                             int *mhd_ret)
     349             : {
     350           3 :   struct WtidTransactionContext *ctx = cls;
     351             :   enum GNUNET_DB_QueryStatus qs;
     352             :   struct GNUNET_TIME_Absolute wire_fee_start_date;
     353             :   struct GNUNET_TIME_Absolute wire_fee_end_date;
     354             :   struct TALER_MasterSignatureP wire_fee_master_sig;
     355             : 
     356           3 :   ctx->is_valid = GNUNET_NO;
     357           3 :   ctx->wdd_head = NULL;
     358           3 :   ctx->wdd_tail = NULL;
     359           3 :   ctx->wire_method = NULL;
     360           6 :   qs = TEH_plugin->lookup_wire_transfer (TEH_plugin->cls,
     361             :                                          session,
     362           3 :                                          &ctx->wtid,
     363             :                                          &handle_transaction_data,
     364             :                                          ctx);
     365           3 :   if (0 > qs)
     366             :   {
     367           0 :     if (GNUNET_DB_STATUS_HARD_ERROR == qs)
     368             :     {
     369           0 :       GNUNET_break (0);
     370           0 :       *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
     371             :                                                        TALER_EC_TRACK_TRANSFER_DB_FETCH_FAILED);
     372             :     }
     373           0 :     return qs;
     374             :   }
     375           3 :   if (GNUNET_SYSERR == ctx->is_valid)
     376             :   {
     377           0 :     GNUNET_break (0);
     378           0 :     *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
     379             :                                                      TALER_EC_TRACK_TRANSFER_DB_INCONSISTENT);
     380           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
     381             :   }
     382           3 :   if (GNUNET_NO == ctx->is_valid)
     383             :   {
     384           1 :     *mhd_ret = TEH_RESPONSE_reply_arg_unknown (connection,
     385             :                                                TALER_EC_TRACK_TRANSFER_WTID_NOT_FOUND,
     386             :                                                "wtid");
     387           1 :     return GNUNET_DB_STATUS_HARD_ERROR;
     388             :   }
     389           4 :   qs = TEH_plugin->get_wire_fee (TEH_plugin->cls,
     390             :                                  session,
     391           2 :                                  ctx->wire_method,
     392             :                                  ctx->exec_time,
     393             :                                  &wire_fee_start_date,
     394             :                                  &wire_fee_end_date,
     395             :                                  &ctx->wire_fee,
     396             :                                  &wire_fee_master_sig);
     397           2 :   if (0 >= qs)
     398             :   {
     399           0 :     if ( (GNUNET_DB_STATUS_HARD_ERROR == qs) ||
     400             :          (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS) )
     401             :     {
     402           0 :       GNUNET_break (0);
     403           0 :       *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
     404             :                                                        TALER_EC_TRACK_TRANSFER_WIRE_FEE_NOT_FOUND);
     405             :     }
     406           0 :     return qs;
     407             :   }
     408           2 :   if (GNUNET_OK !=
     409           2 :       TALER_amount_subtract (&ctx->total,
     410           2 :                              &ctx->total,
     411           2 :                              &ctx->wire_fee))
     412             :   {
     413           0 :     GNUNET_break (0);
     414           0 :     *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
     415             :                                                      TALER_EC_TRACK_TRANSFER_WIRE_FEE_INCONSISTENT);
     416           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
     417             :   }
     418           2 :   return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
     419             : }
     420             : 
     421             : 
     422             : /**
     423             :  * Free data structure reachable from @a ctx, but not @a ctx itself.
     424             :  *
     425             :  * @param ctx context to free
     426             :  */
     427             : static void
     428           3 : free_ctx (struct WtidTransactionContext *ctx)
     429             : {
     430             :   struct TEH_TrackTransferDetail *wdd;
     431             : 
     432           8 :   while (NULL != (wdd = ctx->wdd_head))
     433             :   {
     434           2 :     GNUNET_CONTAINER_DLL_remove (ctx->wdd_head,
     435             :                                  ctx->wdd_tail,
     436             :                                  wdd);
     437           2 :     GNUNET_free (wdd);
     438             :   }
     439           3 :   GNUNET_free_non_null (ctx->wire_method);
     440           3 : }
     441             : 
     442             : 
     443             : /**
     444             :  * Handle a "/track/transfer" request.
     445             :  *
     446             :  * @param rh context of the handler
     447             :  * @param connection the MHD connection to handle
     448             :  * @param[in,out] connection_cls the connection's closure (can be updated)
     449             :  * @param upload_data upload data
     450             :  * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
     451             :  * @return MHD result code
     452             :  */
     453             : int
     454           3 : TEH_TRACKING_handler_track_transfer (struct TEH_RequestHandler *rh,
     455             :                                      struct MHD_Connection *connection,
     456             :                                      void **connection_cls,
     457             :                                      const char *upload_data,
     458             :                                      size_t *upload_data_size)
     459             : {
     460             :   struct WtidTransactionContext ctx;
     461             :   int res;
     462             :   int mhd_ret;
     463             : 
     464           3 :   memset (&ctx, 0, sizeof (ctx));
     465           3 :   res = TEH_PARSE_mhd_request_arg_data (connection,
     466             :                                         "wtid",
     467             :                                         &ctx.wtid,
     468             :                                         sizeof (struct TALER_WireTransferIdentifierRawP));
     469           3 :   if (GNUNET_SYSERR == res)
     470           0 :     return MHD_NO; /* internal error */
     471           3 :   if (GNUNET_NO == res)
     472           0 :     return MHD_YES; /* parse error */
     473           3 :   if (GNUNET_OK !=
     474           3 :       TEH_DB_run_transaction (connection,
     475             :                               &mhd_ret,
     476             :                               &track_transfer_transaction,
     477             :                               &ctx))
     478             :   {
     479           1 :     free_ctx (&ctx);
     480           1 :     return mhd_ret;
     481             :   }
     482           2 :   mhd_ret = reply_track_transfer_details (connection,
     483             :                                           &ctx.total,
     484             :                                           &ctx.merchant_pub,
     485             :                                           &ctx.h_wire,
     486             :                                           &ctx.wire_fee,
     487             :                                           ctx.exec_time,
     488           2 :                                           ctx.wdd_head);
     489           2 :   free_ctx (&ctx);
     490           2 :   return mhd_ret;
     491             : }
     492             : 
     493             : 
     494             : /* end of taler-exchange-httpd_track_transfer.c */

Generated by: LCOV version 1.13