From a0c66e79191ca4339ea91e74e3742ccd8bd515ab Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Mon, 19 Jun 2017 20:12:00 +0200 Subject: fix #5010 for /track/transfer --- src/exchange/taler-exchange-httpd_track_transfer.c | 212 ++++++++++++--------- src/exchangedb/plugin_exchangedb_postgres.c | 194 +++++++++---------- src/exchangedb/test_exchangedb.c | 8 +- src/include/taler_exchangedb_plugin.h | 10 +- 4 files changed, 232 insertions(+), 192 deletions(-) diff --git a/src/exchange/taler-exchange-httpd_track_transfer.c b/src/exchange/taler-exchange-httpd_track_transfer.c index 1b9dd9fad..57b621e48 100644 --- a/src/exchange/taler-exchange-httpd_track_transfer.c +++ b/src/exchange/taler-exchange-httpd_track_transfer.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2014, 2015, 2016 GNUnet e.V. + Copyright (C) 2014-2017 GNUnet e.V. TALER is free software; you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software @@ -81,14 +81,14 @@ struct TEH_TrackTransferDetail * @param wdd_head linked list with details about the combined deposits * @return MHD result code */ -int -TEH_RESPONSE_reply_track_transfer_details (struct MHD_Connection *connection, - const struct TALER_Amount *total, - const struct TALER_MerchantPublicKeyP *merchant_pub, - const struct GNUNET_HashCode *h_wire, - const struct TALER_Amount *wire_fee, - struct GNUNET_TIME_Absolute exec_time, - const struct TEH_TrackTransferDetail *wdd_head) +static int +reply_track_transfer_details (struct MHD_Connection *connection, + const struct TALER_Amount *total, + const struct TALER_MerchantPublicKeyP *merchant_pub, + const struct GNUNET_HashCode *h_wire, + const struct TALER_Amount *wire_fee, + struct GNUNET_TIME_Absolute exec_time, + const struct TEH_TrackTransferDetail *wdd_head) { const struct TEH_TrackTransferDetail *wdd_pos; json_t *deposits; @@ -154,6 +154,11 @@ TEH_RESPONSE_reply_track_transfer_details (struct MHD_Connection *connection, struct WtidTransactionContext { + /** + * Identifier of the wire transfer to track. + */ + struct TALER_WireTransferIdentifierRawP wtid; + /** * Total amount of the wire transfer, as calculated by * summing up the individual amounts. To be rounded down @@ -179,6 +184,11 @@ struct WtidTransactionContext */ struct GNUNET_HashCode h_wire; + /** + * Wire fee applicable at @e exec_time. + */ + struct TALER_Amount wire_fee; + /** * Execution time of the wire transfer */ @@ -309,102 +319,116 @@ handle_transaction_data (void *cls, /** * Execute a "/track/transfer". Returns the transaction information * associated with the given wire transfer identifier. + * + * If it returns a non-error code, the transaction logic MUST + * NOT queue a MHD response. IF it returns an hard error, the + * transaction logic MUST queue a MHD response and set @a mhd_ret. IF + * it returns the soft error code, the function MAY be called again to + * retry and MUST not queue a MHD response. * - * @param connection the MHD connection to handle - * @param wtid wire transfer identifier to resolve - * @return MHD result code + * @param cls closure + * @param connection MHD request which triggered the transaction + * @param session database session to use + * @param[out] mhd_ret set to MHD response status for @a connection, + * if transaction failed (!) + * @return transaction status */ -int -TEH_DB_execute_track_transfer (struct MHD_Connection *connection, - const struct TALER_WireTransferIdentifierRawP *wtid) +static enum GNUNET_DB_QueryStatus +track_transfer_transaction (void *cls, + struct MHD_Connection *connection, + struct TALER_EXCHANGEDB_Session *session, + int *mhd_ret) { - int ret; - struct WtidTransactionContext ctx; - struct TALER_EXCHANGEDB_Session *session; - struct TEH_TrackTransferDetail *wdd; + struct WtidTransactionContext *ctx = cls; + enum GNUNET_DB_QueryStatus qs; struct GNUNET_TIME_Absolute wire_fee_start_date; struct GNUNET_TIME_Absolute wire_fee_end_date; - struct TALER_Amount wire_fee; struct TALER_MasterSignatureP wire_fee_master_sig; - if (NULL == (session = TEH_plugin->get_session (TEH_plugin->cls))) + ctx->is_valid = GNUNET_NO; + ctx->wdd_head = NULL; + ctx->wdd_tail = NULL; + ctx->wire_method = NULL; + qs = TEH_plugin->lookup_wire_transfer (TEH_plugin->cls, + session, + &ctx->wtid, + &handle_transaction_data, + ctx); + if (0 > qs) { - GNUNET_break (0); - return TEH_RESPONSE_reply_internal_db_error (connection, - TALER_EC_DB_SETUP_FAILED); - } - ctx.is_valid = GNUNET_NO; - ctx.wdd_head = NULL; - ctx.wdd_tail = NULL; - ctx.wire_method = NULL; - ret = TEH_plugin->lookup_wire_transfer (TEH_plugin->cls, - session, - wtid, - &handle_transaction_data, - &ctx); - if (GNUNET_SYSERR == ret) - { - GNUNET_break (0); - ret = TEH_RESPONSE_reply_internal_db_error (connection, - TALER_EC_TRACK_TRANSFER_DB_FETCH_FAILED); - goto cleanup; + if (GNUNET_DB_STATUS_HARD_ERROR == qs) + { + GNUNET_break (0); + *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection, + TALER_EC_TRACK_TRANSFER_DB_FETCH_FAILED); + } + return qs; } - if (GNUNET_SYSERR == ctx.is_valid) + if (GNUNET_SYSERR == ctx->is_valid) { GNUNET_break (0); - ret = TEH_RESPONSE_reply_internal_db_error (connection, - TALER_EC_TRACK_TRANSFER_DB_INCONSISTENT); - goto cleanup; + *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection, + TALER_EC_TRACK_TRANSFER_DB_INCONSISTENT); + return GNUNET_DB_STATUS_HARD_ERROR; } - if (GNUNET_NO == ctx.is_valid) + if (GNUNET_NO == ctx->is_valid) { - ret = TEH_RESPONSE_reply_arg_unknown (connection, - TALER_EC_TRACK_TRANSFER_WTID_NOT_FOUND, - "wtid"); - goto cleanup; + *mhd_ret = TEH_RESPONSE_reply_arg_unknown (connection, + TALER_EC_TRACK_TRANSFER_WTID_NOT_FOUND, + "wtid"); + return GNUNET_DB_STATUS_HARD_ERROR; } - if (GNUNET_OK != - TEH_plugin->get_wire_fee (TEH_plugin->cls, - session, - ctx.wire_method, - ctx.exec_time, - &wire_fee_start_date, - &wire_fee_end_date, - &wire_fee, - &wire_fee_master_sig)) + qs = TEH_plugin->get_wire_fee (TEH_plugin->cls, + session, + ctx->wire_method, + ctx->exec_time, + &wire_fee_start_date, + &wire_fee_end_date, + &ctx->wire_fee, + &wire_fee_master_sig); + if (0 >= qs) { - GNUNET_break (0); - ret = TEH_RESPONSE_reply_internal_db_error (connection, - TALER_EC_TRACK_TRANSFER_WIRE_FEE_NOT_FOUND); - goto cleanup; + if ( (GNUNET_DB_STATUS_HARD_ERROR == qs) || + (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS) ) + { + GNUNET_break (0); + *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection, + TALER_EC_TRACK_TRANSFER_WIRE_FEE_NOT_FOUND); + } + return qs; } if (GNUNET_OK != - TALER_amount_subtract (&ctx.total, - &ctx.total, - &wire_fee)) + TALER_amount_subtract (&ctx->total, + &ctx->total, + &ctx->wire_fee)) { GNUNET_break (0); - ret = TEH_RESPONSE_reply_internal_db_error (connection, - TALER_EC_TRACK_TRANSFER_WIRE_FEE_INCONSISTENT); - goto cleanup; + *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection, + TALER_EC_TRACK_TRANSFER_WIRE_FEE_INCONSISTENT); + return GNUNET_DB_STATUS_HARD_ERROR; } - ret = TEH_RESPONSE_reply_track_transfer_details (connection, - &ctx.total, - &ctx.merchant_pub, - &ctx.h_wire, - &wire_fee, - ctx.exec_time, - ctx.wdd_head); - cleanup: - while (NULL != (wdd = ctx.wdd_head)) + return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; +} + + +/** + * Free data structure reachable from @a ctx, but not @a ctx itself. + * + * @param ctx context to free + */ +static void +free_ctx (struct WtidTransactionContext *ctx) +{ + struct TEH_TrackTransferDetail *wdd; + + while (NULL != (wdd = ctx->wdd_head)) { - GNUNET_CONTAINER_DLL_remove (ctx.wdd_head, - ctx.wdd_tail, + GNUNET_CONTAINER_DLL_remove (ctx->wdd_head, + ctx->wdd_tail, wdd); GNUNET_free (wdd); } - GNUNET_free_non_null (ctx.wire_method); - return ret; + GNUNET_free_non_null (ctx->wire_method); } @@ -425,19 +449,37 @@ TEH_TRACKING_handler_track_transfer (struct TEH_RequestHandler *rh, const char *upload_data, size_t *upload_data_size) { - struct TALER_WireTransferIdentifierRawP wtid; + struct WtidTransactionContext ctx; int res; + int mhd_ret; + memset (&ctx, 0, sizeof (ctx)); res = TEH_PARSE_mhd_request_arg_data (connection, "wtid", - &wtid, + &ctx.wtid, sizeof (struct TALER_WireTransferIdentifierRawP)); if (GNUNET_SYSERR == res) return MHD_NO; /* internal error */ if (GNUNET_NO == res) return MHD_YES; /* parse error */ - return TEH_DB_execute_track_transfer (connection, - &wtid); + if (GNUNET_OK != + TEH_DB_run_transaction (connection, + &mhd_ret, + &track_transfer_transaction, + &ctx)) + { + free_ctx (&ctx); + return mhd_ret; + } + mhd_ret = reply_track_transfer_details (connection, + &ctx.total, + &ctx.merchant_pub, + &ctx.h_wire, + &ctx.wire_fee, + ctx.exec_time, + ctx.wdd_head); + free_ctx (&ctx); + return mhd_ret; } diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c index 7267ef66f..2f05c0233 100644 --- a/src/exchangedb/plugin_exchangedb_postgres.c +++ b/src/exchangedb/plugin_exchangedb_postgres.c @@ -4430,50 +4430,44 @@ postgres_get_coin_transactions (void *cls, /** - * Lookup the list of Taler transactions that were aggregated - * into a wire transfer by the respective @a wtid. - * - * @param cls closure - * @param session database connection - * @param wtid the raw wire transfer identifier we used - * @param cb function to call on each transaction found - * @param cb_cls closure for @a cb - * @return #GNUNET_OK on success, #GNUNET_SYSERR on database errors, - * #GNUNET_NO if we found no results + * Closure for #handle_wt_result. */ -static int -postgres_lookup_wire_transfer (void *cls, - struct TALER_EXCHANGEDB_Session *session, - const struct TALER_WireTransferIdentifierRawP *wtid, - TALER_EXCHANGEDB_WireTransferDataCallback cb, - void *cb_cls) +struct WireTransferResultContext { - PGresult *result; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (wtid), - GNUNET_PQ_query_param_end - }; - int nrows; + /** + * Function to call on each result. + */ + TALER_EXCHANGEDB_WireTransferDataCallback cb; - /* check if the melt record exists and get it */ - result = GNUNET_PQ_exec_prepared (session->conn, - "lookup_transactions", - params); - if (PGRES_TUPLES_OK != PQresultStatus (result)) - { - BREAK_DB_ERR (result, session->conn); - PQclear (result); - return GNUNET_SYSERR; - } - nrows = PQntuples (result); - if (0 == nrows) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "lookup_wire_transfer() returned 0 matching rows\n"); - PQclear (result); - return GNUNET_NO; - } - for (int i=0;istatus = GNUNET_SYSERR; + return; } t = json_object_get (wire, "type"); if (NULL == t) { GNUNET_break (0); - PQclear (result); - return GNUNET_SYSERR; + ctx->status = GNUNET_SYSERR; + return; } wire_method = json_string_value (t); if (NULL == wire_method) { GNUNET_break (0); - PQclear (result); - return GNUNET_SYSERR; + ctx->status = GNUNET_SYSERR; + return; } - cb (cb_cls, - rowid, - &merchant_pub, - wire_method, - &h_wire, - exec_time, - &h_contract_terms, - &coin_pub, - &amount_with_fee, - &deposit_fee); + ctx->cb (ctx->cb_cls, + rowid, + &merchant_pub, + wire_method, + &h_wire, + exec_time, + &h_contract_terms, + &coin_pub, + &amount_with_fee, + &deposit_fee); GNUNET_PQ_cleanup_result (rs); } - PQclear (result); - return GNUNET_OK; +} + + +/** + * Lookup the list of Taler transactions that were aggregated + * into a wire transfer by the respective @a wtid. + * + * @param cls closure + * @param session database connection + * @param wtid the raw wire transfer identifier we used + * @param cb function to call on each transaction found + * @param cb_cls closure for @a cb + * @return query status of the transaction + */ +static enum GNUNET_DB_QueryStatus +postgres_lookup_wire_transfer (void *cls, + struct TALER_EXCHANGEDB_Session *session, + const struct TALER_WireTransferIdentifierRawP *wtid, + TALER_EXCHANGEDB_WireTransferDataCallback cb, + void *cb_cls) +{ + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_auto_from_type (wtid), + GNUNET_PQ_query_param_end + }; + struct WireTransferResultContext ctx; + enum GNUNET_DB_QueryStatus qs; + + ctx.cb = cb; + ctx.cb_cls = cb_cls; + ctx.status = GNUNET_OK; + /* check if the melt record exists and get it */ + qs = GNUNET_PQ_eval_prepared_multi_select (session->conn, + "lookup_transactions", + params, + &handle_wt_result, + &ctx); + if (GNUNET_OK != ctx.status) + return GNUNET_DB_STATUS_HARD_ERROR; + return qs; } @@ -4733,10 +4765,9 @@ postgres_insert_aggregation_tracking (void *cls, * @param[out] end_date when does the fee end being valid * @param[out] wire_fee how high is the wire transfer fee * @param[out] master_sig signature over the above by the exchange master key - * @return #GNUNET_OK on success, #GNUNET_NO if no fee is known - * #GNUNET_SYSERR on failure + * @return status of the transaction */ -static int +static enum GNUNET_DB_QueryStatus postgres_get_wire_fee (void *cls, struct TALER_EXCHANGEDB_Session *session, const char *type, @@ -4758,42 +4789,11 @@ postgres_get_wire_fee (void *cls, GNUNET_PQ_result_spec_auto_from_type ("master_sig", master_sig), GNUNET_PQ_result_spec_end }; - PGresult *result; - int nrows; - result = GNUNET_PQ_exec_prepared (session->conn, - "get_wire_fee", - params); - if (PGRES_TUPLES_OK != - PQresultStatus (result)) - { - BREAK_DB_ERR (result, session->conn); - PQclear (result); - return GNUNET_SYSERR; - } - nrows = PQntuples (result); - if (0 == nrows) - { - /* no matches found */ - PQclear (result); - return GNUNET_NO; - } - if (1 != nrows) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - if (GNUNET_OK != - GNUNET_PQ_extract_result (result, - rs, - 0)) - { - PQclear (result); - GNUNET_break (0); - return GNUNET_SYSERR; - } - PQclear (result); - return GNUNET_OK; + return GNUNET_PQ_eval_prepared_singleton_select (session->conn, + "get_wire_fee", + params, + rs); } diff --git a/src/exchangedb/test_exchangedb.c b/src/exchangedb/test_exchangedb.c index 380b037cd..5c33418e1 100644 --- a/src/exchangedb/test_exchangedb.c +++ b/src/exchangedb/test_exchangedb.c @@ -1185,7 +1185,7 @@ test_wire_fees (struct TALER_EXCHANGEDB_Session *session) } /* This must fail as 'end_date' is NOT in the half-open interval [start_date,end_date) */ - if (GNUNET_OK == + if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != plugin->get_wire_fee (plugin->cls, session, "wire-method", @@ -1198,7 +1198,7 @@ test_wire_fees (struct TALER_EXCHANGEDB_Session *session) GNUNET_break (0); return GNUNET_SYSERR; } - if (GNUNET_OK != + if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != plugin->get_wire_fee (plugin->cls, session, "wire-method", @@ -1302,7 +1302,7 @@ test_wire_out (struct TALER_EXCHANGEDB_Session *session, TALER_amount_subtract (&transfer_value_wt, &coin_value_wt, &coin_fee_wt)); - FAILIF (GNUNET_NO != + FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != plugin->lookup_wire_transfer (plugin->cls, session, &wire_out_wtid, @@ -1344,7 +1344,7 @@ test_wire_out (struct TALER_EXCHANGEDB_Session *session, plugin->commit (plugin->cls, session)); - FAILIF (GNUNET_OK != + FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != plugin->lookup_wire_transfer (plugin->cls, session, &wire_out_wtid, diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h index 7045adbbd..8795811a2 100644 --- a/src/include/taler_exchangedb_plugin.h +++ b/src/include/taler_exchangedb_plugin.h @@ -1731,10 +1731,9 @@ struct TALER_EXCHANGEDB_Plugin * @param wtid the raw wire transfer identifier we used * @param cb function to call on each transaction found * @param cb_cls closure for @a cb - * @return #GNUNET_OK on success, #GNUNET_SYSERR on database errors, - * #GNUNET_NO if we found no results + * @return query status of the transaction */ - int + enum GNUNET_DB_QueryStatus (*lookup_wire_transfer) (void *cls, struct TALER_EXCHANGEDB_Session *session, const struct TALER_WireTransferIdentifierRawP *wtid, @@ -1822,10 +1821,9 @@ struct TALER_EXCHANGEDB_Plugin * @param[out] end_date when does the fee end being valid * @param[out] wire_fee how high is the wire transfer fee * @param[out] master_sig signature over the above by the exchange master key - * @return #GNUNET_OK on success, #GNUNET_NO if no fee is known - * #GNUNET_SYSERR on failure + * @return query status of the transaction */ - int + enum GNUNET_DB_QueryStatus (*get_wire_fee) (void *cls, struct TALER_EXCHANGEDB_Session *session, const char *type, -- cgit v1.2.3