From 68fcf5f245b9beccf157f95f06984508df95730a Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Tue, 5 May 2020 21:24:37 +0200 Subject: fix backenddb --- .../taler-merchant-httpd_private-post-transfers.c | 497 +++++++++++---------- 1 file changed, 250 insertions(+), 247 deletions(-) (limited to 'src/backend/taler-merchant-httpd_private-post-transfers.c') diff --git a/src/backend/taler-merchant-httpd_private-post-transfers.c b/src/backend/taler-merchant-httpd_private-post-transfers.c index 7f55c917..6304761f 100644 --- a/src/backend/taler-merchant-httpd_private-post-transfers.c +++ b/src/backend/taler-merchant-httpd_private-post-transfers.c @@ -14,8 +14,8 @@ TALER; see the file COPYING. If not, see */ /** - * @file backend/taler-merchant-httpd_track-transfer.c - * @brief implement API for tracking transfers and wire transfers + * @file backend/taler-merchant-httpd_private-post-transfers.c + * @brief implement API for registering wire transfers * @author Marcello Stanisci * @author Christian Grothoff */ @@ -23,11 +23,9 @@ #include #include #include -#include "taler-merchant-httpd.h" -#include "taler-merchant-httpd_mhd.h" #include "taler-merchant-httpd_auditors.h" #include "taler-merchant-httpd_exchanges.h" -#include "taler-merchant-httpd_track-transfer.h" +#include "taler-merchant-httpd_private-post-transfers.h" /** @@ -42,20 +40,45 @@ #define MAX_RETRIES 3 /** - * Context used for handing /track/transfer requests. + * Context used for handing POST /private/transfers requests. */ struct TrackTransferContext { /** - * This MUST be first! + * Kept in a DLL. */ - struct TM_HandlerContext hc; + struct TrackTransferContext *next; /** - * Handle to the exchange. + * Kept in a DLL. */ - struct TALER_EXCHANGE_Handle *eh; + struct TrackTransferContext *prev; + + /** + * Argument for the /wire/transfers request. + */ + struct TALER_WireTransferIdentifierRawP wtid; + + /** + * Amount of the wire transfer. + */ + struct TALER_Amount amount; + + /** + * URL of the exchange. + */ + char *exchange_url; + + /** + * payto:// URI used for the transfer. + */ + char *payto_uri; + + /** + * Master public key of the exchange at @e exchange_url. + */ + struct TALER_MasterPublicKeyP master_pub; /** * Handle for the /wire/transfers request. @@ -89,15 +112,6 @@ struct TrackTransferContext */ struct GNUNET_SCHEDULER_Task *timeout_task; - /** - * URL of the exchange. - */ - char *url; - - /** - * Wire method used for the transfer. - */ - char *wire_method; /** * Pointer to the detail that we are currently @@ -105,21 +119,6 @@ struct TrackTransferContext */ const struct TALER_TrackTransferDetails *current_detail; - /** - * Argument for the /wire/transfers request. - */ - struct TALER_WireTransferIdentifierRawP wtid; - - /** - * Full original response we are currently processing. - */ - const json_t *original_response; - - /** - * Modified response to return to the frontend. - */ - json_t *deposits_response; - /** * Which transaction detail are we currently looking at? */ @@ -139,13 +138,32 @@ struct TrackTransferContext }; +/** + * Head of list of suspended requests. + */ +static struct TrackTransferContext *pth_head; + +/** + * Tail of list of suspended requests. + */ +static struct TrackTransferContext *pth_tail; + + /** * Represents an entry in the table used to sum up - * individual deposits for each h_contract_terms. + * individual deposits for each h_contract_terms/order_id + * (as the exchange gives us per coin, and we return + * per order). FIXME: decide whether we do this HERE + * or in the DB! (SUM()...) */ struct Entry { + /** + * Order of the entry. FIXME: to be used!? + */ + char *order_id; + /** * Sum accumulator for deposited value. */ @@ -404,13 +422,16 @@ resume_track_transfer_with_response (struct TrackTransferContext *rctx, rctx->response_code = response_code; rctx->response = response; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Resuming /track/transfer handling as exchange interaction is done (%u)\n", + "Resuming POST /transfers handling as exchange interaction is done (%u)\n", response_code); if (NULL != rctx->timeout_task) { GNUNET_SCHEDULER_cancel (rctx->timeout_task); rctx->timeout_task = NULL; } + GNUNET_CONTAINER_DLL_remove (pth_head, + pth_tail, + rctx); MHD_resume_connection (rctx->connection); TMH_trigger_daemon (); /* we resumed, kick MHD */ } @@ -419,12 +440,12 @@ resume_track_transfer_with_response (struct TrackTransferContext *rctx, /** * Custom cleanup routine for a `struct TrackTransferContext`. * - * @param hc the `struct TrackTransferContext` to clean up. + * @param cls the `struct TrackTransferContext` to clean up. */ static void -track_transfer_cleanup (struct TM_HandlerContext *hc) +track_transfer_cleanup (void *cls) { - struct TrackTransferContext *rctx = (struct TrackTransferContext *) hc; + struct TrackTransferContext *rctx = cls; free_transfer_track_context (rctx); } @@ -511,30 +532,19 @@ check_wire_fee (struct TrackTransferContext *rctx, struct GNUNET_TIME_Absolute execution_time, const struct TALER_Amount *wire_fee) { - const struct TALER_MasterPublicKeyP *master_pub; - struct GNUNET_HashCode h_wire_method; struct TALER_Amount expected_fee; struct TALER_Amount closing_fee; struct TALER_MasterSignatureP master_sig; struct GNUNET_TIME_Absolute start_date; struct GNUNET_TIME_Absolute end_date; enum GNUNET_DB_QueryStatus qs; - const struct TALER_EXCHANGE_Keys *keys; + char *wire_method; - keys = TALER_EXCHANGE_get_keys (rctx->eh); - if (NULL == keys) - { - GNUNET_break (0); - return GNUNET_NO; - } - master_pub = &keys->master_pub; - GNUNET_CRYPTO_hash (rctx->wire_method, - strlen (rctx->wire_method) + 1, - &h_wire_method); + wire_method = TALER_payto_get_method (rctx->payto_uri); db->preflight (db->cls); qs = db->lookup_wire_fee (db->cls, - master_pub, - &h_wire_method, + &rctx->master_pub, + wire_method, execution_time, &expected_fee, &closing_fee, @@ -545,16 +555,19 @@ check_wire_fee (struct TrackTransferContext *rctx, { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Failed to find wire fee for `%s' and method `%s' at %s in DB, accepting blindly that the fee is %s\n", - TALER_B2S (master_pub), - rctx->wire_method, + TALER_B2S (&rctx->master_pub), + wire_method, GNUNET_STRINGS_absolute_time_to_string (execution_time), TALER_amount2s (wire_fee)); + GNUNET_free (wire_method); return GNUNET_NO; } if (0 <= TALER_amount_cmp (&expected_fee, wire_fee)) + { + GNUNET_free (wire_method); return GNUNET_OK; /* expected_fee >= wire_fee */ - + } /* Wire fee check failed, export proof to client */ resume_track_transfer_with_response ( rctx, @@ -569,8 +582,9 @@ check_wire_fee (struct TrackTransferContext *rctx, "start_date", GNUNET_JSON_from_time_abs (start_date), "end_date", GNUNET_JSON_from_time_abs (end_date), "master_sig", GNUNET_JSON_from_data_auto (&master_sig), - "master_pub", GNUNET_JSON_from_data_auto (master_pub), + "master_pub", GNUNET_JSON_from_data_auto (&rctx->master_pub), "json", json)); + GNUNET_free (wire_method); return GNUNET_SYSERR; } @@ -607,7 +621,7 @@ wire_transfer_cb (void *cls, rctx->wdh = NULL; GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Got response code %u from exchange for /track/transfer\n", + "Got response code %u from exchange for GET /transfers/$WTID\n", hr->http_status); if (MHD_HTTP_OK != hr->http_status) { @@ -622,36 +636,33 @@ wire_transfer_cb (void *cls, "exchange_reply", hr->reply)); return; } - for (unsigned int i = 0; ipreflight (db->cls); - qs = db->store_transfer_to_proof (db->cls, - rctx->url, - &rctx->wtid, - execution_time, - exchange_pub, - hr->reply); - if (GNUNET_DB_STATUS_SOFT_ERROR != qs) - break; - } + db->preflight (db->cls); + qs = db->insert_transfer_details (db->cls, + rctx->exchange_url, + rctx->payto_uri, + &rctx->wtid, + total_amount, + wire_fee, + execution_time, + details_length, + details); if (0 > qs) { /* Special report if retries insufficient */ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs); /* Always report on hard error as well to enable diagnostics */ GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs); - resume_track_transfer_with_response - (rctx, + resume_track_transfer_with_response ( + rctx, MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_MHD_make_json_pack ("{s:I, s:s}", - "code", - (json_int_t) - TALER_EC_TRACK_TRANSFER_DB_STORE_TRANSFER_ERROR, - "details", - "failed to store response from exchange to local database")); + TALER_MHD_make_json_pack ( + "{s:I, s:s}", + "code", + (json_int_t) TALER_EC_TRACK_TRANSFER_DB_STORE_TRANSFER_ERROR, + "details", + "failed to store response from exchange to local database")); return; } - rctx->original_response = hr->reply; if (GNUNET_SYSERR == check_wire_fee (rctx, @@ -767,27 +778,11 @@ wire_transfer_cb (void *cls, return; } } - rctx->original_response = NULL; - - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "About to call tracks transformator.\n"); - - if (NULL == (jresponse = - transform_response (hr->reply, - rctx))) - { - resume_track_transfer_with_response - (rctx, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_MHD_make_error (TALER_EC_TRACK_TRANSFER_JSON_RESPONSE_ERROR, - "Fail to elaborate the response.")); - return; - } + /* resume processing, main function will build the response */ resume_track_transfer_with_response (rctx, - MHD_HTTP_OK, - TALER_MHD_make_json (jresponse)); - json_decref (jresponse); + 0, + NULL); } @@ -801,12 +796,12 @@ wire_transfer_cb (void *cls, * @param exchange_trusted #GNUNET_YES if this exchange is trusted by config */ static void -process_track_transfer_with_exchange (void *cls, - const struct - TALER_EXCHANGE_HttpResponse *hr, - struct TALER_EXCHANGE_Handle *eh, - const struct TALER_Amount *wire_fee, - int exchange_trusted) +process_track_transfer_with_exchange ( + void *cls, + const struct TALER_EXCHANGE_HttpResponse *hr, + struct TALER_EXCHANGE_Handle *eh, + const struct TALER_Amount *wire_fee, + int exchange_trusted) { struct TrackTransferContext *rctx = cls; @@ -829,7 +824,20 @@ process_track_transfer_with_exchange (void *cls, "exchange_reply", hr->reply)); return; } - rctx->eh = eh; + + /* keep master key for later */ + { + const struct TALER_EXCHANGE_Keys *keys; + + keys = TALER_EXCHANGE_get_keys (eh); + if (NULL == keys) + { + GNUNET_break (0); + return GNUNET_NO; + } + rctx->master_pub = keys->master_pub; + } + rctx->wdh = TALER_EXCHANGE_transfers_get (eh, &rctx->wtid, &wire_transfer_cb, @@ -837,19 +845,47 @@ process_track_transfer_with_exchange (void *cls, if (NULL == rctx->wdh) { GNUNET_break (0); - resume_track_transfer_with_response - (rctx, + resume_track_transfer_with_response ( + rctx, MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_MHD_make_json_pack ("{s:I, s:s}", - "code", - (json_int_t) - TALER_EC_TRACK_TRANSFER_REQUEST_ERROR, - "error", - "failed to run /transfers/ GET on exchange")); + TALER_MHD_make_json_pack ( + "{s:I, s:s}", + "code", + (json_int_t) TALER_EC_TRACK_TRANSFER_REQUEST_ERROR, + "error", + "failed to run GET /transfers/ on exchange")); } } +/** + * Function called with information about a wire transfer identifier. + * Generate a response array based on the given information. + * + * @param cls closure, a `json_t` array to update + * @param order_id the order to which the deposits belong + * @param deposit_value the amount deposited under @a order_id + * @param deposit_fee the fee charged for @a deposit_value + */ +static void +proof_cb (void *cls, + const char *order_id, + const struct TALER_Amount *deposit_value, + const struct TALER_Amount *deposit_fee) +{ + json_t *ja = cls; + + GNUNET_assert ( + 0 == + json_array_append_new ( + ja, + "{s:s,s:o,s:o}", + "order_id", order_id, + "deposit_value", TALER_JSON_spec_amount (deposit_value), + "deposit_fee", TALER_JSON_spec_amount (deposit_fee))); +} + + /** * Handle a timeout for the processing of the track transfer request. * @@ -861,14 +897,18 @@ handle_track_transfer_timeout (void *cls) struct TrackTransferContext *rctx = cls; GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Resuming /track/transfer with error after timeout\n"); + "Resuming POST /private/transfers with error after timeout\n"); rctx->timeout_task = NULL; - if (NULL != rctx->fo) { TMH_EXCHANGES_find_exchange_cancel (rctx->fo); rctx->fo = NULL; } + if (NULL != rctx->wdh) + { + TALER_EXCHANGE_transfers_get_cancel (rctx->wdh); + rctx->wdh = NULL; + } resume_track_transfer_with_response (rctx, MHD_HTTP_SERVICE_UNAVAILABLE, TALER_MHD_make_error ( @@ -878,81 +918,38 @@ handle_track_transfer_timeout (void *cls) /** - * Function called with information about a wire transfer identifier. - * Generate a response based on the given @a proof. - * - * @param cls closure - * @param proof proof from exchange about what the wire transfer was for. - * should match the `TrackTransactionResponse` format - * of the exchange - */ -static void -proof_cb (void *cls, - const json_t *proof) -{ - struct TrackTransferContext *rctx = cls; - json_t *transformed_response; - - if (NULL == (transformed_response = - transform_response (proof, - rctx))) - { - rctx->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; - rctx->response - = TALER_MHD_make_error (TALER_EC_TRACK_TRANSFER_JSON_RESPONSE_ERROR, - "Fail to elaborate response."); - return; - } - - rctx->response_code = MHD_HTTP_OK; - rctx->response = TALER_MHD_make_json (transformed_response); - json_decref (transformed_response); -} - - -/** - * Manages a /track/transfer call, thus it calls the /track/wtid - * offered by the exchange in order to return the set of transfers + * Manages a POST /private/transfers call. It calls the /track/wtid + * offered by the exchange in order to obtain the set of transfers * (of coins) associated with a given wire transfer. * * @param rh context of the handler * @param connection the MHD connection to handle - * @param[in,out] connection_cls the connection's closure (can be updated) - * @param upload_data upload data - * @param[in,out] upload_data_size number of bytes (left) in @a upload_data - * @param mi merchant backend instance, never NULL + * @param[in,out] hc context with further information about the request * @return MHD result code */ MHD_RESULT -MH_handler_track_transfer (struct TMH_RequestHandler *rh, - struct MHD_Connection *connection, - void **connection_cls, - const char *upload_data, - size_t *upload_data_size, - struct MerchantInstance *mi) +TMH_private_post_transfers (const struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + struct TMH_HandlerContext *hc) { - struct TrackTransferContext *rctx; + struct TrackTransferContext *rctx = hc->ctx; const char *str; const char *url; const char *wire_method; - MHD_RESULT ret; enum GNUNET_DB_QueryStatus qs; - if (NULL == *connection_cls) + if (NULL == rctx) { rctx = GNUNET_new (struct TrackTransferContext); - rctx->hc.cc = &track_transfer_cleanup; rctx->connection = connection; - *connection_cls = rctx; - } - else - { - /* not first call, recover state */ - rctx = *connection_cls; + hc->ctx = rctx; + hc->cleanup_cb = &track_transfer_cleanup; } if (0 != rctx->response_code) { + MHD_RESULT ret; + /* We are *done* processing the request, just queue the response (!) */ if (UINT_MAX == rctx->response_code) { @@ -968,13 +965,13 @@ MH_handler_track_transfer (struct TMH_RequestHandler *rh, rctx->response = NULL; } GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Queueing response (%u) for /track/transfer (%s).\n", + "Queueing response (%u) for POST /private/transfers (%s).\n", (unsigned int) rctx->response_code, ret ? "OK" : "FAILED"); return ret; } if ( (NULL != rctx->fo) || - (NULL != rctx->eh) ) + (NULL != rctx->wdh) ) { /* likely old MHD version */ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, @@ -982,66 +979,85 @@ MH_handler_track_transfer (struct TMH_RequestHandler *rh, return MHD_YES; /* still work in progress */ } - url = MHD_lookup_connection_value (connection, - MHD_GET_ARGUMENT_KIND, - "exchange"); - if (NULL == url) - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_PARAMETER_MISSING, - "exchange"); - rctx->url = GNUNET_strdup (url); - - /* FIXME: change again: we probably don't want the wire_method - but rather the _account_ (section) here! */ - wire_method = MHD_lookup_connection_value (connection, - MHD_GET_ARGUMENT_KIND, - "wire_method"); - if (NULL == wire_method) { - if (1) + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_string ("exchange", + &rctx->exchange_url), + GNUNET_JSON_spec_string ("payto_uri", + &rctx->payto_uri), + TALER_JSON_spec_amount ("amount", + &rctx->amount), + GNUNET_JSON_spec_fixed_auto ("wtid", + &rctx->wtid), + GNUNET_JSON_spec_end () + }; + + GNUNET_assert (NULL != mi); { - /* temporary work-around until demo is adjusted... */ - GNUNET_break (0); - wire_method = "x-taler-bank"; - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Client needs fixing, see API change for #4943!\n"); + enum GNUNET_GenericReturnValue res; + + res = TALER_MHD_parse_json_data (connection, + hc->request_body, + spec); + if (GNUNET_OK != res) + return (GNUNET_NO == res) + ? MHD_YES + : MHD_NO; } - else - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_PARAMETER_MISSING, - "wire_method"); } - rctx->wire_method = GNUNET_strdup (wire_method); - rctx->mi = mi; - str = MHD_lookup_connection_value (connection, - MHD_GET_ARGUMENT_KIND, - "wtid"); - if (NULL == str) - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_PARAMETER_MISSING, - "wtid"); - if (GNUNET_OK != - GNUNET_STRINGS_string_to_data (str, - strlen (str), - &rctx->wtid, - sizeof (rctx->wtid))) + + /* Check if reply is already in database! */ { - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_PARAMETER_MALFORMED, - "wtid"); + json_t *deposit_sums; + struct GNUNET_TIME_Absolute execution_time; + struct TALER_Amount total_amount; + struct TALER_Amount wire_fee; + + deposit_sums = json_array (); + GNUNET_assert (NULL != deposit_sums); + db->preflight (db->cls); + qs = db->lookup_transfer_details (db->cls, + rctx->exchange_url, + rctx->payto_uri, + &rctx->wtid, + &total_amount, + &wire_fee, + &execution_time, + &transfer_details_cb, + deposit_sums); + if (0 > qs) + { + /* Simple select queries should not cause serialization issues */ + GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs); + /* Always report on hard error as well to enable diagnostics */ + GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs); + json_decref (deposit_sums); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_POST_TRANSFERS_DB_LOOKUP_ERROR, + "Fail to query database about proofs"); + } + if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != qs) + { + return TALER_MHD_reply_json_pack ( + connection, + MHD_HTTP_OK, + "{s:o,s:o,s:o,s:o}", + "total", TALER_JSON_from_amount (&total_amount), + "wire_fee", TALER_JSON_from_amount (&wire_fee), + "execution_time", GNUNET_JSON_from_time_abs (execution_time), + "deposit_sums", deposit_sums); + } + json_decref (deposit_sums); } - /* Check if reply is already in database! */ - db->preflight (db->cls); - qs = db->find_proof_by_wtid (db->cls, - rctx->url, - &rctx->wtid, - &proof_cb, - rctx); + /* reply not in database, ensure the POST is in the database, and + start work to obtain the reply from the exchange */ + qs = db->insert_transfer (db->cls, + rctx->exchange_url, + &rctx->wtid, + &rctx->amount, + rctx->payto_uri); if (0 > qs) { /* Simple select queries should not cause serialization issues */ @@ -1050,30 +1066,17 @@ MH_handler_track_transfer (struct TMH_RequestHandler *rh, GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR == qs); return TALER_MHD_reply_with_error (connection, MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_TRACK_TRANSFER_DB_FETCH_DEPOSIT_ERROR, - "Fail to query database about proofs"); - } - if (0 != rctx->response_code) - { - ret = MHD_queue_response (connection, - rctx->response_code, - rctx->response); - if (NULL != rctx->response) - { - MHD_destroy_response (rctx->response); - rctx->response = NULL; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Queueing response (%u) for /track/transfer (%s).\n", - (unsigned int) rctx->response_code, - ret ? "OK" : "FAILED"); - return ret; + TALER_EC_POST_TRANSFER_DB_STORE_ERROR, + "Fail to update database with transfer record"); } GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Suspending /track/transfer handling while working with the exchange\n"); + "Suspending POST /private/transfers handling while working with exchange\n"); MHD_suspend_connection (connection); - rctx->fo = TMH_EXCHANGES_find_exchange (url, + GNUNET_CONTAINER_DLL_insert (pth_head, + pth_tail, + rctx); + rctx->fo = TMH_EXCHANGES_find_exchange (rctx->exchange_url, NULL, GNUNET_NO, &process_track_transfer_with_exchange, @@ -1086,4 +1089,4 @@ MH_handler_track_transfer (struct TMH_RequestHandler *rh, } -/* end of taler-merchant-httpd_track-transfer.c */ +/* end of taler-merchant-httpd_private-post-transfers.c */ -- cgit v1.2.3