exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

taler-exchange-httpd_get-transfers-WTID.c (22198B)


      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_get-transfers-WTID.c
     18  * @brief Handle wire transfer(s) GET requests
     19  * @author Christian Grothoff
     20  */
     21 #include <gnunet/gnunet_util_lib.h>
     22 #include <jansson.h>
     23 #include <microhttpd.h>
     24 #include <pthread.h>
     25 #include "taler-exchange-httpd_get-keys.h"
     26 #include "taler-exchange-httpd_get-transfers-WTID.h"
     27 #include "taler-exchange-httpd_responses.h"
     28 #include "taler/taler_json_lib.h"
     29 #include "taler/taler_mhd_lib.h"
     30 #define TALER_EXCHANGEDB_REFUND_COIN_RESULT_CLOSURE struct TALER_Amount
     31 #include "exchange-database/iterate_refunds_by_coin.h"
     32 #include "exchange-database/get_wire_fee.h"
     33 struct WtidTransactionContext;
     34 #define TALER_EXCHANGEDB_AGGREGATION_DATA_RESULT_CLOSURE \
     35         struct WtidTransactionContext
     36 #include "exchange-database/iterate_wire_transfers.h"
     37 
     38 
     39 /**
     40  * Information about one of the transactions that was
     41  * aggregated, to be returned in the /transfers response.
     42  */
     43 struct AggregatedDepositDetail
     44 {
     45 
     46   /**
     47    * We keep deposit details in a DLL.
     48    */
     49   struct AggregatedDepositDetail *next;
     50 
     51   /**
     52    * We keep deposit details in a DLL.
     53    */
     54   struct AggregatedDepositDetail *prev;
     55 
     56   /**
     57    * Hash of the contract terms.
     58    */
     59   struct TALER_PrivateContractHashP h_contract_terms;
     60 
     61   /**
     62    * Coin's public key of the deposited coin.
     63    */
     64   struct TALER_CoinSpendPublicKeyP coin_pub;
     65 
     66   /**
     67    * Total value of the coin in the deposit (after
     68    * refunds).
     69    */
     70   struct TALER_Amount deposit_value;
     71 
     72   /**
     73    * Fees charged by the exchange for the deposit of this coin (possibly after reduction due to refunds).
     74    */
     75   struct TALER_Amount deposit_fee;
     76 
     77   /**
     78    * Total amount refunded for this coin.
     79    */
     80   struct TALER_Amount refund_total;
     81 };
     82 
     83 
     84 /**
     85  * A merchant asked for transaction details about a wire transfer.
     86  * Provide them. Generates the 200 reply.
     87  *
     88  * @param connection connection to the client
     89  * @param total total amount that was transferred
     90  * @param merchant_pub public key of the merchant
     91  * @param payto_uri destination account
     92  * @param exchange_payto_uri our own account that was debited, `full_payto`
     93  *        may be NULL if we did not record it
     94  * @param wire_fee wire fee that was charged
     95  * @param exec_time execution time of the wire transfer
     96  * @param wdd_head linked list with details about the combined deposits
     97  * @return MHD result code
     98  */
     99 static enum MHD_Result
    100 reply_transfer_details (struct MHD_Connection *connection,
    101                         const struct TALER_Amount *total,
    102                         const struct TALER_MerchantPublicKeyP *merchant_pub,
    103                         const struct TALER_FullPayto payto_uri,
    104                         const struct TALER_FullPayto exchange_payto_uri,
    105                         const struct TALER_Amount *wire_fee,
    106                         struct GNUNET_TIME_Timestamp exec_time,
    107                         const struct AggregatedDepositDetail *wdd_head)
    108 {
    109   json_t *deposits;
    110   struct GNUNET_HashContext *hash_context;
    111   struct GNUNET_HashCode h_details;
    112   struct TALER_ExchangePublicKeyP pub;
    113   struct TALER_ExchangeSignatureP sig;
    114   struct TALER_FullPaytoHashP h_payto;
    115 
    116   deposits = json_array ();
    117   GNUNET_assert (NULL != deposits);
    118   hash_context = GNUNET_CRYPTO_hash_context_start ();
    119   for (const struct AggregatedDepositDetail *wdd_pos = wdd_head;
    120        NULL != wdd_pos;
    121        wdd_pos = wdd_pos->next)
    122   {
    123     TALER_exchange_online_wire_deposit_append (hash_context,
    124                                                &wdd_pos->h_contract_terms,
    125                                                exec_time,
    126                                                &wdd_pos->coin_pub,
    127                                                &wdd_pos->deposit_value,
    128                                                &wdd_pos->deposit_fee);
    129     if (0 !=
    130         json_array_append_new (
    131           deposits,
    132           GNUNET_JSON_PACK (
    133             GNUNET_JSON_pack_data_auto ("h_contract_terms",
    134                                         &wdd_pos->h_contract_terms),
    135             GNUNET_JSON_pack_data_auto ("coin_pub",
    136                                         &wdd_pos->coin_pub),
    137 
    138             GNUNET_JSON_pack_allow_null (
    139               TALER_JSON_pack_amount ("refund_total",
    140                                       TALER_amount_is_zero (
    141                                         &wdd_pos->refund_total)
    142                                       ? NULL
    143                                       : &wdd_pos->refund_total)),
    144             TALER_JSON_pack_amount ("deposit_value",
    145                                     &wdd_pos->deposit_value),
    146             TALER_JSON_pack_amount ("deposit_fee",
    147                                     &wdd_pos->deposit_fee))))
    148     {
    149       json_decref (deposits);
    150       GNUNET_CRYPTO_hash_context_abort (hash_context);
    151       return TALER_MHD_reply_with_error (connection,
    152                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
    153                                          TALER_EC_GENERIC_JSON_ALLOCATION_FAILURE,
    154                                          "json_array_append_new() failed");
    155     }
    156   }
    157   GNUNET_CRYPTO_hash_context_finish (hash_context,
    158                                      &h_details);
    159   {
    160     enum TALER_ErrorCode ec;
    161 
    162     if (TALER_EC_NONE !=
    163         (ec = TALER_exchange_online_wire_deposit_sign (
    164            &TEH_keys_exchange_sign_,
    165            total,
    166            wire_fee,
    167            merchant_pub,
    168            payto_uri,
    169            &h_details,
    170            &pub,
    171            &sig)))
    172     {
    173       json_decref (deposits);
    174       return TALER_MHD_reply_with_ec (connection,
    175                                       ec,
    176                                       NULL);
    177     }
    178   }
    179 
    180   TALER_full_payto_hash (payto_uri,
    181                          &h_payto);
    182   return TALER_MHD_REPLY_JSON_PACK (
    183     connection,
    184     MHD_HTTP_OK,
    185     TALER_JSON_pack_amount ("total",
    186                             total),
    187     TALER_JSON_pack_amount ("wire_fee",
    188                             wire_fee),
    189     GNUNET_JSON_pack_data_auto ("merchant_pub",
    190                                 merchant_pub),
    191     GNUNET_JSON_pack_data_auto ("h_payto",
    192                                 &h_payto),
    193     /* Not covered by @e exchange_sig: this is a convenience for the
    194        merchant's bookkeeping, not a claim the client needs to prove. */
    195     GNUNET_JSON_pack_allow_null (
    196       TALER_JSON_pack_full_payto ("exchange_payto_uri",
    197                                   exchange_payto_uri)),
    198     GNUNET_JSON_pack_timestamp ("execution_time",
    199                                 exec_time),
    200     GNUNET_JSON_pack_array_steal ("deposits",
    201                                   deposits),
    202     GNUNET_JSON_pack_data_auto ("exchange_sig",
    203                                 &sig),
    204     GNUNET_JSON_pack_data_auto ("exchange_pub",
    205                                 &pub));
    206 }
    207 
    208 
    209 /**
    210  * Closure for #handle_transaction_data.
    211  */
    212 struct WtidTransactionContext
    213 {
    214 
    215   /**
    216    * Identifier of the wire transfer to track.
    217    */
    218   struct TALER_WireTransferIdentifierRawP wtid;
    219 
    220   /**
    221    * Total amount of the wire transfer, as calculated by
    222    * summing up the individual amounts. To be rounded down
    223    * to calculate the real transfer amount at the end.
    224    * Only valid if @e is_valid is #GNUNET_YES.
    225    */
    226   struct TALER_Amount total;
    227 
    228   /**
    229    * Public key of the merchant, only valid if @e is_valid
    230    * is #GNUNET_YES.
    231    */
    232   struct TALER_MerchantPublicKeyP merchant_pub;
    233 
    234   /**
    235    * Wire fees applicable at @e exec_time.
    236    */
    237   struct TALER_WireFeeSet fees;
    238 
    239   /**
    240    * Execution time of the wire transfer
    241    */
    242   struct GNUNET_TIME_Timestamp exec_time;
    243 
    244   /**
    245    * Head of DLL with deposit details for transfers GET response.
    246    */
    247   struct AggregatedDepositDetail *wdd_head;
    248 
    249   /**
    250    * Tail of DLL with deposit details for transfers GET response.
    251    */
    252   struct AggregatedDepositDetail *wdd_tail;
    253 
    254   /**
    255    * Where were the funds wired?
    256    */
    257   struct TALER_FullPayto payto_uri;
    258 
    259   /**
    260    * Which of our own accounts were the funds wired from?
    261    * `full_payto` is NULL if we did not record this, which is
    262    * the case for all transfers aggregated before the exchange
    263    * started to store it.
    264    */
    265   struct TALER_FullPayto exchange_payto_uri;
    266 
    267   /**
    268    * JSON array with details about the individual deposits.
    269    */
    270   json_t *deposits;
    271 
    272   /**
    273    * Initially #GNUNET_NO, if we found no deposits so far.  Set to
    274    * #GNUNET_YES if we got transaction data, and the database replies
    275    * remained consistent with respect to @e merchant_pub and @e h_wire
    276    * (as they should).  Set to #GNUNET_SYSERR if we encountered an
    277    * internal error.
    278    */
    279   enum GNUNET_GenericReturnValue is_valid;
    280 
    281   /**
    282    * Database status of a failed query inside the deposit
    283    * iterator, used to distinguish a (retryable) serialization
    284    * failure from a hard error when @e is_valid is #GNUNET_SYSERR.
    285    */
    286   enum GNUNET_DB_QueryStatus qs;
    287 
    288 };
    289 
    290 
    291 /**
    292  * Callback that totals up the applicable refunds.
    293  *
    294  * @param total a `struct TALER_Amount` where we keep the total
    295  * @param amount_with_fee amount being refunded
    296  */
    297 static enum GNUNET_GenericReturnValue
    298 add_refunds (struct TALER_Amount *total,
    299              const struct TALER_Amount *amount_with_fee)
    300 
    301 {
    302   if (0 >
    303       TALER_amount_add (total,
    304                         total,
    305                         amount_with_fee))
    306   {
    307     GNUNET_break (0);
    308     return GNUNET_SYSERR;
    309   }
    310   return GNUNET_OK;
    311 }
    312 
    313 
    314 /**
    315  * Function called with the results of the lookup of the individual deposits
    316  * that were aggregated for the given wire transfer.
    317  *
    318  * @param ctx our context for transmission
    319  * @param rowid which row in the DB is the information from (for diagnostics), ignored
    320  * @param merchant_pub public key of the merchant (should be same for all callbacks with the same @e cls)
    321  * @param account_payto_uri where the funds were sent
    322  * @param h_payto hash over @a account_payto_uri as it is in the DB
    323  * @param exchange_payto_uri our own account the funds were sent from,
    324  *        `full_payto` NULL if we did not record it
    325  * @param exec_time execution time of the wire transfer (should be same for all callbacks with the same @e cls)
    326  * @param h_contract_terms which proposal was this payment about
    327  * @param denom_pub denomination public key of the @a coin_pub (ignored)
    328  * @param coin_pub which public key was this payment about
    329  * @param deposit_value amount contributed by this coin in total (including fee)
    330  * @param deposit_fee deposit fee charged by exchange for this coin
    331  */
    332 static void
    333 handle_deposit_data (
    334   struct WtidTransactionContext *ctx,
    335   uint64_t rowid,
    336   const struct TALER_MerchantPublicKeyP *merchant_pub,
    337   const struct TALER_FullPayto account_payto_uri,
    338   const struct TALER_FullPaytoHashP *h_payto,
    339   const struct TALER_FullPayto exchange_payto_uri,
    340   struct GNUNET_TIME_Timestamp exec_time,
    341   const struct TALER_PrivateContractHashP *h_contract_terms,
    342   const struct TALER_DenominationPublicKey *denom_pub,
    343   const struct TALER_CoinSpendPublicKeyP *coin_pub,
    344   const struct TALER_Amount *deposit_value,
    345   const struct TALER_Amount *deposit_fee)
    346 {
    347   struct TALER_Amount total_refunds;
    348   struct TALER_Amount dval;
    349   struct TALER_Amount dfee;
    350   enum GNUNET_DB_QueryStatus qs;
    351 
    352   (void) rowid;
    353   (void) denom_pub;
    354   (void) h_payto;
    355   if (GNUNET_SYSERR == ctx->is_valid)
    356     return;
    357   GNUNET_assert (GNUNET_OK ==
    358                  TALER_amount_set_zero (deposit_value->currency,
    359                                         &total_refunds));
    360   qs = TALER_EXCHANGEDB_iterate_refunds_by_coin (TEH_pg,
    361                                                  coin_pub,
    362                                                  merchant_pub,
    363                                                  h_contract_terms,
    364                                                  &add_refunds,
    365                                                  &total_refunds);
    366   if (qs < 0)
    367   {
    368     GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    369     ctx->qs = qs;
    370     ctx->is_valid = GNUNET_SYSERR;
    371     return;
    372   }
    373   if (1 ==
    374       TALER_amount_cmp (&total_refunds,
    375                         deposit_value))
    376   {
    377     /* Refunds exceeded total deposit? not OK! */
    378     GNUNET_break (0);
    379     ctx->is_valid = GNUNET_SYSERR;
    380     return;
    381   }
    382   if (0 ==
    383       TALER_amount_cmp (&total_refunds,
    384                         deposit_value))
    385   {
    386     /* total_refunds == deposit_value;
    387        in this case, the total contributed to the
    388        wire transfer is zero (as are fees) */
    389     GNUNET_assert (GNUNET_OK ==
    390                    TALER_amount_set_zero (deposit_value->currency,
    391                                           &dval));
    392     GNUNET_assert (GNUNET_OK ==
    393                    TALER_amount_set_zero (deposit_value->currency,
    394                                           &dfee));
    395 
    396   }
    397   else
    398   {
    399     /* Compute deposit value by subtracting refunds */
    400     GNUNET_assert (0 <
    401                    TALER_amount_subtract (&dval,
    402                                           deposit_value,
    403                                           &total_refunds));
    404     if (-1 ==
    405         TALER_amount_cmp (&dval,
    406                           deposit_fee))
    407     {
    408       /* dval < deposit_fee, so after refunds less than
    409          the deposit fee remains; reduce deposit fee to
    410          the remaining value of the coin */
    411       dfee = dval;
    412     }
    413     else
    414     {
    415       /* Partial refund, deposit fee remains */
    416       dfee = *deposit_fee;
    417     }
    418   }
    419 
    420   if (GNUNET_NO == ctx->is_valid)
    421   {
    422     /* First one we encounter, setup general information in 'ctx' */
    423     ctx->merchant_pub = *merchant_pub;
    424     ctx->payto_uri.full_payto = GNUNET_strdup (account_payto_uri.full_payto);
    425     if (NULL != exchange_payto_uri.full_payto)
    426       ctx->exchange_payto_uri.full_payto
    427         = GNUNET_strdup (exchange_payto_uri.full_payto);
    428     ctx->exec_time = exec_time;
    429     ctx->is_valid = GNUNET_YES;
    430     if (0 >
    431         TALER_amount_subtract (&ctx->total,
    432                                &dval,
    433                                &dfee))
    434     {
    435       GNUNET_break (0);
    436       ctx->is_valid = GNUNET_SYSERR;
    437       return;
    438     }
    439   }
    440   else
    441   {
    442     struct TALER_Amount delta;
    443 
    444     /* Subsequent data, check general information matches that in 'ctx';
    445        (it should, otherwise the deposits should not have been aggregated) */
    446     if ( (0 != GNUNET_memcmp (&ctx->merchant_pub,
    447                               merchant_pub)) ||
    448          (0 != TALER_full_payto_cmp (account_payto_uri,
    449                                      ctx->payto_uri)) )
    450     {
    451       GNUNET_break (0);
    452       ctx->is_valid = GNUNET_SYSERR;
    453       return;
    454     }
    455     if (0 >
    456         TALER_amount_subtract (&delta,
    457                                &dval,
    458                                &dfee))
    459     {
    460       GNUNET_break (0);
    461       ctx->is_valid = GNUNET_SYSERR;
    462       return;
    463     }
    464     if (0 >
    465         TALER_amount_add (&ctx->total,
    466                           &ctx->total,
    467                           &delta))
    468     {
    469       GNUNET_break (0);
    470       ctx->is_valid = GNUNET_SYSERR;
    471       return;
    472     }
    473   }
    474 
    475   {
    476     struct AggregatedDepositDetail *wdd;
    477 
    478     wdd = GNUNET_new (struct AggregatedDepositDetail);
    479     wdd->deposit_value = dval;
    480     wdd->deposit_fee = dfee;
    481     wdd->refund_total = total_refunds;
    482     wdd->h_contract_terms = *h_contract_terms;
    483     wdd->coin_pub = *coin_pub;
    484     GNUNET_CONTAINER_DLL_insert (ctx->wdd_head,
    485                                  ctx->wdd_tail,
    486                                  wdd);
    487   }
    488 }
    489 
    490 
    491 /**
    492  * Free data structure reachable from @a ctx, but not @a ctx itself.
    493  *
    494  * @param ctx context to free
    495  */
    496 static void
    497 free_ctx (struct WtidTransactionContext *ctx)
    498 {
    499   struct AggregatedDepositDetail *wdd;
    500 
    501   while (NULL != (wdd = ctx->wdd_head))
    502   {
    503     GNUNET_CONTAINER_DLL_remove (ctx->wdd_head,
    504                                  ctx->wdd_tail,
    505                                  wdd);
    506     GNUNET_free (wdd);
    507   }
    508   GNUNET_free (ctx->payto_uri.full_payto);
    509   GNUNET_free (ctx->exchange_payto_uri.full_payto);
    510 }
    511 
    512 
    513 /**
    514  * Execute a "/transfers" GET operation.  Returns the deposit details of the
    515  * deposits that were aggregated to create the given wire transfer.
    516  *
    517  * If it returns a non-error code, the transaction logic MUST
    518  * NOT queue a MHD response.  IF it returns an hard error, the
    519  * transaction logic MUST queue a MHD response and set @a mhd_ret.  IF
    520  * it returns the soft error code, the function MAY be called again to
    521  * retry and MUST not queue a MHD response.
    522  *
    523  * @param cls closure
    524  * @param connection MHD request which triggered the transaction
    525  * @param[out] mhd_ret set to MHD response status for @a connection,
    526  *             if transaction failed (!)
    527  * @return transaction status
    528  */
    529 static enum GNUNET_DB_QueryStatus
    530 get_transfer_deposits (void *cls,
    531                        struct MHD_Connection *connection,
    532                        enum MHD_Result *mhd_ret)
    533 {
    534   struct WtidTransactionContext *ctx = cls;
    535   enum GNUNET_DB_QueryStatus qs;
    536   struct GNUNET_TIME_Timestamp wire_fee_start_date;
    537   struct GNUNET_TIME_Timestamp wire_fee_end_date;
    538   struct TALER_MasterSignatureP wire_fee_master_sig;
    539 
    540   /* resetting to NULL/0 in case transaction was repeated after
    541      serialization failure */
    542   free_ctx (ctx);
    543   ctx->is_valid = GNUNET_NO;
    544   ctx->qs = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
    545   qs = TALER_EXCHANGEDB_iterate_wire_transfers (TEH_pg,
    546                                                 &ctx->wtid,
    547                                                 &handle_deposit_data,
    548                                                 ctx);
    549   if (0 > qs)
    550   {
    551     if (GNUNET_DB_STATUS_HARD_ERROR == qs)
    552     {
    553       GNUNET_break (0);
    554       *mhd_ret = TALER_MHD_reply_with_error (
    555         connection,
    556         MHD_HTTP_INTERNAL_SERVER_ERROR,
    557         TALER_EC_GENERIC_DB_FETCH_FAILED,
    558         "wire transfer");
    559     }
    560     return qs;
    561   }
    562   if (GNUNET_SYSERR == ctx->is_valid)
    563   {
    564     if (GNUNET_DB_STATUS_SOFT_ERROR == ctx->qs)
    565       return GNUNET_DB_STATUS_SOFT_ERROR; /* serialization issue, retry */
    566     GNUNET_break (0);
    567     *mhd_ret = TALER_MHD_reply_with_error (
    568       connection,
    569       MHD_HTTP_INTERNAL_SERVER_ERROR,
    570       TALER_EC_GENERIC_DB_INVARIANT_FAILURE,
    571       "wire history malformed");
    572     return GNUNET_DB_STATUS_HARD_ERROR;
    573   }
    574   if (GNUNET_NO == ctx->is_valid)
    575   {
    576     *mhd_ret = TALER_MHD_reply_with_error (
    577       connection,
    578       MHD_HTTP_NOT_FOUND,
    579       TALER_EC_EXCHANGE_TRANSFERS_GET_WTID_NOT_FOUND,
    580       NULL);
    581     return GNUNET_DB_STATUS_HARD_ERROR;
    582   }
    583   {
    584     char *wire_method;
    585     uint64_t rowid;
    586 
    587     wire_method = TALER_payto_get_method (ctx->payto_uri.full_payto);
    588     if (NULL == wire_method)
    589     {
    590       GNUNET_break (0);
    591       *mhd_ret = TALER_MHD_reply_with_error (
    592         connection,
    593         MHD_HTTP_INTERNAL_SERVER_ERROR,
    594         TALER_EC_GENERIC_DB_INVARIANT_FAILURE,
    595         "payto:// without wire method encountered");
    596       return GNUNET_DB_STATUS_HARD_ERROR;
    597     }
    598     qs = TALER_EXCHANGEDB_get_wire_fee (TEH_pg,
    599                                         wire_method,
    600                                         ctx->exec_time,
    601                                         &rowid,
    602                                         &wire_fee_start_date,
    603                                         &wire_fee_end_date,
    604                                         &ctx->fees,
    605                                         &wire_fee_master_sig);
    606     GNUNET_free (wire_method);
    607   }
    608   if (0 >= qs)
    609   {
    610     if ( (GNUNET_DB_STATUS_HARD_ERROR == qs) ||
    611          (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) )
    612     {
    613       GNUNET_break (0);
    614       *mhd_ret = TALER_MHD_reply_with_error (
    615         connection,
    616         MHD_HTTP_INTERNAL_SERVER_ERROR,
    617         TALER_EC_EXCHANGE_TRANSFERS_GET_WIRE_FEE_NOT_FOUND,
    618         NULL);
    619     }
    620     return qs;
    621   }
    622   if (0 >
    623       TALER_amount_subtract (&ctx->total,
    624                              &ctx->total,
    625                              &ctx->fees.wire))
    626   {
    627     GNUNET_break (0);
    628     *mhd_ret = TALER_MHD_reply_with_error (
    629       connection,
    630       MHD_HTTP_INTERNAL_SERVER_ERROR,
    631       TALER_EC_EXCHANGE_TRANSFERS_GET_WIRE_FEE_INCONSISTENT,
    632       NULL);
    633     return GNUNET_DB_STATUS_HARD_ERROR;
    634   }
    635   return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
    636 }
    637 
    638 
    639 enum MHD_Result
    640 TEH_handler_transfers_get (struct TEH_RequestContext *rc,
    641                            const char *const args[1])
    642 {
    643   struct WtidTransactionContext ctx;
    644   enum MHD_Result mhd_ret;
    645 
    646   memset (&ctx,
    647           0,
    648           sizeof (ctx));
    649   if (GNUNET_OK !=
    650       GNUNET_STRINGS_string_to_data (args[0],
    651                                      strlen (args[0]),
    652                                      &ctx.wtid,
    653                                      sizeof (ctx.wtid)))
    654   {
    655     GNUNET_break_op (0);
    656     return TALER_MHD_reply_with_error (
    657       rc->connection,
    658       MHD_HTTP_BAD_REQUEST,
    659       TALER_EC_EXCHANGE_TRANSFERS_GET_WTID_MALFORMED,
    660       args[0]);
    661   }
    662   if (GNUNET_OK !=
    663       TEH_DB_run_transaction (rc->connection,
    664                               "run transfers GET",
    665                               TEH_MT_REQUEST_OTHER,
    666                               &mhd_ret,
    667                               &get_transfer_deposits,
    668                               &ctx))
    669   {
    670     free_ctx (&ctx);
    671     return mhd_ret;
    672   }
    673   mhd_ret = reply_transfer_details (rc->connection,
    674                                     &ctx.total,
    675                                     &ctx.merchant_pub,
    676                                     ctx.payto_uri,
    677                                     ctx.exchange_payto_uri,
    678                                     &ctx.fees.wire,
    679                                     ctx.exec_time,
    680                                     ctx.wdd_head);
    681   free_ctx (&ctx);
    682   return mhd_ret;
    683 }
    684 
    685 
    686 /* end of taler-exchange-httpd_get-transfers-WTID.c */