LCOV - code coverage report
Current view: top level - exchange - taler-exchange-httpd_track_transfer.c (source / functions) Hit Total Coverage
Test: rcoverage.info Lines: 78 117 66.7 %
Date: 2017-09-17 17:24:28 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 :   TEH_KS_sign (&wdp.purpose,
     135             :                &pub,
     136             :                &sig);
     137           2 :   return TEH_RESPONSE_reply_json_pack (connection,
     138             :                                        MHD_HTTP_OK,
     139             :                                        "{s:o, s:o, s:o, s:o, s:o, s:o, s:o, s:o}",
     140             :                                        "total", TALER_JSON_from_amount (total),
     141             :                                        "wire_fee", TALER_JSON_from_amount (wire_fee),
     142             :                                        "merchant_pub", GNUNET_JSON_from_data_auto (merchant_pub),
     143             :                                        "H_wire", GNUNET_JSON_from_data_auto (h_wire),
     144             :                                        "execution_time", GNUNET_JSON_from_time_abs (exec_time),
     145             :                                        "deposits", deposits,
     146             :                                        "exchange_sig", GNUNET_JSON_from_data_auto (&sig),
     147             :                                        "exchange_pub", GNUNET_JSON_from_data_auto (&pub));
     148             : }
     149             : 
     150             : 
     151             : /**
     152             :  * Closure for #handle_transaction_data.
     153             :  */
     154             : struct WtidTransactionContext
     155             : {
     156             : 
     157             :   /**
     158             :    * Identifier of the wire transfer to track.
     159             :    */
     160             :   struct TALER_WireTransferIdentifierRawP wtid;
     161             : 
     162             :   /**
     163             :    * Total amount of the wire transfer, as calculated by
     164             :    * summing up the individual amounts. To be rounded down
     165             :    * to calculate the real transfer amount at the end.
     166             :    * Only valid if @e is_valid is #GNUNET_YES.
     167             :    */
     168             :   struct TALER_Amount total;
     169             : 
     170             :   /**
     171             :    * Public key of the merchant, only valid if @e is_valid
     172             :    * is #GNUNET_YES.
     173             :    */
     174             :   struct TALER_MerchantPublicKeyP merchant_pub;
     175             : 
     176             :   /**
     177             :    * Which method was used to wire the funds?
     178             :    */
     179             :   char *wire_method;
     180             : 
     181             :   /**
     182             :    * Hash of the wire details of the merchant (identical for all
     183             :    * deposits), only valid if @e is_valid is #GNUNET_YES.
     184             :    */
     185             :   struct GNUNET_HashCode h_wire;
     186             : 
     187             :   /**
     188             :    * Wire fee applicable at @e exec_time.
     189             :    */
     190             :   struct TALER_Amount wire_fee;
     191             : 
     192             :   /**
     193             :    * Execution time of the wire transfer
     194             :    */
     195             :   struct GNUNET_TIME_Absolute exec_time;
     196             : 
     197             :   /**
     198             :    * Head of DLL with details for /wire/deposit response.
     199             :    */
     200             :   struct TEH_TrackTransferDetail *wdd_head;
     201             : 
     202             :   /**
     203             :    * Head of DLL with details for /wire/deposit response.
     204             :    */
     205             :   struct TEH_TrackTransferDetail *wdd_tail;
     206             : 
     207             :   /**
     208             :    * JSON array with details about the individual deposits.
     209             :    */
     210             :   json_t *deposits;
     211             : 
     212             :   /**
     213             :    * Initially #GNUNET_NO, if we found no deposits so far.  Set to
     214             :    * #GNUNET_YES if we got transaction data, and the database replies
     215             :    * remained consistent with respect to @e merchant_pub and @e h_wire
     216             :    * (as they should).  Set to #GNUNET_SYSERR if we encountered an
     217             :    * internal error.
     218             :    */
     219             :   int is_valid;
     220             : 
     221             : };
     222             : 
     223             : 
     224             : /**
     225             :  * Function called with the results of the lookup of the
     226             :  * transaction data for the given wire transfer identifier.
     227             :  *
     228             :  * @param cls our context for transmission
     229             :  * @param rowid which row in the DB is the information from (for diagnostics)
     230             :  * @param merchant_pub public key of the merchant (should be same for all callbacks with the same @e cls)
     231             :  * @param wire_method which wire plugin was used
     232             :  * @param h_wire hash of wire transfer details of the merchant (should be same for all callbacks with the same @e cls)
     233             :  * @param exec_time execution time of the wire transfer (should be same for all callbacks with the same @e cls)
     234             :  * @param h_contract_terms which proposal was this payment about
     235             :  * @param coin_pub which public key was this payment about
     236             :  * @param deposit_value amount contributed by this coin in total
     237             :  * @param deposit_fee deposit fee charged by exchange for this coin
     238             :  */
     239             : static void
     240           2 : handle_transaction_data (void *cls,
     241             :                          uint64_t rowid,
     242             :                          const struct TALER_MerchantPublicKeyP *merchant_pub,
     243             :                          const char *wire_method,
     244             :                          const struct GNUNET_HashCode *h_wire,
     245             :                          struct GNUNET_TIME_Absolute exec_time,
     246             :                          const struct GNUNET_HashCode *h_contract_terms,
     247             :                          const struct TALER_CoinSpendPublicKeyP *coin_pub,
     248             :                          const struct TALER_Amount *deposit_value,
     249             :                          const struct TALER_Amount *deposit_fee)
     250             : {
     251           2 :   struct WtidTransactionContext *ctx = cls;
     252             :   struct TALER_Amount delta;
     253             :   struct TEH_TrackTransferDetail *wdd;
     254             : 
     255           2 :   if (GNUNET_SYSERR == ctx->is_valid)
     256           0 :     return;
     257           2 :   if (GNUNET_NO == ctx->is_valid)
     258             :   {
     259           2 :     ctx->merchant_pub = *merchant_pub;
     260           2 :     ctx->h_wire = *h_wire;
     261           2 :     ctx->exec_time = exec_time;
     262           2 :     ctx->wire_method = GNUNET_strdup (wire_method);
     263           2 :     ctx->is_valid = GNUNET_YES;
     264           2 :     if (GNUNET_OK !=
     265           2 :         TALER_amount_subtract (&ctx->total,
     266             :                                deposit_value,
     267             :                                deposit_fee))
     268             :     {
     269           0 :       GNUNET_break (0);
     270           0 :       ctx->is_valid = GNUNET_SYSERR;
     271           0 :       return;
     272             :     }
     273             :   }
     274             :   else
     275             :   {
     276           0 :     if ( (0 != memcmp (&ctx->merchant_pub,
     277             :                        merchant_pub,
     278           0 :                        sizeof (struct TALER_MerchantPublicKeyP))) ||
     279           0 :          (0 != strcmp (wire_method,
     280           0 :                        ctx->wire_method)) ||
     281           0 :          (0 != memcmp (&ctx->h_wire,
     282             :                        h_wire,
     283             :                        sizeof (struct GNUNET_HashCode))) )
     284             :     {
     285           0 :       GNUNET_break (0);
     286           0 :       ctx->is_valid = GNUNET_SYSERR;
     287           0 :       return;
     288             :     }
     289           0 :     if (GNUNET_OK !=
     290           0 :         TALER_amount_subtract (&delta,
     291             :                                deposit_value,
     292             :                                deposit_fee))
     293             :     {
     294           0 :       GNUNET_break (0);
     295           0 :       ctx->is_valid = GNUNET_SYSERR;
     296           0 :       return;
     297             :     }
     298           0 :     if (GNUNET_OK !=
     299           0 :         TALER_amount_add (&ctx->total,
     300           0 :                           &ctx->total,
     301             :                           &delta))
     302             :     {
     303           0 :       GNUNET_break (0);
     304           0 :       ctx->is_valid = GNUNET_SYSERR;
     305           0 :       return;
     306             :     }
     307             :   }
     308           2 :   wdd = GNUNET_new (struct TEH_TrackTransferDetail);
     309           2 :   wdd->deposit_value = *deposit_value;
     310           2 :   wdd->deposit_fee = *deposit_fee;
     311           2 :   wdd->h_contract_terms = *h_contract_terms;
     312           2 :   wdd->coin_pub = *coin_pub;
     313           2 :   GNUNET_CONTAINER_DLL_insert (ctx->wdd_head,
     314             :                                ctx->wdd_tail,
     315             :                                wdd);
     316             : }
     317             : 
     318             : 
     319             : /**
     320             :  * Execute a "/track/transfer".  Returns the transaction information
     321             :  * associated with the given wire transfer identifier.
     322             :  * 
     323             :  * If it returns a non-error code, the transaction logic MUST
     324             :  * NOT queue a MHD response.  IF it returns an hard error, the
     325             :  * transaction logic MUST queue a MHD response and set @a mhd_ret.  IF
     326             :  * it returns the soft error code, the function MAY be called again to
     327             :  * retry and MUST not queue a MHD response.
     328             :  *
     329             :  * @param cls closure
     330             :  * @param connection MHD request which triggered the transaction
     331             :  * @param session database session to use
     332             :  * @param[out] mhd_ret set to MHD response status for @a connection,
     333             :  *             if transaction failed (!)
     334             :  * @return transaction status
     335             :  */
     336             : static enum GNUNET_DB_QueryStatus
     337           3 : track_transfer_transaction (void *cls,
     338             :                             struct MHD_Connection *connection,
     339             :                             struct TALER_EXCHANGEDB_Session *session,
     340             :                             int *mhd_ret)
     341             : {
     342           3 :   struct WtidTransactionContext *ctx = cls;
     343             :   enum GNUNET_DB_QueryStatus qs;
     344             :   struct GNUNET_TIME_Absolute wire_fee_start_date;
     345             :   struct GNUNET_TIME_Absolute wire_fee_end_date;
     346             :   struct TALER_MasterSignatureP wire_fee_master_sig;
     347             : 
     348           3 :   ctx->is_valid = GNUNET_NO;
     349           3 :   ctx->wdd_head = NULL;
     350           3 :   ctx->wdd_tail = NULL;
     351           3 :   ctx->wire_method = NULL;
     352           6 :   qs = TEH_plugin->lookup_wire_transfer (TEH_plugin->cls,
     353             :                                          session,
     354           3 :                                          &ctx->wtid,
     355             :                                          &handle_transaction_data,
     356             :                                          ctx);
     357           3 :   if (0 > qs)
     358             :   {
     359           0 :     if (GNUNET_DB_STATUS_HARD_ERROR == qs)
     360             :     {
     361           0 :       GNUNET_break (0);
     362           0 :       *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
     363             :                                                        TALER_EC_TRACK_TRANSFER_DB_FETCH_FAILED);
     364             :     }
     365           0 :     return qs;
     366             :   }
     367           3 :   if (GNUNET_SYSERR == ctx->is_valid)
     368             :   {
     369           0 :     GNUNET_break (0);
     370           0 :     *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
     371             :                                                      TALER_EC_TRACK_TRANSFER_DB_INCONSISTENT);
     372           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
     373             :   }
     374           3 :   if (GNUNET_NO == ctx->is_valid)
     375             :   {
     376           1 :     *mhd_ret = TEH_RESPONSE_reply_arg_unknown (connection,
     377             :                                                TALER_EC_TRACK_TRANSFER_WTID_NOT_FOUND,
     378             :                                                "wtid");
     379           1 :     return GNUNET_DB_STATUS_HARD_ERROR;
     380             :   }
     381           4 :   qs = TEH_plugin->get_wire_fee (TEH_plugin->cls,
     382             :                                  session,
     383           2 :                                  ctx->wire_method,
     384             :                                  ctx->exec_time,
     385             :                                  &wire_fee_start_date,
     386             :                                  &wire_fee_end_date,
     387             :                                  &ctx->wire_fee,
     388             :                                  &wire_fee_master_sig);
     389           2 :   if (0 >= qs)
     390             :   {
     391           0 :     if ( (GNUNET_DB_STATUS_HARD_ERROR == qs) ||
     392             :          (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS) )
     393             :     {
     394           0 :       GNUNET_break (0);
     395           0 :       *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
     396             :                                                        TALER_EC_TRACK_TRANSFER_WIRE_FEE_NOT_FOUND);
     397             :     }
     398           0 :     return qs;
     399             :   }
     400           2 :   if (GNUNET_OK !=
     401           2 :       TALER_amount_subtract (&ctx->total,
     402           2 :                              &ctx->total,
     403           2 :                              &ctx->wire_fee))
     404             :   {
     405           0 :     GNUNET_break (0);
     406           0 :     *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
     407             :                                                      TALER_EC_TRACK_TRANSFER_WIRE_FEE_INCONSISTENT);
     408           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
     409             :   }
     410           2 :   return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
     411             : }
     412             : 
     413             : 
     414             : /**
     415             :  * Free data structure reachable from @a ctx, but not @a ctx itself.
     416             :  *
     417             :  * @param ctx context to free
     418             :  */
     419             : static void
     420           3 : free_ctx (struct WtidTransactionContext *ctx)
     421             : {
     422             :   struct TEH_TrackTransferDetail *wdd;
     423             : 
     424           8 :   while (NULL != (wdd = ctx->wdd_head))
     425             :   {
     426           2 :     GNUNET_CONTAINER_DLL_remove (ctx->wdd_head,
     427             :                                  ctx->wdd_tail,
     428             :                                  wdd);
     429           2 :     GNUNET_free (wdd);
     430             :   }
     431           3 :   GNUNET_free_non_null (ctx->wire_method);
     432           3 : }
     433             : 
     434             : 
     435             : /**
     436             :  * Handle a "/track/transfer" request.
     437             :  *
     438             :  * @param rh context of the handler
     439             :  * @param connection the MHD connection to handle
     440             :  * @param[in,out] connection_cls the connection's closure (can be updated)
     441             :  * @param upload_data upload data
     442             :  * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
     443             :  * @return MHD result code
     444             :  */
     445             : int
     446           3 : TEH_TRACKING_handler_track_transfer (struct TEH_RequestHandler *rh,
     447             :                                      struct MHD_Connection *connection,
     448             :                                      void **connection_cls,
     449             :                                      const char *upload_data,
     450             :                                      size_t *upload_data_size)
     451             : {
     452             :   struct WtidTransactionContext ctx;
     453             :   int res;
     454             :   int mhd_ret;
     455             : 
     456           3 :   memset (&ctx, 0, sizeof (ctx));
     457           3 :   res = TEH_PARSE_mhd_request_arg_data (connection,
     458             :                                         "wtid",
     459             :                                         &ctx.wtid,
     460             :                                         sizeof (struct TALER_WireTransferIdentifierRawP));
     461           3 :   if (GNUNET_SYSERR == res)
     462           0 :     return MHD_NO; /* internal error */
     463           3 :   if (GNUNET_NO == res)
     464           0 :     return MHD_YES; /* parse error */
     465           3 :   if (GNUNET_OK !=
     466           3 :       TEH_DB_run_transaction (connection,
     467             :                               &mhd_ret,
     468             :                               &track_transfer_transaction,
     469             :                               &ctx))
     470             :   {
     471           1 :     free_ctx (&ctx);
     472           1 :     return mhd_ret;
     473             :   }
     474           2 :   mhd_ret = reply_track_transfer_details (connection,
     475             :                                           &ctx.total,
     476             :                                           &ctx.merchant_pub,
     477             :                                           &ctx.h_wire,
     478             :                                           &ctx.wire_fee,
     479             :                                           ctx.exec_time,
     480           2 :                                           ctx.wdd_head);
     481           2 :   free_ctx (&ctx);
     482           2 :   return mhd_ret;
     483             : }
     484             : 
     485             : 
     486             : /* end of taler-exchange-httpd_track_transfer.c */

Generated by: LCOV version 1.13