LCOV - code coverage report
Current view: top level - exchange - taler-exchange-httpd_transfers_get.c (source / functions) Hit Total Coverage
Test: GNU Taler exchange coverage report Lines: 81 130 62.3 %
Date: 2021-08-30 06:43:37 Functions: 5 5 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   Copyright (C) 2014-2018, 2021 Taler Systems SA
       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_transfers_get.c
      18             :  * @brief Handle wire transfer(s) GET 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_keys.h"
      28             : #include "taler-exchange-httpd_transfers_get.h"
      29             : #include "taler-exchange-httpd_responses.h"
      30             : #include "taler_json_lib.h"
      31             : #include "taler_mhd_lib.h"
      32             : 
      33             : 
      34             : /**
      35             :  * Information about one of the transactions that was
      36             :  * aggregated, to be returned in the /transfers response.
      37             :  */
      38             : struct AggregatedDepositDetail
      39             : {
      40             : 
      41             :   /**
      42             :    * We keep deposit details in a DLL.
      43             :    */
      44             :   struct AggregatedDepositDetail *next;
      45             : 
      46             :   /**
      47             :    * We keep deposit details in a DLL.
      48             :    */
      49             :   struct AggregatedDepositDetail *prev;
      50             : 
      51             :   /**
      52             :    * Hash of the contract terms.
      53             :    */
      54             :   struct GNUNET_HashCode h_contract_terms;
      55             : 
      56             :   /**
      57             :    * Coin's public key of the deposited coin.
      58             :    */
      59             :   struct TALER_CoinSpendPublicKeyP coin_pub;
      60             : 
      61             :   /**
      62             :    * Total value of the coin in the deposit.
      63             :    */
      64             :   struct TALER_Amount deposit_value;
      65             : 
      66             :   /**
      67             :    * Fees charged by the exchange for the deposit of this coin.
      68             :    */
      69             :   struct TALER_Amount deposit_fee;
      70             : };
      71             : 
      72             : 
      73             : /**
      74             :  * A merchant asked for transaction details about a wire transfer.
      75             :  * Provide them. Generates the 200 reply.
      76             :  *
      77             :  * @param connection connection to the client
      78             :  * @param total total amount that was transferred
      79             :  * @param merchant_pub public key of the merchant
      80             :  * @param h_wire destination account
      81             :  * @param wire_fee wire fee that was charged
      82             :  * @param exec_time execution time of the wire transfer
      83             :  * @param wdd_head linked list with details about the combined deposits
      84             :  * @return MHD result code
      85             :  */
      86             : static MHD_RESULT
      87           2 : reply_transfer_details (struct MHD_Connection *connection,
      88             :                         const struct TALER_Amount *total,
      89             :                         const struct TALER_MerchantPublicKeyP *merchant_pub,
      90             :                         const struct GNUNET_HashCode *h_wire,
      91             :                         const struct TALER_Amount *wire_fee,
      92             :                         struct GNUNET_TIME_Absolute exec_time,
      93             :                         const struct AggregatedDepositDetail *wdd_head)
      94             : {
      95             :   json_t *deposits;
      96             :   struct TALER_WireDepositDetailP dd;
      97             :   struct GNUNET_HashContext *hash_context;
      98             :   struct TALER_WireDepositDataPS wdp;
      99             :   struct TALER_ExchangePublicKeyP pub;
     100             :   struct TALER_ExchangeSignatureP sig;
     101             : 
     102           2 :   GNUNET_TIME_round_abs (&exec_time);
     103           2 :   deposits = json_array ();
     104           2 :   GNUNET_assert (NULL != deposits);
     105           2 :   hash_context = GNUNET_CRYPTO_hash_context_start ();
     106           4 :   for (const struct AggregatedDepositDetail *wdd_pos = wdd_head;
     107             :        NULL != wdd_pos;
     108           2 :        wdd_pos = wdd_pos->next)
     109             :   {
     110           2 :     dd.h_contract_terms = wdd_pos->h_contract_terms;
     111           2 :     dd.execution_time = GNUNET_TIME_absolute_hton (exec_time);
     112           2 :     dd.coin_pub = wdd_pos->coin_pub;
     113           2 :     TALER_amount_hton (&dd.deposit_value,
     114             :                        &wdd_pos->deposit_value);
     115           2 :     TALER_amount_hton (&dd.deposit_fee,
     116             :                        &wdd_pos->deposit_fee);
     117           2 :     GNUNET_CRYPTO_hash_context_read (hash_context,
     118             :                                      &dd,
     119             :                                      sizeof (struct TALER_WireDepositDetailP));
     120           2 :     if (0 !=
     121           2 :         json_array_append_new (
     122             :           deposits,
     123           2 :           GNUNET_JSON_PACK (
     124             :             GNUNET_JSON_pack_data_auto ("h_contract_terms",
     125             :                                         &wdd_pos->h_contract_terms),
     126             :             GNUNET_JSON_pack_data_auto ("coin_pub",
     127             :                                         &wdd_pos->coin_pub),
     128             :             TALER_JSON_pack_amount ("deposit_value",
     129             :                                     &wdd_pos->deposit_value),
     130             :             TALER_JSON_pack_amount ("deposit_fee",
     131             :                                     &wdd_pos->deposit_fee))))
     132             :     {
     133           0 :       json_decref (deposits);
     134           0 :       GNUNET_CRYPTO_hash_context_abort (hash_context);
     135           0 :       return TALER_MHD_reply_with_error (connection,
     136             :                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
     137             :                                          TALER_EC_GENERIC_JSON_ALLOCATION_FAILURE,
     138             :                                          "json_array_append_new() failed");
     139             :     }
     140             :   }
     141           2 :   wdp.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_WIRE_DEPOSIT);
     142           2 :   wdp.purpose.size = htonl (sizeof (struct TALER_WireDepositDataPS));
     143           2 :   TALER_amount_hton (&wdp.total,
     144             :                      total);
     145           2 :   TALER_amount_hton (&wdp.wire_fee,
     146             :                      wire_fee);
     147           2 :   wdp.merchant_pub = *merchant_pub;
     148           2 :   wdp.h_wire = *h_wire;
     149           2 :   GNUNET_CRYPTO_hash_context_finish (hash_context,
     150             :                                      &wdp.h_details);
     151             :   {
     152             :     enum TALER_ErrorCode ec;
     153             : 
     154           2 :     if (TALER_EC_NONE !=
     155           2 :         (ec = TEH_keys_exchange_sign (&wdp,
     156             :                                       &pub,
     157             :                                       &sig)))
     158             :     {
     159           0 :       json_decref (deposits);
     160           0 :       return TALER_MHD_reply_with_ec (connection,
     161             :                                       ec,
     162             :                                       NULL);
     163             :     }
     164             :   }
     165             : 
     166           2 :   return TALER_MHD_REPLY_JSON_PACK (
     167             :     connection,
     168             :     MHD_HTTP_OK,
     169             :     TALER_JSON_pack_amount ("total",
     170             :                             total),
     171             :     TALER_JSON_pack_amount ("wire_fee",
     172             :                             wire_fee),
     173             :     GNUNET_JSON_pack_data_auto ("merchant_pub",
     174             :                                 merchant_pub),
     175             :     GNUNET_JSON_pack_data_auto ("h_wire",
     176             :                                 h_wire),
     177             :     GNUNET_JSON_pack_time_abs ("execution_time",
     178             :                                exec_time),
     179             :     GNUNET_JSON_pack_array_steal ("deposits",
     180             :                                   deposits),
     181             :     GNUNET_JSON_pack_data_auto ("exchange_sig",
     182             :                                 &sig),
     183             :     GNUNET_JSON_pack_data_auto ("exchange_pub",
     184             :                                 &pub));
     185             : }
     186             : 
     187             : 
     188             : /**
     189             :  * Closure for #handle_transaction_data.
     190             :  */
     191             : struct WtidTransactionContext
     192             : {
     193             : 
     194             :   /**
     195             :    * Identifier of the wire transfer to track.
     196             :    */
     197             :   struct TALER_WireTransferIdentifierRawP wtid;
     198             : 
     199             :   /**
     200             :    * Total amount of the wire transfer, as calculated by
     201             :    * summing up the individual amounts. To be rounded down
     202             :    * to calculate the real transfer amount at the end.
     203             :    * Only valid if @e is_valid is #GNUNET_YES.
     204             :    */
     205             :   struct TALER_Amount total;
     206             : 
     207             :   /**
     208             :    * Public key of the merchant, only valid if @e is_valid
     209             :    * is #GNUNET_YES.
     210             :    */
     211             :   struct TALER_MerchantPublicKeyP merchant_pub;
     212             : 
     213             :   /**
     214             :    * Hash of the wire details of the merchant (identical for all
     215             :    * deposits), only valid if @e is_valid is #GNUNET_YES.
     216             :    */
     217             :   struct GNUNET_HashCode h_wire;
     218             : 
     219             :   /**
     220             :    * Wire fee applicable at @e exec_time.
     221             :    */
     222             :   struct TALER_Amount wire_fee;
     223             : 
     224             :   /**
     225             :    * Execution time of the wire transfer
     226             :    */
     227             :   struct GNUNET_TIME_Absolute exec_time;
     228             : 
     229             :   /**
     230             :    * Head of DLL with deposit details for transfers GET response.
     231             :    */
     232             :   struct AggregatedDepositDetail *wdd_head;
     233             : 
     234             :   /**
     235             :    * Tail of DLL with deposit details for transfers GET response.
     236             :    */
     237             :   struct AggregatedDepositDetail *wdd_tail;
     238             : 
     239             :   /**
     240             :    * Which method was used to wire the funds?
     241             :    */
     242             :   char *wire_method;
     243             : 
     244             :   /**
     245             :    * JSON array with details about the individual deposits.
     246             :    */
     247             :   json_t *deposits;
     248             : 
     249             :   /**
     250             :    * Initially #GNUNET_NO, if we found no deposits so far.  Set to
     251             :    * #GNUNET_YES if we got transaction data, and the database replies
     252             :    * remained consistent with respect to @e merchant_pub and @e h_wire
     253             :    * (as they should).  Set to #GNUNET_SYSERR if we encountered an
     254             :    * internal error.
     255             :    */
     256             :   int is_valid;
     257             : 
     258             : };
     259             : 
     260             : 
     261             : /**
     262             :  * Function called with the results of the lookup of the individual deposits
     263             :  * that were aggregated for the given wire transfer.
     264             :  *
     265             :  * @param cls our context for transmission
     266             :  * @param rowid which row in the DB is the information from (for diagnostics), ignored
     267             :  * @param merchant_pub public key of the merchant (should be same for all callbacks with the same @e cls)
     268             :  * @param h_wire hash of wire transfer details of the merchant (should be same for all callbacks with the same @e cls)
     269             :  * @param wire where the funds were sent
     270             :  * @param exec_time execution time of the wire transfer (should be same for all callbacks with the same @e cls)
     271             :  * @param h_contract_terms which proposal was this payment about
     272             :  * @param denom_pub denomination public key of the @a coin_pub (ignored)
     273             :  * @param coin_pub which public key was this payment about
     274             :  * @param deposit_value amount contributed by this coin in total
     275             :  * @param deposit_fee deposit fee charged by exchange for this coin
     276             :  */
     277             : static void
     278           2 : handle_deposit_data (void *cls,
     279             :                      uint64_t rowid,
     280             :                      const struct TALER_MerchantPublicKeyP *merchant_pub,
     281             :                      const struct GNUNET_HashCode *h_wire,
     282             :                      const json_t *wire,
     283             :                      struct GNUNET_TIME_Absolute exec_time,
     284             :                      const struct GNUNET_HashCode *h_contract_terms,
     285             :                      const struct TALER_DenominationPublicKey *denom_pub,
     286             :                      const struct TALER_CoinSpendPublicKeyP *coin_pub,
     287             :                      const struct TALER_Amount *deposit_value,
     288             :                      const struct TALER_Amount *deposit_fee)
     289             : {
     290           2 :   struct WtidTransactionContext *ctx = cls;
     291             :   char *wire_method;
     292             : 
     293             :   (void) rowid;
     294             :   (void) denom_pub;
     295           2 :   if (GNUNET_SYSERR == ctx->is_valid)
     296           0 :     return;
     297           2 :   if (NULL == (wire_method = TALER_JSON_wire_to_method (wire)))
     298             :   {
     299           0 :     GNUNET_break (0);
     300           0 :     ctx->is_valid = GNUNET_SYSERR;
     301           0 :     return;
     302             :   }
     303           2 :   if (GNUNET_NO == ctx->is_valid)
     304             :   {
     305             :     /* First one we encounter, setup general information in 'ctx' */
     306           2 :     ctx->merchant_pub = *merchant_pub;
     307           2 :     ctx->h_wire = *h_wire;
     308           2 :     ctx->exec_time = exec_time;
     309           2 :     ctx->wire_method = wire_method; /* captures the reference */
     310           2 :     ctx->is_valid = GNUNET_YES;
     311           2 :     if (0 >
     312           2 :         TALER_amount_subtract (&ctx->total,
     313             :                                deposit_value,
     314             :                                deposit_fee))
     315             :     {
     316           0 :       GNUNET_break (0);
     317           0 :       ctx->is_valid = GNUNET_SYSERR;
     318           0 :       return;
     319             :     }
     320             :   }
     321             :   else
     322             :   {
     323             :     struct TALER_Amount delta;
     324             : 
     325             :     /* Subsequent data, check general information matches that in 'ctx';
     326             :        (it should, otherwise the deposits should not have been aggregated) */
     327           0 :     if ( (0 != GNUNET_memcmp (&ctx->merchant_pub,
     328           0 :                               merchant_pub)) ||
     329           0 :          (0 != strcmp (wire_method,
     330           0 :                        ctx->wire_method)) ||
     331           0 :          (0 != GNUNET_memcmp (&ctx->h_wire,
     332             :                               h_wire)) )
     333             :     {
     334           0 :       GNUNET_break (0);
     335           0 :       ctx->is_valid = GNUNET_SYSERR;
     336           0 :       GNUNET_free (wire_method);
     337           0 :       return;
     338             :     }
     339           0 :     GNUNET_free (wire_method);
     340           0 :     if (0 >
     341           0 :         TALER_amount_subtract (&delta,
     342             :                                deposit_value,
     343             :                                deposit_fee))
     344             :     {
     345           0 :       GNUNET_break (0);
     346           0 :       ctx->is_valid = GNUNET_SYSERR;
     347           0 :       return;
     348             :     }
     349           0 :     if (0 >
     350           0 :         TALER_amount_add (&ctx->total,
     351           0 :                           &ctx->total,
     352             :                           &delta))
     353             :     {
     354           0 :       GNUNET_break (0);
     355           0 :       ctx->is_valid = GNUNET_SYSERR;
     356           0 :       return;
     357             :     }
     358             :   }
     359             : 
     360             :   {
     361             :     struct AggregatedDepositDetail *wdd;
     362             : 
     363           2 :     wdd = GNUNET_new (struct AggregatedDepositDetail);
     364           2 :     wdd->deposit_value = *deposit_value;
     365           2 :     wdd->deposit_fee = *deposit_fee;
     366           2 :     wdd->h_contract_terms = *h_contract_terms;
     367           2 :     wdd->coin_pub = *coin_pub;
     368           2 :     GNUNET_CONTAINER_DLL_insert (ctx->wdd_head,
     369             :                                  ctx->wdd_tail,
     370             :                                  wdd);
     371             :   }
     372             : }
     373             : 
     374             : 
     375             : /**
     376             :  * Free data structure reachable from @a ctx, but not @a ctx itself.
     377             :  *
     378             :  * @param ctx context to free
     379             :  */
     380             : static void
     381           6 : free_ctx (struct WtidTransactionContext *ctx)
     382             : {
     383             :   struct AggregatedDepositDetail *wdd;
     384             : 
     385           8 :   while (NULL != (wdd = ctx->wdd_head))
     386             :   {
     387           2 :     GNUNET_CONTAINER_DLL_remove (ctx->wdd_head,
     388             :                                  ctx->wdd_tail,
     389             :                                  wdd);
     390           2 :     GNUNET_free (wdd);
     391             :   }
     392           6 :   GNUNET_free (ctx->wire_method);
     393           6 :   ctx->wire_method = NULL;
     394           6 : }
     395             : 
     396             : 
     397             : /**
     398             :  * Execute a "/transfers" GET operation.  Returns the deposit details of the
     399             :  * deposits that were aggregated to create the given wire transfer.
     400             :  *
     401             :  * If it returns a non-error code, the transaction logic MUST
     402             :  * NOT queue a MHD response.  IF it returns an hard error, the
     403             :  * transaction logic MUST queue a MHD response and set @a mhd_ret.  IF
     404             :  * it returns the soft error code, the function MAY be called again to
     405             :  * retry and MUST not queue a MHD response.
     406             :  *
     407             :  * @param cls closure
     408             :  * @param connection MHD request which triggered the transaction
     409             :  * @param[out] mhd_ret set to MHD response status for @a connection,
     410             :  *             if transaction failed (!)
     411             :  * @return transaction status
     412             :  */
     413             : static enum GNUNET_DB_QueryStatus
     414           3 : get_transfer_deposits (void *cls,
     415             :                        struct MHD_Connection *connection,
     416             :                        MHD_RESULT *mhd_ret)
     417             : {
     418           3 :   struct WtidTransactionContext *ctx = cls;
     419             :   enum GNUNET_DB_QueryStatus qs;
     420             :   struct GNUNET_TIME_Absolute wire_fee_start_date;
     421             :   struct GNUNET_TIME_Absolute wire_fee_end_date;
     422             :   struct TALER_MasterSignatureP wire_fee_master_sig;
     423             :   struct TALER_Amount closing_fee;
     424             : 
     425             :   /* resetting to NULL/0 in case transaction was repeated after
     426             :      serialization failure */
     427           3 :   free_ctx (ctx);
     428           3 :   qs = TEH_plugin->lookup_wire_transfer (TEH_plugin->cls,
     429           3 :                                          &ctx->wtid,
     430             :                                          &handle_deposit_data,
     431             :                                          ctx);
     432           3 :   if (0 > qs)
     433             :   {
     434           0 :     if (GNUNET_DB_STATUS_HARD_ERROR == qs)
     435             :     {
     436           0 :       GNUNET_break (0);
     437           0 :       *mhd_ret = TALER_MHD_reply_with_error (connection,
     438             :                                              MHD_HTTP_INTERNAL_SERVER_ERROR,
     439             :                                              TALER_EC_GENERIC_DB_FETCH_FAILED,
     440             :                                              "wire transfer");
     441             :     }
     442           0 :     return qs;
     443             :   }
     444           3 :   if (GNUNET_SYSERR == ctx->is_valid)
     445             :   {
     446           0 :     GNUNET_break (0);
     447           0 :     *mhd_ret = TALER_MHD_reply_with_error (connection,
     448             :                                            MHD_HTTP_INTERNAL_SERVER_ERROR,
     449             :                                            TALER_EC_GENERIC_DB_INVARIANT_FAILURE,
     450             :                                            "wire history malformed");
     451           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
     452             :   }
     453           3 :   if (GNUNET_NO == ctx->is_valid)
     454             :   {
     455           1 :     *mhd_ret = TALER_MHD_reply_with_error (connection,
     456             :                                            MHD_HTTP_NOT_FOUND,
     457             :                                            TALER_EC_EXCHANGE_TRANSFERS_GET_WTID_NOT_FOUND,
     458             :                                            NULL);
     459           1 :     return GNUNET_DB_STATUS_HARD_ERROR;
     460             :   }
     461           2 :   qs = TEH_plugin->get_wire_fee (TEH_plugin->cls,
     462           2 :                                  ctx->wire_method,
     463             :                                  ctx->exec_time,
     464             :                                  &wire_fee_start_date,
     465             :                                  &wire_fee_end_date,
     466             :                                  &ctx->wire_fee,
     467             :                                  &closing_fee,
     468             :                                  &wire_fee_master_sig);
     469           2 :   if (0 >= qs)
     470             :   {
     471           0 :     if ( (GNUNET_DB_STATUS_HARD_ERROR == qs) ||
     472             :          (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) )
     473             :     {
     474           0 :       GNUNET_break (0);
     475           0 :       *mhd_ret = TALER_MHD_reply_with_error (connection,
     476             :                                              MHD_HTTP_INTERNAL_SERVER_ERROR,
     477             :                                              TALER_EC_EXCHANGE_TRANSFERS_GET_WIRE_FEE_NOT_FOUND,
     478             :                                              NULL);
     479             :     }
     480           0 :     return qs;
     481             :   }
     482           2 :   if (0 >
     483           2 :       TALER_amount_subtract (&ctx->total,
     484           2 :                              &ctx->total,
     485           2 :                              &ctx->wire_fee))
     486             :   {
     487           0 :     GNUNET_break (0);
     488           0 :     *mhd_ret = TALER_MHD_reply_with_error (connection,
     489             :                                            MHD_HTTP_INTERNAL_SERVER_ERROR,
     490             :                                            TALER_EC_EXCHANGE_TRANSFERS_GET_WIRE_FEE_INCONSISTENT,
     491             :                                            NULL);
     492           0 :     return GNUNET_DB_STATUS_HARD_ERROR;
     493             :   }
     494           2 :   return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
     495             : }
     496             : 
     497             : 
     498             : MHD_RESULT
     499           3 : TEH_handler_transfers_get (struct TEH_RequestContext *rc,
     500             :                            const char *const args[1])
     501             : {
     502             :   struct WtidTransactionContext ctx;
     503             :   MHD_RESULT mhd_ret;
     504             : 
     505           3 :   memset (&ctx,
     506             :           0,
     507             :           sizeof (ctx));
     508           3 :   if (GNUNET_OK !=
     509           3 :       GNUNET_STRINGS_string_to_data (args[0],
     510             :                                      strlen (args[0]),
     511             :                                      &ctx.wtid,
     512             :                                      sizeof (ctx.wtid)))
     513             :   {
     514           0 :     GNUNET_break_op (0);
     515           0 :     return TALER_MHD_reply_with_error (rc->connection,
     516             :                                        MHD_HTTP_BAD_REQUEST,
     517             :                                        TALER_EC_EXCHANGE_TRANSFERS_GET_WTID_MALFORMED,
     518             :                                        args[0]);
     519             :   }
     520           3 :   if (GNUNET_OK !=
     521           3 :       TEH_DB_run_transaction (rc->connection,
     522             :                               "run transfers GET",
     523             :                               &mhd_ret,
     524             :                               &get_transfer_deposits,
     525             :                               &ctx))
     526             :   {
     527           1 :     free_ctx (&ctx);
     528           1 :     return mhd_ret;
     529             :   }
     530           4 :   mhd_ret = reply_transfer_details (rc->connection,
     531             :                                     &ctx.total,
     532             :                                     &ctx.merchant_pub,
     533             :                                     &ctx.h_wire,
     534             :                                     &ctx.wire_fee,
     535             :                                     ctx.exec_time,
     536           2 :                                     ctx.wdd_head);
     537           2 :   free_ctx (&ctx);
     538           2 :   return mhd_ret;
     539             : }
     540             : 
     541             : 
     542             : /* end of taler-exchange-httpd_transfers_get.c */

Generated by: LCOV version 1.14