LCOV - code coverage report
Current view: top level - backend - taler-merchant-httpd_private-post-transfers.c (source / functions) Hit Total Coverage
Test: GNU Taler merchant coverage report Lines: 230 424 54.2 %
Date: 2021-08-30 06:54:17 Functions: 15 15 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   (C) 2014-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 General Public License for more details.
      12             : 
      13             :   You should have received a copy of the GNU General Public License along with
      14             :   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
      15             : */
      16             : /**
      17             :  * @file taler-merchant-httpd_private-post-transfers.c
      18             :  * @brief implement API for registering wire transfers
      19             :  * @author Marcello Stanisci
      20             :  * @author Christian Grothoff
      21             :  */
      22             : #include "platform.h"
      23             : #include <jansson.h>
      24             : #include <taler/taler_signatures.h>
      25             : #include <taler/taler_json_lib.h>
      26             : #include "taler-merchant-httpd_auditors.h"
      27             : #include "taler-merchant-httpd_exchanges.h"
      28             : #include "taler-merchant-httpd_helper.h"
      29             : #include "taler-merchant-httpd_private-post-transfers.h"
      30             : 
      31             : 
      32             : /**
      33             :  * How long to wait before giving up processing with the exchange?
      34             :  */
      35             : #define TRANSFER_GENERIC_TIMEOUT (GNUNET_TIME_relative_multiply ( \
      36             :                                     GNUNET_TIME_UNIT_SECONDS, \
      37             :                                     15))
      38             : 
      39             : /**
      40             :  * How often do we retry the simple INSERT database transaction?
      41             :  */
      42             : #define MAX_RETRIES 3
      43             : 
      44             : /**
      45             :  * Context used for handing POST /private/transfers requests.
      46             :  */
      47             : struct PostTransfersContext
      48             : {
      49             : 
      50             :   /**
      51             :    * Kept in a DLL.
      52             :    */
      53             :   struct PostTransfersContext *next;
      54             : 
      55             :   /**
      56             :    * Kept in a DLL.
      57             :    */
      58             :   struct PostTransfersContext *prev;
      59             : 
      60             :   /**
      61             :    * Argument for the /wire/transfers request.
      62             :    */
      63             :   struct TALER_WireTransferIdentifierRawP wtid;
      64             : 
      65             :   /**
      66             :    * Amount of the wire transfer.
      67             :    */
      68             :   struct TALER_Amount amount;
      69             : 
      70             :   /**
      71             :    * URL of the exchange.
      72             :    */
      73             :   const char *exchange_url;
      74             : 
      75             :   /**
      76             :    * payto:// URI used for the transfer.
      77             :    */
      78             :   const char *payto_uri;
      79             : 
      80             :   /**
      81             :    * Master public key of the exchange at @e exchange_url.
      82             :    */
      83             :   struct TALER_MasterPublicKeyP master_pub;
      84             : 
      85             :   /**
      86             :    * Handle for the /wire/transfers request.
      87             :    */
      88             :   struct TALER_EXCHANGE_TransfersGetHandle *wdh;
      89             : 
      90             :   /**
      91             :    * For which merchant instance is this tracking request?
      92             :    */
      93             :   struct TMH_HandlerContext *hc;
      94             : 
      95             :   /**
      96             :    * HTTP connection we are handling.
      97             :    */
      98             :   struct MHD_Connection *connection;
      99             : 
     100             :   /**
     101             :    * Response to return upon resume.
     102             :    */
     103             :   struct MHD_Response *response;
     104             : 
     105             :   /**
     106             :    * Handle for operation to lookup /keys (and auditors) from
     107             :    * the exchange used for this transaction; NULL if no operation is
     108             :    * pending.
     109             :    */
     110             :   struct TMH_EXCHANGES_FindOperation *fo;
     111             : 
     112             :   /**
     113             :    * Task run on timeout.
     114             :    */
     115             :   struct GNUNET_SCHEDULER_Task *timeout_task;
     116             : 
     117             :   /**
     118             :    * Pointer to the detail that we are currently
     119             :    * checking in #check_transfer().
     120             :    */
     121             :   const struct TALER_TrackTransferDetails *current_detail;
     122             : 
     123             :   /**
     124             :    * Which transaction detail are we currently looking at?
     125             :    */
     126             :   unsigned int current_offset;
     127             : 
     128             :   /**
     129             :    * Response code to return.
     130             :    */
     131             :   unsigned int response_code;
     132             : 
     133             :   /**
     134             :    * #GNUNET_NO if we did not find a matching coin.
     135             :    * #GNUNET_SYSERR if we found a matching coin, but the amounts do not match.
     136             :    * #GNUNET_OK if we did find a matching coin.
     137             :    */
     138             :   int check_transfer_result;
     139             : 
     140             :   /**
     141             :    * Should we retry the transaction due to a serialization error?
     142             :    */
     143             :   bool soft_retry;
     144             : 
     145             :   /**
     146             :    * Did we just download the exchange reply?
     147             :    */
     148             :   bool downloaded;
     149             : 
     150             :   /**
     151             :    * Are we currently suspended?
     152             :    */
     153             :   bool suspended;
     154             : };
     155             : 
     156             : 
     157             : /**
     158             :  * Head of list of suspended requests.
     159             :  */
     160             : static struct PostTransfersContext *ptc_head;
     161             : 
     162             : /**
     163             :  * Tail of list of suspended requests.
     164             :  */
     165             : static struct PostTransfersContext *ptc_tail;
     166             : 
     167             : 
     168             : void
     169          16 : TMH_force_post_transfers_resume ()
     170             : {
     171             :   struct PostTransfersContext *ptc;
     172             : 
     173          16 :   while (NULL != (ptc = ptc_head))
     174             :   {
     175           0 :     GNUNET_CONTAINER_DLL_remove (ptc_head,
     176             :                                  ptc_tail,
     177             :                                  ptc);
     178           0 :     ptc->suspended = false;
     179           0 :     MHD_resume_connection (ptc->connection);
     180           0 :     if (NULL != ptc->timeout_task)
     181             :     {
     182           0 :       GNUNET_SCHEDULER_cancel (ptc->timeout_task);
     183           0 :       ptc->timeout_task = NULL;
     184             :     }
     185             :   }
     186          16 : }
     187             : 
     188             : 
     189             : /**
     190             :  * Resume the given /track/transfer operation and send the given response.
     191             :  * Stores the response in the @a ptc and signals MHD to resume
     192             :  * the connection.  Also ensures MHD runs immediately.
     193             :  *
     194             :  * @param ptc transfer tracking context
     195             :  * @param response_code response code to use
     196             :  * @param response response data to send back
     197             :  */
     198             : static void
     199           2 : resume_transfer_with_response (struct PostTransfersContext *ptc,
     200             :                                unsigned int response_code,
     201             :                                struct MHD_Response *response)
     202             : {
     203           2 :   ptc->response_code = response_code;
     204           2 :   ptc->response = response;
     205           2 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     206             :               "Resuming POST /transfers handling as exchange interaction is done (%u)\n",
     207             :               response_code);
     208           2 :   if (NULL != ptc->timeout_task)
     209             :   {
     210           1 :     GNUNET_SCHEDULER_cancel (ptc->timeout_task);
     211           1 :     ptc->timeout_task = NULL;
     212             :   }
     213           2 :   GNUNET_CONTAINER_DLL_remove (ptc_head,
     214             :                                ptc_tail,
     215             :                                ptc);
     216           2 :   ptc->suspended = false;
     217           2 :   MHD_resume_connection (ptc->connection);
     218           2 :   TALER_MHD_daemon_trigger ();   /* we resumed, kick MHD */
     219           2 : }
     220             : 
     221             : 
     222             : /**
     223             :  * Resume the given POST /transfers operation with an error.
     224             :  *
     225             :  * @param ptc transfer tracking context
     226             :  * @param response_code response code to use
     227             :  * @param ec error code to use
     228             :  * @param hint hint text to provide
     229             :  */
     230             : static void
     231           1 : resume_transfer_with_error (struct PostTransfersContext *ptc,
     232             :                             unsigned int response_code,
     233             :                             enum TALER_ErrorCode ec,
     234             :                             const char *hint)
     235             : {
     236           1 :   resume_transfer_with_response (ptc,
     237             :                                  response_code,
     238             :                                  TALER_MHD_make_error (ec,
     239             :                                                        hint));
     240           1 : }
     241             : 
     242             : 
     243             : /**
     244             :  * Custom cleanup routine for a `struct PostTransfersContext`.
     245             :  *
     246             :  * @param cls the `struct PostTransfersContext` to clean up.
     247             :  */
     248             : static void
     249           2 : transfer_cleanup (void *cls)
     250             : {
     251           2 :   struct PostTransfersContext *ptc = cls;
     252             : 
     253           2 :   if (NULL != ptc->fo)
     254             :   {
     255           0 :     TMH_EXCHANGES_find_exchange_cancel (ptc->fo);
     256           0 :     ptc->fo = NULL;
     257             :   }
     258           2 :   if (NULL != ptc->timeout_task)
     259             :   {
     260           0 :     GNUNET_SCHEDULER_cancel (ptc->timeout_task);
     261           0 :     ptc->timeout_task = NULL;
     262             :   }
     263           2 :   if (NULL != ptc->wdh)
     264             :   {
     265           0 :     TALER_EXCHANGE_transfers_get_cancel (ptc->wdh);
     266           0 :     ptc->wdh = NULL;
     267             :   }
     268           2 :   if (NULL != ptc->response)
     269             :   {
     270           1 :     MHD_destroy_response (ptc->response);
     271           1 :     ptc->response = NULL;
     272             :   }
     273           2 :   GNUNET_free (ptc);
     274           2 : }
     275             : 
     276             : 
     277             : /**
     278             :  * This function checks that the information about the coin which
     279             :  * was paid back by _this_ wire transfer matches what _we_ (the merchant)
     280             :  * knew about this coin.
     281             :  *
     282             :  * @param cls closure with our `struct PostTransfersContext *`
     283             :  * @param exchange_url URL of the exchange that issued @a coin_pub
     284             :  * @param amount_with_fee amount the exchange will transfer for this coin
     285             :  * @param deposit_fee fee the exchange will charge for this coin
     286             :  * @param refund_fee fee the exchange will charge for refunding this coin
     287             :  * @param wire_fee paid wire fee
     288             :  * @param h_wire hash of merchant's wire details
     289             :  * @param deposit_timestamp when did the exchange receive the deposit
     290             :  * @param refund_deadline until when are refunds allowed
     291             :  * @param exchange_sig signature by the exchange
     292             :  * @param exchange_pub exchange signing key used for @a exchange_sig
     293             :  */
     294             : static void
     295           1 : check_transfer (void *cls,
     296             :                 const char *exchange_url,
     297             :                 const struct TALER_Amount *amount_with_fee,
     298             :                 const struct TALER_Amount *deposit_fee,
     299             :                 const struct TALER_Amount *refund_fee,
     300             :                 const struct TALER_Amount *wire_fee,
     301             :                 const struct GNUNET_HashCode *h_wire,
     302             :                 struct GNUNET_TIME_Absolute deposit_timestamp,
     303             :                 struct GNUNET_TIME_Absolute refund_deadline,
     304             :                 const struct TALER_ExchangeSignatureP *exchange_sig,
     305             :                 const struct TALER_ExchangePublicKeyP *exchange_pub)
     306             : {
     307           1 :   struct PostTransfersContext *ptc = cls;
     308           1 :   const struct TALER_TrackTransferDetails *ttd = ptc->current_detail;
     309             : 
     310           1 :   if (GNUNET_SYSERR == ptc->check_transfer_result)
     311           0 :     return;   /* already had a serious issue; odd that we're called more than once as well... */
     312           1 :   if ( (0 != TALER_amount_cmp (amount_with_fee,
     313           1 :                                &ttd->coin_value)) ||
     314           1 :        (0 != TALER_amount_cmp (deposit_fee,
     315             :                                &ttd->coin_fee)) )
     316             :   {
     317             :     /* Disagreement between the exchange and us about how much this
     318             :        coin is worth! */
     319           0 :     GNUNET_break_op (0);
     320           0 :     ptc->check_transfer_result = GNUNET_SYSERR;
     321             :     /* Build the `TrackTransferConflictDetails` */
     322           0 :     ptc->response_code = MHD_HTTP_ACCEPTED;
     323             :     ptc->response
     324           0 :       = TALER_MHD_MAKE_JSON_PACK (
     325             :           TALER_JSON_pack_ec (
     326             :             TALER_EC_MERCHANT_PRIVATE_POST_TRANSFERS_CONFLICTING_REPORTS),
     327             :           GNUNET_JSON_pack_string ("exchange_url",
     328             :                                    exchange_url),
     329             :           GNUNET_JSON_pack_time_abs ("deposit_timestamp",
     330             :                                      deposit_timestamp),
     331             :           GNUNET_JSON_pack_time_abs ("refund_deadline",
     332             :                                      refund_deadline),
     333             :           GNUNET_JSON_pack_uint64 ("conflict_offset",
     334             :                                    ptc->current_offset),
     335             :           GNUNET_JSON_pack_data_auto ("coin_pub",
     336             :                                       &ttd->coin_pub),
     337             :           GNUNET_JSON_pack_data_auto ("h_wire",
     338             :                                       h_wire),
     339             :           GNUNET_JSON_pack_data_auto ("deposit_exchange_sig",
     340             :                                       exchange_sig),
     341             :           GNUNET_JSON_pack_data_auto ("deposit_exchange_pub",
     342             :                                       exchange_pub),
     343             :           GNUNET_JSON_pack_data_auto ("h_contract_terms",
     344             :                                       &ttd->h_contract_terms),
     345             :           TALER_JSON_pack_amount ("amount_with_fee",
     346             :                                   amount_with_fee),
     347             :           TALER_JSON_pack_amount ("coin_value",
     348             :                                   &ttd->coin_value),
     349             :           TALER_JSON_pack_amount ("coin_fee",
     350             :                                   &ttd->coin_fee),
     351             :           TALER_JSON_pack_amount ("deposit_fee",
     352             :                                   deposit_fee));
     353           0 :     return;
     354             :   }
     355           1 :   ptc->check_transfer_result = GNUNET_OK;
     356             : }
     357             : 
     358             : 
     359             : /**
     360             :  * Check that the given @a wire_fee is what the @a exchange_pub should charge
     361             :  * at the @a execution_time.  If the fee is correct (according to our
     362             :  * database), return #GNUNET_OK.  If we do not have the fee structure in our
     363             :  * DB, we just accept it and return #GNUNET_NO; if we have proof that the fee
     364             :  * is bogus, we respond with the proof to the client and return
     365             :  * #GNUNET_SYSERR.
     366             :  *
     367             :  * @param ptc context of the transfer to respond to
     368             :  * @param execution_time time of the wire transfer
     369             :  * @param wire_fee fee claimed by the exchange
     370             :  * @return #GNUNET_SYSERR if we returned hard proof of
     371             :  *   missbehavior from the exchange to the client
     372             :  */
     373             : static int
     374           1 : check_wire_fee (struct PostTransfersContext *ptc,
     375             :                 struct GNUNET_TIME_Absolute execution_time,
     376             :                 const struct TALER_Amount *wire_fee)
     377             : {
     378             :   struct TALER_Amount expected_fee;
     379             :   struct TALER_Amount closing_fee;
     380             :   struct TALER_MasterSignatureP master_sig;
     381             :   struct GNUNET_TIME_Absolute start_date;
     382             :   struct GNUNET_TIME_Absolute end_date;
     383             :   enum GNUNET_DB_QueryStatus qs;
     384             :   char *wire_method;
     385             : 
     386           1 :   wire_method = TALER_payto_get_method (ptc->payto_uri);
     387           1 :   qs = TMH_db->lookup_wire_fee (TMH_db->cls,
     388           1 :                                 &ptc->master_pub,
     389             :                                 wire_method,
     390             :                                 execution_time,
     391             :                                 &expected_fee,
     392             :                                 &closing_fee,
     393             :                                 &start_date,
     394             :                                 &end_date,
     395             :                                 &master_sig);
     396           1 :   switch (qs)
     397             :   {
     398           0 :   case GNUNET_DB_STATUS_HARD_ERROR:
     399           0 :     GNUNET_break (0);
     400           0 :     ptc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
     401           0 :     ptc->response = TALER_MHD_make_error (TALER_EC_GENERIC_DB_FETCH_FAILED,
     402             :                                           "lookup_wire_fee");
     403           0 :     return GNUNET_SYSERR;
     404           0 :   case GNUNET_DB_STATUS_SOFT_ERROR:
     405           0 :     ptc->soft_retry = true;
     406           0 :     return GNUNET_NO;
     407           0 :   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
     408           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     409             :                 "Failed to find wire fee for `%s' and method `%s' at %s in DB, accepting blindly that the fee is %s\n",
     410             :                 TALER_B2S (&ptc->master_pub),
     411             :                 wire_method,
     412             :                 GNUNET_STRINGS_absolute_time_to_string (execution_time),
     413             :                 TALER_amount2s (wire_fee));
     414           0 :     GNUNET_free (wire_method);
     415           0 :     return GNUNET_NO;
     416           1 :   case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
     417           1 :     break;
     418             :   }
     419           1 :   if (0 <= TALER_amount_cmp (&expected_fee,
     420             :                              wire_fee))
     421             :   {
     422           1 :     GNUNET_free (wire_method);
     423           1 :     return GNUNET_OK;   /* expected_fee >= wire_fee */
     424             :   }
     425             :   /* Wire fee check failed, export proof to client */
     426           0 :   ptc->response_code = MHD_HTTP_ACCEPTED;
     427           0 :   ptc->response =
     428           0 :     TALER_MHD_MAKE_JSON_PACK (
     429             :       TALER_JSON_pack_ec (
     430             :         TALER_EC_MERCHANT_PRIVATE_POST_TRANSFERS_BAD_WIRE_FEE),
     431             :       TALER_JSON_pack_amount ("wire_fee",
     432             :                               wire_fee),
     433             :       GNUNET_JSON_pack_time_abs ("execution_time",
     434             :                                  execution_time),
     435             :       TALER_JSON_pack_amount ("expected_wire_fee",
     436             :                               &expected_fee),
     437             :       TALER_JSON_pack_amount ("expected_closing_fee",
     438             :                               &closing_fee),
     439             :       GNUNET_JSON_pack_time_abs ("start_date",
     440             :                                  start_date),
     441             :       GNUNET_JSON_pack_time_abs ("end_date",
     442             :                                  end_date),
     443             :       GNUNET_JSON_pack_data_auto ("master_sig",
     444             :                                   &master_sig),
     445             :       GNUNET_JSON_pack_data_auto ("master_pub",
     446             :                                   &ptc->master_pub));
     447           0 :   GNUNET_free (wire_method);
     448           0 :   return GNUNET_SYSERR;
     449             : }
     450             : 
     451             : 
     452             : /**
     453             :  * Function called with detailed wire transfer data, including all
     454             :  * of the coin transactions that were combined into the wire transfer.
     455             :  *
     456             :  * @param cls closure
     457             :  * @param hr HTTP response details
     458             :  * @param td transfer data
     459             :  */
     460             : static void
     461           1 : wire_transfer_cb (void *cls,
     462             :                   const struct TALER_EXCHANGE_HttpResponse *hr,
     463             :                   const struct TALER_EXCHANGE_TransferData *td)
     464             : {
     465           1 :   struct PostTransfersContext *ptc = cls;
     466           1 :   const char *instance_id = ptc->hc->instance->settings.id;
     467             :   enum GNUNET_DB_QueryStatus qs;
     468             : 
     469           1 :   ptc->wdh = NULL;
     470           1 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     471             :               "Got response code %u from exchange for GET /transfers/$WTID\n",
     472             :               hr->http_status);
     473           1 :   switch (hr->http_status)
     474             :   {
     475           1 :   case MHD_HTTP_OK:
     476           1 :     break;
     477           0 :   case MHD_HTTP_NOT_FOUND:
     478           0 :     resume_transfer_with_response (
     479             :       ptc,
     480             :       MHD_HTTP_BAD_GATEWAY,
     481           0 :       TALER_MHD_MAKE_JSON_PACK (
     482             :         TALER_JSON_pack_ec (
     483             :           TALER_EC_MERCHANT_PRIVATE_POST_TRANSFERS_EXCHANGE_UNKNOWN),
     484             :         TMH_pack_exchange_reply (hr)));
     485           0 :     return;
     486           0 :   default:
     487           0 :     resume_transfer_with_response (
     488             :       ptc,
     489             :       MHD_HTTP_BAD_GATEWAY,
     490           0 :       TALER_MHD_MAKE_JSON_PACK (
     491             :         TALER_JSON_pack_ec (
     492             :           TALER_EC_MERCHANT_GENERIC_EXCHANGE_UNEXPECTED_STATUS),
     493             :         TMH_pack_exchange_reply (hr)));
     494           0 :     return;
     495             :   }
     496           1 :   TMH_db->preflight (TMH_db->cls);
     497             :   /* Ok, exchange answer is acceptable, store it */
     498           1 :   qs = TMH_db->insert_transfer_details (TMH_db->cls,
     499             :                                         instance_id,
     500             :                                         ptc->exchange_url,
     501             :                                         ptc->payto_uri,
     502           1 :                                         &ptc->wtid,
     503             :                                         td);
     504           1 :   if (0 > qs)
     505             :   {
     506             :     /* Always report on DB error as well to enable diagnostics */
     507           0 :     GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs);
     508           0 :     resume_transfer_with_error (
     509             :       ptc,
     510             :       MHD_HTTP_INTERNAL_SERVER_ERROR,
     511             :       (GNUNET_DB_STATUS_HARD_ERROR == qs)
     512             :       ? TALER_EC_GENERIC_DB_COMMIT_FAILED
     513             :       : TALER_EC_GENERIC_DB_SOFT_FAILURE,
     514             :       NULL);
     515           0 :     return;
     516             :   }
     517           1 :   if (0 == qs)
     518             :   {
     519           0 :     GNUNET_break (0);
     520           0 :     resume_transfer_with_error (
     521             :       ptc,
     522             :       MHD_HTTP_INTERNAL_SERVER_ERROR,
     523             :       TALER_EC_GENERIC_DB_STORE_FAILED,
     524             :       "insert-transfer-details");
     525           0 :     return;
     526             :   }
     527           1 :   if (0 !=
     528           1 :       TALER_amount_cmp (&td->total_amount,
     529           1 :                         &ptc->amount))
     530             :   {
     531           0 :     resume_transfer_with_error (
     532             :       ptc,
     533             :       MHD_HTTP_CONFLICT,
     534             :       TALER_EC_MERCHANT_PRIVATE_POST_TRANSFERS_CONFLICTING_TRANSFERS,
     535             :       NULL);
     536           0 :     return;
     537             :   }
     538           1 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     539             :               "Transfer details inserted, resuming request...\n");
     540             :   /* resume processing, main function will build the response */
     541           1 :   resume_transfer_with_response (ptc,
     542             :                                  0,
     543             :                                  NULL);
     544             : }
     545             : 
     546             : 
     547             : /**
     548             :  * Function called with the result of our exchange lookup.
     549             :  *
     550             :  * @param cls the `struct PostTransfersContext`
     551             :  * @param hr HTTP response details
     552             :  * @param eh NULL if exchange was not found to be acceptable
     553             :  * @param payto_uri payto://-URI of the exchange
     554             :  * @param wire_fee NULL (we did not specify a wire method)
     555             :  * @param exchange_trusted #GNUNET_YES if this exchange is trusted by config
     556             :  */
     557             : static void
     558           1 : process_transfer_with_exchange (void *cls,
     559             :                                 const struct TALER_EXCHANGE_HttpResponse *hr,
     560             :                                 struct TALER_EXCHANGE_Handle *eh,
     561             :                                 const char *payto_uri,
     562             :                                 const struct TALER_Amount *wire_fee,
     563             :                                 bool exchange_trusted)
     564             : {
     565           1 :   struct PostTransfersContext *ptc = cls;
     566             : 
     567             :   (void) payto_uri;
     568             :   (void) exchange_trusted;
     569           1 :   ptc->fo = NULL;
     570           1 :   if (NULL == hr)
     571             :   {
     572           0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     573             :                 "Exchange failed to respond!\n");
     574           0 :     resume_transfer_with_response (
     575             :       ptc,
     576             :       MHD_HTTP_GATEWAY_TIMEOUT,
     577           0 :       TALER_MHD_MAKE_JSON_PACK (
     578             :         TALER_JSON_pack_ec (TALER_EC_MERCHANT_GENERIC_EXCHANGE_TIMEOUT)));
     579           0 :     return;
     580             :   }
     581           1 :   if (NULL == eh)
     582             :   {
     583             :     /* The request failed somehow */
     584           0 :     GNUNET_break_op (0);
     585           0 :     resume_transfer_with_response (
     586             :       ptc,
     587             :       MHD_HTTP_BAD_GATEWAY,
     588           0 :       TALER_MHD_MAKE_JSON_PACK (
     589             :         TALER_JSON_pack_ec (
     590             :           TALER_EC_MERCHANT_GENERIC_EXCHANGE_CONNECT_FAILURE),
     591             :         TMH_pack_exchange_reply (hr)));
     592           0 :     return;
     593             :   }
     594             : 
     595             :   /* keep master key for later */
     596             :   {
     597             :     const struct TALER_EXCHANGE_Keys *keys;
     598             : 
     599           1 :     keys = TALER_EXCHANGE_get_keys (eh);
     600           1 :     if (NULL == keys)
     601             :     {
     602           0 :       GNUNET_break (0);
     603           0 :       resume_transfer_with_error (ptc,
     604             :                                   MHD_HTTP_BAD_GATEWAY,
     605             :                                   TALER_EC_MERCHANT_GENERIC_EXCHANGE_KEYS_FAILURE,
     606             :                                   NULL);
     607           0 :       return;
     608             :     }
     609           1 :     ptc->master_pub = keys->master_pub;
     610             :   }
     611             : 
     612           1 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     613             :               "Requesting transfer details from exchange\n");
     614           2 :   ptc->wdh = TALER_EXCHANGE_transfers_get (eh,
     615           1 :                                            &ptc->wtid,
     616             :                                            &wire_transfer_cb,
     617             :                                            ptc);
     618           1 :   if (NULL == ptc->wdh)
     619             :   {
     620           0 :     GNUNET_break (0);
     621           0 :     resume_transfer_with_error (ptc,
     622             :                                 MHD_HTTP_INTERNAL_SERVER_ERROR,
     623             :                                 TALER_EC_MERCHANT_PRIVATE_POST_TRANSFERS_REQUEST_ERROR,
     624             :                                 "failed to run GET /transfers/ on exchange");
     625             :   }
     626             : }
     627             : 
     628             : 
     629             : /**
     630             :  * Now we want to double-check that any (Taler coin) deposit which is
     631             :  * accounted into _this_ wire transfer, does exist into _our_ database.  This
     632             :  * is the rationale: if the exchange paid us for it, we must have received it
     633             :  * _beforehands_!
     634             :  *
     635             :  * @param cls a `struct PostTransfersContext`
     636             :  * @param current_offset at which offset in the exchange's reply are the @a ttd
     637             :  * @param ttd details about an aggregated transfer (to check)
     638             :  */
     639             : static void
     640           1 : verify_exchange_claim_cb (void *cls,
     641             :                           unsigned int current_offset,
     642             :                           const struct TALER_TrackTransferDetails *ttd)
     643             : {
     644           1 :   struct PostTransfersContext *ptc = cls;
     645             :   enum GNUNET_DB_QueryStatus qs;
     646             : 
     647           1 :   if (0 != ptc->response_code)
     648           0 :     return; /* already encountered an error */
     649           1 :   if (ptc->soft_retry)
     650           0 :     return; /* already encountered an error */
     651           1 :   ptc->current_offset = current_offset;
     652           1 :   ptc->current_detail = ttd;
     653             :   /* Set the coin as "never seen" before. */
     654           1 :   ptc->check_transfer_result = GNUNET_NO;
     655           1 :   qs = TMH_db->lookup_deposits_by_contract_and_coin (
     656           1 :     TMH_db->cls,
     657           1 :     ptc->hc->instance->settings.id,
     658             :     &ttd->h_contract_terms,
     659             :     &ttd->coin_pub,
     660             :     &check_transfer,
     661             :     ptc);
     662           1 :   switch (qs)
     663             :   {
     664           0 :   case GNUNET_DB_STATUS_SOFT_ERROR:
     665           0 :     ptc->soft_retry = true;
     666           0 :     return;
     667           0 :   case GNUNET_DB_STATUS_HARD_ERROR:
     668           0 :     GNUNET_break (0);
     669           0 :     ptc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
     670             :     ptc->response
     671           0 :       = TALER_MHD_make_error (TALER_EC_GENERIC_DB_FETCH_FAILED,
     672             :                               "deposit by contract and coin");
     673           0 :     return;
     674           0 :   case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
     675             :     /* The exchange says we made this deposit, but WE do not
     676             :        recall making it (corrupted / unreliable database?)!
     677             :        Well, let's say thanks and accept the money! */
     678           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     679             :                 "Failed to find payment data in DB\n");
     680           0 :     ptc->check_transfer_result = GNUNET_OK;
     681           0 :     break;
     682           1 :   case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
     683           1 :     break;
     684             :   }
     685           1 :   switch (ptc->check_transfer_result)
     686             :   {
     687           0 :   case GNUNET_NO:
     688             :     /* Internal error: how can we have called #check_transfer()
     689             :        but still have no result? */
     690           0 :     GNUNET_break (0);
     691           0 :     ptc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
     692           0 :     ptc->response =
     693           0 :       TALER_MHD_make_error (TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
     694             :                             "check_transfer_result must not be GNUNET_NO");
     695           0 :     return;
     696           0 :   case GNUNET_SYSERR:
     697             :     /* #check_transfer() failed, report conflict! */
     698           0 :     GNUNET_break_op (0);
     699           0 :     GNUNET_assert (NULL != ptc->response);
     700           0 :     return;
     701           1 :   case GNUNET_OK:
     702           1 :     break;
     703             :   }
     704             : }
     705             : 
     706             : 
     707             : /**
     708             :  * Represents an entry in the table used to sum up
     709             :  * individual deposits for each h_contract_terms/order_id
     710             :  * (as the exchange gives us per coin, and we return
     711             :  * per order).
     712             :  */
     713             : struct Entry
     714             : {
     715             : 
     716             :   /**
     717             :    * Order of the entry.
     718             :    */
     719             :   char *order_id;
     720             : 
     721             :   /**
     722             :    * Sum accumulator for deposited value.
     723             :    */
     724             :   struct TALER_Amount deposit_value;
     725             : 
     726             :   /**
     727             :    * Sum accumulator for deposit fee.
     728             :    */
     729             :   struct TALER_Amount deposit_fee;
     730             : 
     731             : };
     732             : 
     733             : 
     734             : /**
     735             :  * Function called with information about a wire transfer identifier.
     736             :  * Generate a response array based on the given information.
     737             :  *
     738             :  * @param cls closure, a hashmap to update
     739             :  * @param order_id the order to which the deposits belong
     740             :  * @param deposit_value the amount deposited under @a order_id
     741             :  * @param deposit_fee the fee charged for @a deposit_value
     742             :  */
     743             : static void
     744           1 : transfer_summary_cb (void *cls,
     745             :                      const char *order_id,
     746             :                      const struct TALER_Amount *deposit_value,
     747             :                      const struct TALER_Amount *deposit_fee)
     748             : {
     749           1 :   struct GNUNET_CONTAINER_MultiHashMap *map = cls;
     750             :   struct Entry *current_entry;
     751             :   struct GNUNET_HashCode h_key;
     752             : 
     753           1 :   GNUNET_CRYPTO_hash (order_id,
     754             :                       strlen (order_id),
     755             :                       &h_key);
     756           1 :   current_entry = GNUNET_CONTAINER_multihashmap_get (map,
     757             :                                                      &h_key);
     758           1 :   if (NULL != current_entry)
     759             :   {
     760             :     /* The map already knows this order, do aggregation */
     761           0 :     GNUNET_assert ( (0 <=
     762             :                      TALER_amount_add (&current_entry->deposit_value,
     763             :                                        &current_entry->deposit_value,
     764             :                                        deposit_value)) &&
     765             :                     (0 <=
     766             :                      TALER_amount_add (&current_entry->deposit_fee,
     767             :                                        &current_entry->deposit_fee,
     768             :                                        deposit_fee)) );
     769             :   }
     770             :   else
     771             :   {
     772             :     /* First time in the map for this h_contract_terms*/
     773           1 :     current_entry = GNUNET_new (struct Entry);
     774           1 :     current_entry->deposit_value = *deposit_value;
     775           1 :     current_entry->deposit_fee = *deposit_fee;
     776           1 :     current_entry->order_id = GNUNET_strdup (order_id);
     777           1 :     GNUNET_assert (GNUNET_SYSERR !=
     778             :                    GNUNET_CONTAINER_multihashmap_put (map,
     779             :                                                       &h_key,
     780             :                                                       current_entry,
     781             :                                                       GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
     782             :   }
     783           1 : }
     784             : 
     785             : 
     786             : /**
     787             :  * Callback that frees all the elements in the hashmap, and @a cls
     788             :  * is non-NULL, appends them as JSON to the array
     789             :  *
     790             :  * @param cls closure, NULL or a `json_t *` array
     791             :  * @param key current key
     792             :  * @param value a `struct Entry`
     793             :  * @return #GNUNET_YES if the iteration should continue,
     794             :  *         #GNUNET_NO otherwise.
     795             :  */
     796             : static int
     797           1 : hashmap_update_and_free (void *cls,
     798             :                          const struct GNUNET_HashCode *key,
     799             :                          void *value)
     800             : {
     801           1 :   json_t *ja = cls;
     802           1 :   struct Entry *entry = value;
     803             : 
     804             :   (void) key;
     805           1 :   if (NULL != ja)
     806             :   {
     807           1 :     GNUNET_assert (
     808             :       0 ==
     809             :       json_array_append_new (
     810             :         ja,
     811             :         GNUNET_JSON_PACK (
     812             :           GNUNET_JSON_pack_string ("order_id",
     813             :                                    entry->order_id),
     814             :           TALER_JSON_pack_amount ("deposit_value",
     815             :                                   &entry->deposit_value),
     816             :           TALER_JSON_pack_amount ("deposit_fee",
     817             :                                   &entry->deposit_fee))));
     818             :   }
     819           1 :   GNUNET_free (entry->order_id);
     820           1 :   GNUNET_free (entry);
     821           1 :   return GNUNET_YES;
     822             : }
     823             : 
     824             : 
     825             : /**
     826             :  * Handle a timeout for the processing of the track transfer request.
     827             :  *
     828             :  * @param cls closure
     829             :  */
     830             : static void
     831           1 : handle_transfer_timeout (void *cls)
     832             : {
     833           1 :   struct PostTransfersContext *ptc = cls;
     834             : 
     835           1 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     836             :               "Resuming POST /private/transfers with error after timeout\n");
     837           1 :   ptc->timeout_task = NULL;
     838           1 :   if (NULL != ptc->fo)
     839             :   {
     840           1 :     TMH_EXCHANGES_find_exchange_cancel (ptc->fo);
     841           1 :     ptc->fo = NULL;
     842             :   }
     843           1 :   if (NULL != ptc->wdh)
     844             :   {
     845           0 :     TALER_EXCHANGE_transfers_get_cancel (ptc->wdh);
     846           0 :     ptc->wdh = NULL;
     847             :   }
     848           1 :   resume_transfer_with_error (ptc,
     849             :                               MHD_HTTP_GATEWAY_TIMEOUT,
     850             :                               TALER_EC_MERCHANT_GENERIC_EXCHANGE_TIMEOUT,
     851             :                               NULL);
     852           1 : }
     853             : 
     854             : 
     855             : /**
     856             :  * We are *done* processing the request, just queue the response (!)
     857             :  *
     858             :  * @param ptc request context
     859             :  */
     860             : static MHD_RESULT
     861           1 : queue (struct PostTransfersContext *ptc)
     862             : {
     863             :   MHD_RESULT ret;
     864             : 
     865           1 :   GNUNET_assert (0 != ptc->response_code);
     866           1 :   if (UINT_MAX == ptc->response_code)
     867             :   {
     868           0 :     GNUNET_break (0);
     869           0 :     return MHD_NO;   /* hard error */
     870             :   }
     871           1 :   ret = MHD_queue_response (ptc->connection,
     872             :                             ptc->response_code,
     873             :                             ptc->response);
     874           1 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     875             :               "Queueing response (%u) for POST /private/transfers (%s).\n",
     876             :               (unsigned int) ptc->response_code,
     877             :               ret ? "OK" : "FAILED");
     878           1 :   return ret;
     879             : }
     880             : 
     881             : 
     882             : /**
     883             :  * Download transfer data from the exchange.
     884             :  *
     885             :  * @param ptc request context
     886             :  */
     887             : static void
     888           2 : download (struct PostTransfersContext *ptc)
     889             : {
     890           2 :   ptc->downloaded = true;
     891           2 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     892             :               "Suspending POST /private/transfers handling while working with exchange\n");
     893           2 :   MHD_suspend_connection (ptc->connection);
     894           2 :   ptc->suspended = true;
     895           2 :   GNUNET_CONTAINER_DLL_insert (ptc_head,
     896             :                                ptc_tail,
     897             :                                ptc);
     898           2 :   ptc->fo = TMH_EXCHANGES_find_exchange (ptc->exchange_url,
     899             :                                          NULL,
     900             :                                          GNUNET_NO,
     901             :                                          &process_transfer_with_exchange,
     902             :                                          ptc);
     903             :   ptc->timeout_task
     904           2 :     = GNUNET_SCHEDULER_add_delayed (TRANSFER_GENERIC_TIMEOUT,
     905             :                                     &handle_transfer_timeout,
     906             :                                     ptc);
     907           2 : }
     908             : 
     909             : 
     910             : MHD_RESULT
     911           4 : TMH_private_post_transfers (const struct TMH_RequestHandler *rh,
     912             :                             struct MHD_Connection *connection,
     913             :                             struct TMH_HandlerContext *hc)
     914             : {
     915           4 :   struct PostTransfersContext *ptc = hc->ctx;
     916             :   enum GNUNET_DB_QueryStatus qs;
     917             : 
     918           4 :   if (NULL == ptc)
     919             :   {
     920           2 :     ptc = GNUNET_new (struct PostTransfersContext);
     921           2 :     ptc->connection = connection;
     922           2 :     ptc->hc = hc;
     923           2 :     hc->ctx = ptc;
     924           2 :     hc->cc = &transfer_cleanup;
     925             :   }
     926             :   /* resume logic: did we get resumed after a reply was built? */
     927           4 :   if (0 != ptc->response_code)
     928           1 :     return queue (ptc);
     929           3 :   if ( (NULL != ptc->fo) ||
     930           3 :        (NULL != ptc->wdh) )
     931             :   {
     932             :     /* likely old MHD version causing spurious wake-up */
     933           0 :     GNUNET_break (ptc->suspended);
     934           0 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     935             :                 "Not sure why we are here, should be suspended\n");
     936           0 :     return MHD_YES; /* still work in progress */
     937             :   }
     938           3 :   if (NULL == ptc->exchange_url)
     939             :   {
     940             :     /* First request, parse it! */
     941             :     struct GNUNET_JSON_Specification spec[] = {
     942           2 :       TALER_JSON_spec_amount ("credit_amount",
     943             :                               TMH_currency,
     944             :                               &ptc->amount),
     945           2 :       GNUNET_JSON_spec_fixed_auto ("wtid",
     946             :                                    &ptc->wtid),
     947           2 :       GNUNET_JSON_spec_string ("payto_uri",
     948             :                                &ptc->payto_uri),
     949           2 :       GNUNET_JSON_spec_string ("exchange_url",
     950             :                                &ptc->exchange_url),
     951           2 :       GNUNET_JSON_spec_end ()
     952             :     };
     953             :     enum GNUNET_GenericReturnValue res;
     954             : 
     955           2 :     res = TALER_MHD_parse_json_data (connection,
     956           2 :                                      hc->request_body,
     957             :                                      spec);
     958           2 :     if (GNUNET_OK != res)
     959             :       return (GNUNET_NO == res)
     960             :         ? MHD_YES
     961           0 :         : MHD_NO;
     962           2 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     963             :                 "New inbound wire transfer over %s to %s from %s\n",
     964             :                 TALER_amount2s (&ptc->amount),
     965             :                 ptc->payto_uri,
     966             :                 ptc->exchange_url);
     967             :   }
     968             : 
     969             :   /* Check if transfer data is in database, if not, add it. */
     970           3 :   for (unsigned int retry = 0; retry<MAX_RETRIES; retry++)
     971             :   {
     972             :     struct GNUNET_TIME_Absolute execution_time;
     973             :     struct TALER_Amount total_amount;
     974             :     struct TALER_Amount exchange_amount;
     975             :     struct TALER_Amount wire_fee;
     976             :     bool verified;
     977             :     bool have_exchange_sig;
     978             : 
     979           3 :     TMH_db->preflight (TMH_db->cls);
     980           3 :     if (GNUNET_OK !=
     981           3 :         TMH_db->start (TMH_db->cls,
     982             :                        "post-transfers"))
     983             :     {
     984           0 :       GNUNET_break (0);
     985           3 :       return TALER_MHD_reply_with_error (connection,
     986             :                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
     987             :                                          TALER_EC_GENERIC_DB_START_FAILED,
     988             :                                          "transfer");
     989             :     }
     990           3 :     qs = TMH_db->lookup_transfer (TMH_db->cls,
     991           3 :                                   ptc->hc->instance->settings.id,
     992             :                                   ptc->exchange_url,
     993           3 :                                   &ptc->wtid,
     994             :                                   &total_amount,
     995             :                                   &wire_fee,
     996             :                                   &exchange_amount,
     997             :                                   &execution_time,
     998             :                                   &have_exchange_sig,
     999             :                                   &verified);
    1000           3 :     switch (qs)
    1001             :     {
    1002           0 :     case GNUNET_DB_STATUS_HARD_ERROR:
    1003           0 :       GNUNET_break (0);
    1004           0 :       TMH_db->rollback (TMH_db->cls);
    1005           0 :       return TALER_MHD_reply_with_error (connection,
    1006             :                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
    1007             :                                          TALER_EC_GENERIC_DB_FETCH_FAILED,
    1008             :                                          "transfer");
    1009             :       break;
    1010           0 :     case GNUNET_DB_STATUS_SOFT_ERROR:
    1011           0 :       TMH_db->rollback (TMH_db->cls);
    1012           0 :       continue;
    1013           2 :     case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    1014             :       /* Transfer so far unknown; try to persist the wire transfer information
    1015             :          we have received in the database (it is not yet present). Upon
    1016             :          success, try to download the transfer details from the exchange. */
    1017             :       {
    1018             :         uint64_t account_serial;
    1019             : 
    1020             :         /* Make sure the bank account is configured. */
    1021           2 :         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1022             :                     "Transfer is not yet known\n");
    1023           2 :         qs = TMH_db->lookup_account (TMH_db->cls,
    1024           2 :                                      ptc->hc->instance->settings.id,
    1025             :                                      ptc->payto_uri,
    1026             :                                      &account_serial);
    1027             :         switch (qs)
    1028             :         {
    1029           0 :         case GNUNET_DB_STATUS_SOFT_ERROR:
    1030           0 :           TMH_db->rollback (TMH_db->cls);
    1031           0 :           continue;
    1032           0 :         case GNUNET_DB_STATUS_HARD_ERROR:
    1033           0 :           GNUNET_break (0);
    1034           0 :           TMH_db->rollback (TMH_db->cls);
    1035           2 :           return TALER_MHD_reply_with_error (connection,
    1036             :                                              MHD_HTTP_INTERNAL_SERVER_ERROR,
    1037             :                                              TALER_EC_GENERIC_DB_FETCH_FAILED,
    1038             :                                              "lookup_account");
    1039           0 :         case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    1040           0 :           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    1041             :                       "Bank account `%s' not configured for instance `%s'\n",
    1042             :                       ptc->payto_uri,
    1043             :                       ptc->hc->instance->settings.id);
    1044           0 :           TMH_db->rollback (TMH_db->cls);
    1045           0 :           return TALER_MHD_reply_with_error (connection,
    1046             :                                              MHD_HTTP_NOT_FOUND,
    1047             :                                              TALER_EC_MERCHANT_PRIVATE_POST_TRANSFERS_ACCOUNT_NOT_FOUND,
    1048             :                                              ptc->payto_uri);
    1049           2 :         case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    1050           2 :           break;
    1051             :         }
    1052             : 
    1053           2 :         GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1054             :                     "Inserting new transfer\n");
    1055           2 :         qs = TMH_db->insert_transfer (TMH_db->cls,
    1056           2 :                                       ptc->hc->instance->settings.id,
    1057             :                                       ptc->exchange_url,
    1058           2 :                                       &ptc->wtid,
    1059           2 :                                       &ptc->amount,
    1060             :                                       ptc->payto_uri,
    1061             :                                       true /* confirmed! */);
    1062             :         switch (qs)
    1063             :         {
    1064           0 :         case GNUNET_DB_STATUS_SOFT_ERROR:
    1065           0 :           TMH_db->rollback (TMH_db->cls);
    1066           0 :           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1067             :                       "Soft error, retrying...\n");
    1068           0 :           continue;
    1069           0 :         case GNUNET_DB_STATUS_HARD_ERROR:
    1070           0 :           GNUNET_break (0);
    1071           0 :           TMH_db->rollback (TMH_db->cls);
    1072           0 :           return TALER_MHD_reply_with_error (connection,
    1073             :                                              MHD_HTTP_INTERNAL_SERVER_ERROR,
    1074             :                                              TALER_EC_GENERIC_DB_STORE_FAILED,
    1075             :                                              "transfer");
    1076           0 :         case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    1077           0 :           TMH_db->rollback (TMH_db->cls);
    1078             :           /* Should not happen: we checked earlier! */
    1079           0 :           GNUNET_break (0);
    1080           0 :           return TALER_MHD_reply_with_error (connection,
    1081             :                                              MHD_HTTP_CONFLICT,
    1082             :                                              TALER_EC_GENERIC_DB_STORE_FAILED,
    1083             :                                              "not unique");
    1084           2 :         case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    1085           2 :           break;
    1086             :         }
    1087             : 
    1088           2 :         qs = TMH_db->commit (TMH_db->cls);
    1089             :         switch (qs)
    1090             :         {
    1091           0 :         case GNUNET_DB_STATUS_SOFT_ERROR:
    1092           0 :           TMH_db->rollback (TMH_db->cls);
    1093           0 :           continue;
    1094           0 :         case GNUNET_DB_STATUS_HARD_ERROR:
    1095           0 :           GNUNET_break (0);
    1096           0 :           TMH_db->rollback (TMH_db->cls);
    1097           0 :           return TALER_MHD_reply_with_error (connection,
    1098             :                                              MHD_HTTP_INTERNAL_SERVER_ERROR,
    1099             :                                              TALER_EC_GENERIC_DB_COMMIT_FAILED,
    1100             :                                              NULL);
    1101           2 :         case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    1102             :         case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    1103           2 :           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1104             :                       "post-transfer committed successfully\n");
    1105           2 :           break;
    1106             :         }
    1107           2 :         download (ptc);
    1108           2 :         return MHD_YES; /* download() always suspends */
    1109             :       }
    1110           1 :     case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    1111             :       /* Transfer exists */
    1112           1 :       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1113             :                   "Transfer exists in DB (verified: %s, exchange signature: %s)\n",
    1114             :                   verified ? "true" : "false",
    1115             :                   have_exchange_sig ? "true" : "false");
    1116           1 :       if (! verified)
    1117             :       {
    1118           1 :         if ( (! ptc->downloaded) &&
    1119           0 :              (! have_exchange_sig) )
    1120             :         {
    1121             :           /* We may have previously attempted and failed to
    1122             :              download the exchange data, do it again! */
    1123           0 :           TMH_db->rollback (TMH_db->cls);
    1124           0 :           download (ptc);
    1125           0 :           return MHD_YES; /* download always suspends */
    1126             :         }
    1127           1 :         if (! have_exchange_sig)
    1128             :         {
    1129             :           /* We tried to download and still failed to get
    1130             :              an exchange signture. Still, that should have
    1131             :              been handled there. */
    1132           0 :           TMH_db->rollback (TMH_db->cls);
    1133           0 :           GNUNET_break (0);
    1134           0 :           return TALER_MHD_reply_with_error (connection,
    1135             :                                              MHD_HTTP_INTERNAL_SERVER_ERROR,
    1136             :                                              TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
    1137             :                                              "download but no exchange signature and no error");
    1138             :         }
    1139             :         /* verify */
    1140           1 :         if (GNUNET_SYSERR ==
    1141           1 :             check_wire_fee (ptc,
    1142             :                             execution_time,
    1143             :                             &wire_fee))
    1144             :         {
    1145           0 :           TMH_db->rollback (TMH_db->cls);
    1146           0 :           return queue (ptc); /* generate error */
    1147             :         }
    1148           1 :         if (ptc->soft_retry)
    1149             :         {
    1150             :           /* DB serialization failure */
    1151           0 :           ptc->soft_retry = false;
    1152           0 :           TMH_db->rollback (TMH_db->cls);
    1153           0 :           continue;
    1154             :         }
    1155           1 :         qs = TMH_db->lookup_transfer_details (TMH_db->cls,
    1156             :                                               ptc->exchange_url,
    1157           1 :                                               &ptc->wtid,
    1158             :                                               &verify_exchange_claim_cb,
    1159             :                                               ptc);
    1160             :         switch (qs)
    1161             :         {
    1162           0 :         case GNUNET_DB_STATUS_HARD_ERROR:
    1163           0 :           GNUNET_break (0);
    1164           0 :           TMH_db->rollback (TMH_db->cls);
    1165           0 :           return TALER_MHD_reply_with_error (connection,
    1166             :                                              MHD_HTTP_INTERNAL_SERVER_ERROR,
    1167             :                                              TALER_EC_GENERIC_DB_FETCH_FAILED,
    1168             :                                              "lookup_transfer_details");
    1169           0 :         case GNUNET_DB_STATUS_SOFT_ERROR:
    1170           0 :           TMH_db->rollback (TMH_db->cls);
    1171           0 :           continue;
    1172           1 :         case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    1173             :         case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    1174             :         default:
    1175           1 :           break;
    1176             :         }
    1177           1 :         if (0 != ptc->response_code)
    1178             :         {
    1179           0 :           TMH_db->rollback (TMH_db->cls);
    1180           0 :           return queue (ptc); /* generate error */
    1181             :         }
    1182           1 :         if (ptc->soft_retry)
    1183             :         {
    1184             :           /* DB serialization failure */
    1185           0 :           ptc->soft_retry = false;
    1186           0 :           TMH_db->rollback (TMH_db->cls);
    1187           0 :           continue;
    1188             :         }
    1189             : 
    1190             :         {
    1191             :           struct TALER_Amount delta;
    1192             : 
    1193           1 :           if (0 >
    1194           1 :               TALER_amount_subtract (&delta,
    1195             :                                      &total_amount,
    1196             :                                      &wire_fee))
    1197             :           {
    1198           0 :             GNUNET_break (0);
    1199           0 :             TMH_db->rollback (TMH_db->cls);
    1200           0 :             return TALER_MHD_reply_with_error (
    1201             :               connection,
    1202             :               MHD_HTTP_INTERNAL_SERVER_ERROR,
    1203             :               TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
    1204             :               NULL);
    1205             :           }
    1206           1 :           if (0 !=
    1207           1 :               TALER_amount_cmp (&exchange_amount,
    1208             :                                 &delta))
    1209             :           {
    1210           0 :             GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1211             :                         "Amount of expected was %s\n",
    1212             :                         TALER_amount2s (&delta));
    1213           0 :             TMH_db->rollback (TMH_db->cls);
    1214           0 :             return TALER_MHD_reply_with_error (
    1215             :               connection,
    1216             :               MHD_HTTP_CONFLICT,
    1217             :               TALER_EC_MERCHANT_PRIVATE_POST_TRANSFERS_CONFLICTING_TRANSFERS,
    1218             :               TALER_amount2s (&exchange_amount));
    1219             :           }
    1220           1 :           if ( (GNUNET_OK !=
    1221           1 :                 TALER_amount_cmp_currency (&ptc->amount,
    1222           1 :                                            &delta)) ||
    1223             :                (0 !=
    1224           1 :                 TALER_amount_cmp (&ptc->amount,
    1225             :                                   &delta)) )
    1226             :           {
    1227           0 :             GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1228             :                         "Amount submitted was %s\n",
    1229             :                         TALER_amount2s (&ptc->amount));
    1230           0 :             TMH_db->rollback (TMH_db->cls);
    1231           0 :             return TALER_MHD_reply_with_error (
    1232             :               connection,
    1233             :               MHD_HTTP_CONFLICT,
    1234             :               TALER_EC_MERCHANT_PRIVATE_POST_TRANSFERS_CONFLICTING_SUBMISSION,
    1235             :               TALER_amount2s (&exchange_amount));
    1236             :           }
    1237             :         }
    1238           1 :         verified = true;
    1239           1 :         qs = TMH_db->set_transfer_status_to_verified (TMH_db->cls,
    1240             :                                                       ptc->exchange_url,
    1241           1 :                                                       &ptc->wtid);
    1242             :         switch (qs)
    1243             :         {
    1244           0 :         case GNUNET_DB_STATUS_HARD_ERROR:
    1245           0 :           GNUNET_break (0);
    1246           0 :           TMH_db->rollback (TMH_db->cls);
    1247           0 :           return TALER_MHD_reply_with_error (connection,
    1248             :                                              MHD_HTTP_INTERNAL_SERVER_ERROR,
    1249             :                                              TALER_EC_GENERIC_DB_FETCH_FAILED,
    1250             :                                              "set_transfer_status_to_verified");
    1251           0 :         case GNUNET_DB_STATUS_SOFT_ERROR:
    1252           0 :           TMH_db->rollback (TMH_db->cls);
    1253           0 :           continue;
    1254           0 :         case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    1255           0 :           GNUNET_assert (0);
    1256             :           break;
    1257           1 :         case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    1258           1 :           break;
    1259             :         }
    1260           0 :       } /* end of 'if (! verified)' */
    1261             : 
    1262             :       /* Short version: we verified that the exchange reply and
    1263             :          our own accounting match; generate the summary response */
    1264           1 :       GNUNET_assert (verified);
    1265             :       {
    1266             :         struct GNUNET_CONTAINER_MultiHashMap *map;
    1267             :         json_t *deposit_sums;
    1268             : 
    1269           1 :         map = GNUNET_CONTAINER_multihashmap_create (16,
    1270             :                                                     GNUNET_NO);
    1271           1 :         qs = TMH_db->lookup_transfer_summary (TMH_db->cls,
    1272             :                                               ptc->exchange_url,
    1273           1 :                                               &ptc->wtid,
    1274             :                                               &transfer_summary_cb,
    1275             :                                               map);
    1276             :         switch (qs)
    1277             :         {
    1278           0 :         case GNUNET_DB_STATUS_SOFT_ERROR:
    1279           0 :           TMH_db->rollback (TMH_db->cls);
    1280           0 :           continue;
    1281           0 :         case GNUNET_DB_STATUS_HARD_ERROR:
    1282           0 :           GNUNET_break (0);
    1283           0 :           TMH_db->rollback (TMH_db->cls);
    1284           0 :           GNUNET_CONTAINER_multihashmap_iterate (map,
    1285             :                                                  &hashmap_update_and_free,
    1286             :                                                  NULL);
    1287           0 :           GNUNET_CONTAINER_multihashmap_destroy (map);
    1288           1 :           return TALER_MHD_reply_with_error (connection,
    1289             :                                              MHD_HTTP_INTERNAL_SERVER_ERROR,
    1290             :                                              TALER_EC_GENERIC_DB_FETCH_FAILED,
    1291             :                                              "transfer summary");
    1292           1 :         case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    1293             :         case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    1294             :         default:
    1295           1 :           break;
    1296             :         }
    1297             : 
    1298           1 :         qs = TMH_db->commit (TMH_db->cls);
    1299             :         switch (qs)
    1300             :         {
    1301           0 :         case GNUNET_DB_STATUS_SOFT_ERROR:
    1302           0 :           TMH_db->rollback (TMH_db->cls);
    1303           0 :           continue;
    1304           0 :         case GNUNET_DB_STATUS_HARD_ERROR:
    1305           0 :           GNUNET_break (0);
    1306           0 :           TMH_db->rollback (TMH_db->cls);
    1307           0 :           return TALER_MHD_reply_with_error (connection,
    1308             :                                              MHD_HTTP_INTERNAL_SERVER_ERROR,
    1309             :                                              TALER_EC_GENERIC_DB_COMMIT_FAILED,
    1310             :                                              NULL);
    1311           1 :         case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    1312             :         case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    1313           1 :           GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    1314             :                       "post-transfer committed uselessly\n");
    1315           1 :           break;
    1316             :         }
    1317             : 
    1318           1 :         deposit_sums = json_array ();
    1319           1 :         GNUNET_assert (NULL != deposit_sums);
    1320           1 :         GNUNET_CONTAINER_multihashmap_iterate (map,
    1321             :                                                &hashmap_update_and_free,
    1322             :                                                deposit_sums);
    1323           1 :         GNUNET_CONTAINER_multihashmap_destroy (map);
    1324           1 :         return TALER_MHD_REPLY_JSON_PACK (
    1325             :           connection,
    1326             :           MHD_HTTP_OK,
    1327             :           TALER_JSON_pack_amount ("total",
    1328             :                                   &total_amount),
    1329             :           TALER_JSON_pack_amount ("wire_fee",
    1330             :                                   &wire_fee),
    1331             :           GNUNET_JSON_pack_time_abs ("execution_time",
    1332             :                                      execution_time),
    1333             :           GNUNET_JSON_pack_array_steal ("deposit_sums",
    1334             :                                         deposit_sums));
    1335             :       } /* end of 'verified == true' (not an 'if'!) */
    1336             :     } /* end of 'switch (qs)' */
    1337           0 :     GNUNET_assert (0);
    1338             :   } /* end of 'for(retries...) */
    1339           0 :   return TALER_MHD_reply_with_error (connection,
    1340             :                                      MHD_HTTP_INTERNAL_SERVER_ERROR,
    1341             :                                      TALER_EC_GENERIC_DB_SOFT_FAILURE,
    1342             :                                      "post-transfers");
    1343             : }
    1344             : 
    1345             : 
    1346             : /* end of taler-merchant-httpd_private-post-transfers.c */

Generated by: LCOV version 1.14