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: 3 427 0.7 %
Date: 2022-06-30 06:15:34 Functions: 1 15 6.7 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.14