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

Generated by: LCOV version 1.14