From 2d5b238beb4661721afddd3c76262837d13ee8c4 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sun, 18 Jun 2017 22:48:54 +0200 Subject: rework /reserve/history to address #5010 --- src/exchange/Makefile.am | 1 + src/exchange/taler-exchange-httpd_db.c | 49 +- src/exchange/taler-exchange-httpd_db.h | 13 - src/exchange/taler-exchange-httpd_reserve.c | 36 -- src/exchange/taler-exchange-httpd_reserve_status.c | 137 +++++ src/exchange/taler-exchange-httpd_reserve_status.h | 49 ++ src/exchangedb/perf_taler_exchangedb_interpreter.c | 9 +- src/exchangedb/plugin_exchangedb_postgres.c | 577 ++++++++++++--------- src/exchangedb/test_exchangedb.c | 7 +- src/include/taler_exchangedb_plugin.h | 8 +- 10 files changed, 531 insertions(+), 355 deletions(-) create mode 100644 src/exchange/taler-exchange-httpd_reserve_status.c create mode 100644 src/exchange/taler-exchange-httpd_reserve_status.h (limited to 'src') diff --git a/src/exchange/Makefile.am b/src/exchange/Makefile.am index 92a8556a0..96e9d7aa2 100644 --- a/src/exchange/Makefile.am +++ b/src/exchange/Makefile.am @@ -53,6 +53,7 @@ taler_exchange_httpd_SOURCES = \ taler-exchange-httpd_refresh.c taler-exchange-httpd_refresh.h \ taler-exchange-httpd_refund.c taler-exchange-httpd_refund.h \ taler-exchange-httpd_reserve.c taler-exchange-httpd_reserve.h \ + taler-exchange-httpd_reserve_status.c taler-exchange-httpd_reserve_status.h \ taler-exchange-httpd_responses.c taler-exchange-httpd_responses.h \ taler-exchange-httpd_tracking.c taler-exchange-httpd_tracking.h \ taler-exchange-httpd_wire.c taler-exchange-httpd_wire.h \ diff --git a/src/exchange/taler-exchange-httpd_db.c b/src/exchange/taler-exchange-httpd_db.c index 2cdf61d8d..4131e1230 100644 --- a/src/exchange/taler-exchange-httpd_db.c +++ b/src/exchange/taler-exchange-httpd_db.c @@ -552,47 +552,6 @@ TEH_DB_execute_refund (struct MHD_Connection *connection, } -/** - * Execute a /reserve/status. Given the public key of a reserve, - * return the associated transaction history. - * - * @param connection the MHD connection to handle - * @param reserve_pub public key of the reserve to check - * @return MHD result code - */ -int -TEH_DB_execute_reserve_status (struct MHD_Connection *connection, - const struct TALER_ReservePublicKeyP *reserve_pub) -{ - struct TALER_EXCHANGEDB_Session *session; - struct TALER_EXCHANGEDB_ReserveHistory *rh; - int res; - - if (NULL == (session = TEH_plugin->get_session (TEH_plugin->cls))) - { - GNUNET_break (0); - return TEH_RESPONSE_reply_internal_db_error (connection, - TALER_EC_DB_SETUP_FAILED); - } - START_TRANSACTION (session, connection); - rh = TEH_plugin->get_reserve_history (TEH_plugin->cls, - session, - reserve_pub); - COMMIT_TRANSACTION (session, connection); - if (NULL == rh) - return TEH_RESPONSE_reply_json_pack (connection, - MHD_HTTP_NOT_FOUND, - "{s:s, s:s}", - "error", "Reserve not found", - "parameter", "withdraw_pub"); - res = TEH_RESPONSE_reply_reserve_status_success (connection, - rh); - TEH_plugin->free_reserve_history (TEH_plugin->cls, - rh); - return res; -} - - /** * Try to execute /reserve/withdraw transaction. * @@ -635,12 +594,16 @@ execute_reserve_withdraw_transaction (struct MHD_Connection *connection, struct TALER_Amount fee_withdraw; int res; int ret; + enum GNUNET_DB_QueryStatus qs; /* Check if balance is sufficient */ START_TRANSACTION (session, connection); - rh = TEH_plugin->get_reserve_history (TEH_plugin->cls, + qs = TEH_plugin->get_reserve_history (TEH_plugin->cls, session, - reserve); + reserve, + &rh); + (void) qs; + /* qs: #5010! */ if (NULL == rh) { TEH_plugin->rollback (TEH_plugin->cls, diff --git a/src/exchange/taler-exchange-httpd_db.h b/src/exchange/taler-exchange-httpd_db.h index 6d0b7e35a..0834db56c 100644 --- a/src/exchange/taler-exchange-httpd_db.h +++ b/src/exchange/taler-exchange-httpd_db.h @@ -97,19 +97,6 @@ TEH_DB_execute_refund (struct MHD_Connection *connection, const struct TALER_EXCHANGEDB_Refund *refund); -/** - * Execute a "/reserve/status". Given the public key of a reserve, - * return the associated transaction history. - * - * @param connection the MHD connection to handle - * @param reserve_pub public key of the reserve to check - * @return MHD result code - */ -int -TEH_DB_execute_reserve_status (struct MHD_Connection *connection, - const struct TALER_ReservePublicKeyP *reserve_pub); - - /** * Execute a "/reserve/withdraw". Given a reserve and a properly signed * request to withdraw a coin, check the balance of the reserve and diff --git a/src/exchange/taler-exchange-httpd_reserve.c b/src/exchange/taler-exchange-httpd_reserve.c index 78f8ff1d0..08c904c54 100644 --- a/src/exchange/taler-exchange-httpd_reserve.c +++ b/src/exchange/taler-exchange-httpd_reserve.c @@ -29,42 +29,6 @@ #include "taler-exchange-httpd_keystate.h" -/** - * Handle a "/reserve/status" request. Parses the - * given "reserve_pub" argument (which should contain the - * EdDSA public key of a reserve) and then respond with the - * status of the reserve. - * - * @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 - * @return MHD result code - */ -int -TEH_RESERVE_handler_reserve_status (struct TEH_RequestHandler *rh, - struct MHD_Connection *connection, - void **connection_cls, - const char *upload_data, - size_t *upload_data_size) -{ - struct TALER_ReservePublicKeyP reserve_pub; - int res; - - res = TEH_PARSE_mhd_request_arg_data (connection, - "reserve_pub", - &reserve_pub, - sizeof (struct TALER_ReservePublicKeyP)); - if (GNUNET_SYSERR == res) - return MHD_NO; /* internal error */ - if (GNUNET_NO == res) - return MHD_YES; /* parse error */ - return TEH_DB_execute_reserve_status (connection, - &reserve_pub); -} - - /** * Handle a "/reserve/withdraw" request. Parses the "reserve_pub" * EdDSA key of the reserve and the requested "denom_pub" which diff --git a/src/exchange/taler-exchange-httpd_reserve_status.c b/src/exchange/taler-exchange-httpd_reserve_status.c new file mode 100644 index 000000000..e13b8f393 --- /dev/null +++ b/src/exchange/taler-exchange-httpd_reserve_status.c @@ -0,0 +1,137 @@ +/* + This file is part of TALER + Copyright (C) 2014, 2015, 2016 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 + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, see +*/ +/** + * @file taler-exchange-httpd_reserve_status.c + * @brief Handle /reserve/status requests + * @author Florian Dold + * @author Benedikt Mueller + * @author Christian Grothoff + */ +#include "platform.h" +#include +#include +#include "taler-exchange-httpd_reserve_status.h" +#include "taler-exchange-httpd_parsing.h" +#include "taler-exchange-httpd_responses.h" +#include "taler-exchange-httpd_keystate.h" + + +/** + * Closure for #reserve_status_transaction. + */ +struct ReserveStatusContext +{ + /** + * Public key of the reserve the inquiry is about. + */ + struct TALER_ReservePublicKeyP reserve_pub; + + /** + * History of the reserve, set in the callback. + */ + struct TALER_EXCHANGEDB_ReserveHistory *rh; + +}; + + +/** + * Function implementing /reserve/status transaction. + * Execute a /reserve/status. Given the public key of a reserve, + * return the associated transaction history. Runs the + * transaction logic; 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 cls a `struct ReserveStatusContext *` + * @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 + */ +static enum GNUNET_DB_QueryStatus +reserve_status_transaction (void *cls, + struct MHD_Connection *connection, + struct TALER_EXCHANGEDB_Session *session, + int *mhd_ret) +{ + struct ReserveStatusContext *rsc = cls; + + return TEH_plugin->get_reserve_history (TEH_plugin->cls, + session, + &rsc->reserve_pub, + &rsc->rh); +} + + +/** + * Handle a "/reserve/status" request. Parses the + * given "reserve_pub" argument (which should contain the + * EdDSA public key of a reserve) and then respond with the + * status of the reserve. + * + * @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 + * @return MHD result code + */ +int +TEH_RESERVE_handler_reserve_status (struct TEH_RequestHandler *rh, + struct MHD_Connection *connection, + void **connection_cls, + const char *upload_data, + size_t *upload_data_size) +{ + struct ReserveStatusContext rsc; + int res; + int mhd_ret; + + res = TEH_PARSE_mhd_request_arg_data (connection, + "reserve_pub", + &rsc.reserve_pub, + sizeof (struct TALER_ReservePublicKeyP)); + if (GNUNET_SYSERR == res) + return MHD_NO; /* internal error */ + if (GNUNET_NO == res) + return MHD_YES; /* parse error */ + rsc.rh = NULL; + if (GNUNET_OK != + TEH_DB_run_transaction (connection, + &mhd_ret, + &reserve_status_transaction, + &rsc)) + return mhd_ret; + + /* generate proper response */ + if (NULL == rsc.rh) + return TEH_RESPONSE_reply_json_pack (connection, + MHD_HTTP_NOT_FOUND, + "{s:s, s:s}", + "error", "Reserve not found", + "parameter", "withdraw_pub"); + mhd_ret = TEH_RESPONSE_reply_reserve_status_success (connection, + rsc.rh); + TEH_plugin->free_reserve_history (TEH_plugin->cls, + rsc.rh); + return mhd_ret; +} + + +/* end of taler-exchange-httpd_reserve_status.c */ diff --git a/src/exchange/taler-exchange-httpd_reserve_status.h b/src/exchange/taler-exchange-httpd_reserve_status.h new file mode 100644 index 000000000..7bfd4dd83 --- /dev/null +++ b/src/exchange/taler-exchange-httpd_reserve_status.h @@ -0,0 +1,49 @@ +/* + This file is part of TALER + 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 + Foundation; either version 3, or (at your option) any later version. + + TALER is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, see +*/ +/** + * @file taler-exchange-httpd_reserve_status.h + * @brief Handle /reserve/status requests + * @author Florian Dold + * @author Benedikt Mueller + * @author Christian Grothoff + */ +#ifndef TALER_EXCHANGE_HTTPD_RESERVE_STATUS_H +#define TALER_EXCHANGE_HTTPD_RESERVE_STATUS_H + +#include +#include "taler-exchange-httpd.h" + +/** + * Handle a "/reserve/status" request. Parses the + * given "reserve_pub" argument (which should contain the + * EdDSA public key of a reserve) and then respond with the + * status of the reserve. + * + * @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 + * @return MHD result code + */ +int +TEH_RESERVE_handler_reserve_status (struct TEH_RequestHandler *rh, + struct MHD_Connection *connection, + void **connection_cls, + const char *upload_data, + size_t *upload_data_size); + +#endif diff --git a/src/exchangedb/perf_taler_exchangedb_interpreter.c b/src/exchangedb/perf_taler_exchangedb_interpreter.c index 054df58db..b5845411e 100644 --- a/src/exchangedb/perf_taler_exchangedb_interpreter.c +++ b/src/exchangedb/perf_taler_exchangedb_interpreter.c @@ -1422,12 +1422,15 @@ interpret (struct PERF_TALER_EXCHANGEDB_interpreter_state *state) unsigned int reserve_index; struct TALER_EXCHANGEDB_ReserveHistory *history; struct PERF_TALER_EXCHANGEDB_Data *data; + enum GNUNET_DB_QueryStatus qs; reserve_index = state->cmd[state->i].details.get_reserve_history.index_reserve; data = &state->cmd[reserve_index].exposed; - history = state->plugin->get_reserve_history (state->plugin->cls, - state->session, - &data->data.reserve->reserve.pub); + qs = state->plugin->get_reserve_history (state->plugin->cls, + state->session, + &data->data.reserve->reserve.pub, + &history); + GNUNET_assert (0 >= qs); GNUNET_assert (NULL != history); state->plugin->free_reserve_history (state->plugin->cls, history); diff --git a/src/exchangedb/plugin_exchangedb_postgres.c b/src/exchangedb/plugin_exchangedb_postgres.c index 954fca389..0f18ef587 100644 --- a/src/exchangedb/plugin_exchangedb_postgres.c +++ b/src/exchangedb/plugin_exchangedb_postgres.c @@ -2393,6 +2393,7 @@ postgres_insert_withdraw_info (void *cls, if (0 >= qs) { GNUNET_break (0); + return GNUNET_SYSERR; } return GNUNET_OK; @@ -2400,295 +2401,361 @@ postgres_insert_withdraw_info (void *cls, /** - * Get all of the transaction history associated with the specified - * reserve. - * - * @param cls the `struct PostgresClosure` with the plugin-specific state - * @param session connection to use - * @param reserve_pub public key of the reserve - * @return known transaction history (NULL if reserve is unknown) + * Closure for callbacks invoked via #postgres_get_reserve_history. */ -static struct TALER_EXCHANGEDB_ReserveHistory * -postgres_get_reserve_history (void *cls, - struct TALER_EXCHANGEDB_Session *session, - const struct TALER_ReservePublicKeyP *reserve_pub) +struct ReserveHistoryContext { - PGresult *result; + + /** + * Which reserve are we building the history for? + */ + const struct TALER_ReservePublicKeyP *reserve_pub; + + /** + * Where we build the history. + */ struct TALER_EXCHANGEDB_ReserveHistory *rh; + + /** + * Tail of @e rh list. + */ struct TALER_EXCHANGEDB_ReserveHistory *rh_tail; - int rows; - int ret; - rh = NULL; - rh_tail = NULL; - ret = GNUNET_SYSERR; - /** #TALER_EXCHANGEDB_RO_BANK_TO_EXCHANGE */ + /** + * Set to #GNUNET_SYSERR on serious internal errors during + * the callbacks. + */ + int status; +}; + + +/** + * Append and return a fresh element to the reserve + * history kept in @a rhc. + * + * @param rhc where the history is kept + * @return the fresh element that was added + */ +static struct TALER_EXCHANGEDB_ReserveHistory * +append_rh (struct ReserveHistoryContext *rhc) +{ + struct TALER_EXCHANGEDB_ReserveHistory *tail; + + tail = GNUNET_new (struct TALER_EXCHANGEDB_ReserveHistory); + if (NULL != rhc->rh_tail) + { + rhc->rh_tail->next = tail; + rhc->rh_tail = tail; + } + else + { + rhc->rh_tail = tail; + rhc->rh = tail; + } + return tail; +} + + +/** + * Add bank transfers to result set for #postgres_get_reserve_history. + * + * @param cls a `struct ReserveHistoryContext *` + * @param result SQL result + * @param num_results number of rows in @a result + */ +static void +add_bank_to_exchange (void *cls, + PGresult *result, + unsigned int num_results) +{ + struct ReserveHistoryContext *rhc = cls; + + while (0 < num_results) { struct TALER_EXCHANGEDB_BankTransfer *bt; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (reserve_pub), - GNUNET_PQ_query_param_end - }; + struct TALER_EXCHANGEDB_ReserveHistory *tail; - result = GNUNET_PQ_exec_prepared (session->conn, - "reserves_in_get_transactions", - params); - if (PGRES_TUPLES_OK != PQresultStatus (result)) - { - QUERY_ERR (result, session->conn); - goto cleanup; - } - if (0 == (rows = PQntuples (result))) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Asked to fetch history for an unknown reserve.\n"); - goto cleanup; - } - while (0 < rows) + bt = GNUNET_new (struct TALER_EXCHANGEDB_BankTransfer); { - bt = GNUNET_new (struct TALER_EXCHANGEDB_BankTransfer); - { - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_variable_size ("wire_reference", - &bt->wire_reference, - &bt->wire_reference_size), - TALER_PQ_result_spec_amount ("credit", - &bt->amount), - GNUNET_PQ_result_spec_absolute_time ("execution_date", - &bt->execution_date), - TALER_PQ_result_spec_json ("sender_account_details", - &bt->sender_account_details), - GNUNET_PQ_result_spec_end - }; - if (GNUNET_OK != - GNUNET_PQ_extract_result (result, - rs, - --rows)) - { - GNUNET_break (0); - GNUNET_free (bt); - PQclear (result); - goto cleanup; - } - } - bt->reserve_pub = *reserve_pub; - if (NULL != rh_tail) - { - rh_tail->next = GNUNET_new (struct TALER_EXCHANGEDB_ReserveHistory); - rh_tail = rh_tail->next; - } - else + struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_variable_size ("wire_reference", + &bt->wire_reference, + &bt->wire_reference_size), + TALER_PQ_result_spec_amount ("credit", + &bt->amount), + GNUNET_PQ_result_spec_absolute_time ("execution_date", + &bt->execution_date), + TALER_PQ_result_spec_json ("sender_account_details", + &bt->sender_account_details), + GNUNET_PQ_result_spec_end + }; + + if (GNUNET_OK != + GNUNET_PQ_extract_result (result, + rs, + --num_results)) { - rh_tail = GNUNET_new (struct TALER_EXCHANGEDB_ReserveHistory); - rh = rh_tail; + GNUNET_break (0); + GNUNET_free (bt); + rhc->status = GNUNET_SYSERR; + return; } - rh_tail->type = TALER_EXCHANGEDB_RO_BANK_TO_EXCHANGE; - rh_tail->details.bank = bt; - } /* end of 'while (0 < rows)' */ - PQclear (result); - } - /** #TALER_EXCHANGEDB_RO_WITHDRAW_COIN */ - { - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (reserve_pub), - GNUNET_PQ_query_param_end - }; - - GNUNET_assert (NULL != rh); - GNUNET_assert (NULL != rh_tail); - GNUNET_assert (NULL == rh_tail->next); - result = GNUNET_PQ_exec_prepared (session->conn, - "get_reserves_out", - params); - if (PGRES_TUPLES_OK != PQresultStatus (result)) - { - QUERY_ERR (result, session->conn); - PQclear (result); - goto cleanup; } - rows = PQntuples (result); - while (0 < rows) - { - struct TALER_EXCHANGEDB_CollectableBlindcoin *cbc; + bt->reserve_pub = *rhc->reserve_pub; + tail = append_rh (rhc); + tail->type = TALER_EXCHANGEDB_RO_BANK_TO_EXCHANGE; + tail->details.bank = bt; + } /* end of 'while (0 < rows)' */ +} - cbc = GNUNET_new (struct TALER_EXCHANGEDB_CollectableBlindcoin); - { - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_auto_from_type ("h_blind_ev", - &cbc->h_coin_envelope), - GNUNET_PQ_result_spec_rsa_public_key ("denom_pub", - &cbc->denom_pub.rsa_public_key), - GNUNET_PQ_result_spec_rsa_signature ("denom_sig", - &cbc->sig.rsa_signature), - GNUNET_PQ_result_spec_auto_from_type ("reserve_sig", - &cbc->reserve_sig), - TALER_PQ_result_spec_amount ("amount_with_fee", - &cbc->amount_with_fee), - TALER_PQ_result_spec_amount ("fee_withdraw", - &cbc->withdraw_fee), - GNUNET_PQ_result_spec_end - }; - if (GNUNET_OK != - GNUNET_PQ_extract_result (result, - rs, - --rows)) - { - GNUNET_break (0); - GNUNET_free (cbc); - PQclear (result); - goto cleanup; - } - cbc->reserve_pub = *reserve_pub; - } - rh_tail->next = GNUNET_new (struct TALER_EXCHANGEDB_ReserveHistory); - rh_tail = rh_tail->next; - rh_tail->type = TALER_EXCHANGEDB_RO_WITHDRAW_COIN; - rh_tail->details.withdraw = cbc; - } /* end of 'while (0 < rows)' */ - ret = GNUNET_OK; - PQclear (result); - } - /** #TALER_EXCHANGEDB_RO_PAYBACK_COIN */ +/** + * Add coin withdrawals to result set for #postgres_get_reserve_history. + * + * @param cls a `struct ReserveHistoryContext *` + * @param result SQL result + * @param num_results number of rows in @a result + */ +static void +add_withdraw_coin (void *cls, + PGresult *result, + unsigned int num_results) +{ + struct ReserveHistoryContext *rhc = cls; + + while (0 < num_results) { - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (reserve_pub), - GNUNET_PQ_query_param_end - }; + struct TALER_EXCHANGEDB_CollectableBlindcoin *cbc; + struct TALER_EXCHANGEDB_ReserveHistory *tail; - result = GNUNET_PQ_exec_prepared (session->conn, - "payback_by_reserve", - params); - if (PGRES_TUPLES_OK != PQresultStatus (result)) - { - QUERY_ERR (result, session->conn); - goto cleanup; - } - rows = PQntuples (result); - while (0 < rows) + cbc = GNUNET_new (struct TALER_EXCHANGEDB_CollectableBlindcoin); { - struct TALER_EXCHANGEDB_Payback *payback; - - payback = GNUNET_new (struct TALER_EXCHANGEDB_Payback); - { - struct GNUNET_PQ_ResultSpec rs[] = { - TALER_PQ_result_spec_amount ("amount", - &payback->value), - GNUNET_PQ_result_spec_auto_from_type ("coin_pub", - &payback->coin.coin_pub), - GNUNET_PQ_result_spec_auto_from_type ("coin_blind", - &payback->coin_blind), - GNUNET_PQ_result_spec_auto_from_type ("coin_sig", - &payback->coin_sig), - GNUNET_PQ_result_spec_absolute_time ("timestamp", - &payback->timestamp), - GNUNET_PQ_result_spec_rsa_public_key ("denom_pub", - &payback->coin.denom_pub.rsa_public_key), - GNUNET_PQ_result_spec_rsa_signature ("denom_sig", - &payback->coin.denom_sig.rsa_signature), - GNUNET_PQ_result_spec_end - }; - if (GNUNET_OK != - GNUNET_PQ_extract_result (result, - rs, - --rows)) - { - GNUNET_break (0); - GNUNET_free (payback); - PQclear (result); - goto cleanup; - } - } - payback->reserve_pub = *reserve_pub; - if (NULL != rh_tail) - { - rh_tail->next = GNUNET_new (struct TALER_EXCHANGEDB_ReserveHistory); - rh_tail = rh_tail->next; - } - else + struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_auto_from_type ("h_blind_ev", + &cbc->h_coin_envelope), + GNUNET_PQ_result_spec_rsa_public_key ("denom_pub", + &cbc->denom_pub.rsa_public_key), + GNUNET_PQ_result_spec_rsa_signature ("denom_sig", + &cbc->sig.rsa_signature), + GNUNET_PQ_result_spec_auto_from_type ("reserve_sig", + &cbc->reserve_sig), + TALER_PQ_result_spec_amount ("amount_with_fee", + &cbc->amount_with_fee), + TALER_PQ_result_spec_amount ("fee_withdraw", + &cbc->withdraw_fee), + GNUNET_PQ_result_spec_end + }; + + if (GNUNET_OK != + GNUNET_PQ_extract_result (result, + rs, + --num_results)) { - rh_tail = GNUNET_new (struct TALER_EXCHANGEDB_ReserveHistory); - rh = rh_tail; + GNUNET_break (0); + GNUNET_free (cbc); + rhc->status = GNUNET_SYSERR; + return; } - rh_tail->type = TALER_EXCHANGEDB_RO_PAYBACK_COIN; - rh_tail->details.payback = payback; - } /* end of 'while (0 < rows)' */ - PQclear (result); + } + cbc->reserve_pub = *rhc->reserve_pub; + tail = append_rh (rhc); + tail->type = TALER_EXCHANGEDB_RO_WITHDRAW_COIN; + tail->details.withdraw = cbc; } +} - /** #TALER_EXCHANGEDB_RO_EXCHANGE_TO_BANK */ +/** + * Add paybacks to result set for #postgres_get_reserve_history. + * + * @param cls a `struct ReserveHistoryContext *` + * @param result SQL result + * @param num_results number of rows in @a result + */ +static void +add_payback (void *cls, + PGresult *result, + unsigned int num_results) +{ + struct ReserveHistoryContext *rhc = cls; + + while (0 < num_results) { - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_auto_from_type (reserve_pub), - GNUNET_PQ_query_param_end - }; + struct TALER_EXCHANGEDB_Payback *payback; + struct TALER_EXCHANGEDB_ReserveHistory *tail; - result = GNUNET_PQ_exec_prepared (session->conn, - "close_by_reserve", - params); - if (PGRES_TUPLES_OK != PQresultStatus (result)) - { - QUERY_ERR (result, session->conn); - goto cleanup; - } - rows = PQntuples (result); - while (0 < rows) + payback = GNUNET_new (struct TALER_EXCHANGEDB_Payback); { - struct TALER_EXCHANGEDB_ClosingTransfer *closing; - - closing = GNUNET_new (struct TALER_EXCHANGEDB_ClosingTransfer); - { - struct GNUNET_PQ_ResultSpec rs[] = { - TALER_PQ_result_spec_amount ("amount", - &closing->amount), - TALER_PQ_result_spec_amount ("closing_fee", - &closing->closing_fee), - GNUNET_PQ_result_spec_absolute_time ("execution_date", - &closing->execution_date), - TALER_PQ_result_spec_json ("receiver_account", - &closing->receiver_account_details), - GNUNET_PQ_result_spec_auto_from_type ("wtid", - &closing->wtid), - GNUNET_PQ_result_spec_end - }; - if (GNUNET_OK != - GNUNET_PQ_extract_result (result, - rs, - --rows)) - { - GNUNET_break (0); - GNUNET_free (closing); - PQclear (result); - goto cleanup; - } - } - closing->reserve_pub = *reserve_pub; - if (NULL != rh_tail) + struct GNUNET_PQ_ResultSpec rs[] = { + TALER_PQ_result_spec_amount ("amount", + &payback->value), + GNUNET_PQ_result_spec_auto_from_type ("coin_pub", + &payback->coin.coin_pub), + GNUNET_PQ_result_spec_auto_from_type ("coin_blind", + &payback->coin_blind), + GNUNET_PQ_result_spec_auto_from_type ("coin_sig", + &payback->coin_sig), + GNUNET_PQ_result_spec_absolute_time ("timestamp", + &payback->timestamp), + GNUNET_PQ_result_spec_rsa_public_key ("denom_pub", + &payback->coin.denom_pub.rsa_public_key), + GNUNET_PQ_result_spec_rsa_signature ("denom_sig", + &payback->coin.denom_sig.rsa_signature), + GNUNET_PQ_result_spec_end + }; + + if (GNUNET_OK != + GNUNET_PQ_extract_result (result, + rs, + --num_results)) { - rh_tail->next = GNUNET_new (struct TALER_EXCHANGEDB_ReserveHistory); - rh_tail = rh_tail->next; + GNUNET_break (0); + GNUNET_free (payback); + rhc->status = GNUNET_SYSERR; + return; } - else + } + payback->reserve_pub = *rhc->reserve_pub; + tail = append_rh (rhc); + tail->type = TALER_EXCHANGEDB_RO_PAYBACK_COIN; + tail->details.payback = payback; + } /* end of 'while (0 < rows)' */ +} + + +/** + * Add exchange-to-bank transfers to result set for + * #postgres_get_reserve_history. + * + * @param cls a `struct ReserveHistoryContext *` + * @param result SQL result + * @param num_results number of rows in @a result + */ +static void +add_exchange_to_bank (void *cls, + PGresult *result, + unsigned int num_results) +{ + struct ReserveHistoryContext *rhc = cls; + + while (0 < num_results) + { + struct TALER_EXCHANGEDB_ClosingTransfer *closing; + struct TALER_EXCHANGEDB_ReserveHistory *tail; + + closing = GNUNET_new (struct TALER_EXCHANGEDB_ClosingTransfer); + { + struct GNUNET_PQ_ResultSpec rs[] = { + TALER_PQ_result_spec_amount ("amount", + &closing->amount), + TALER_PQ_result_spec_amount ("closing_fee", + &closing->closing_fee), + GNUNET_PQ_result_spec_absolute_time ("execution_date", + &closing->execution_date), + TALER_PQ_result_spec_json ("receiver_account", + &closing->receiver_account_details), + GNUNET_PQ_result_spec_auto_from_type ("wtid", + &closing->wtid), + GNUNET_PQ_result_spec_end + }; + + if (GNUNET_OK != + GNUNET_PQ_extract_result (result, + rs, + --num_results)) { - rh_tail = GNUNET_new (struct TALER_EXCHANGEDB_ReserveHistory); - rh = rh_tail; + GNUNET_break (0); + GNUNET_free (closing); + rhc->status = GNUNET_SYSERR; + return; } - rh_tail->type = TALER_EXCHANGEDB_RO_EXCHANGE_TO_BANK; - rh_tail->details.closing = closing; - } /* end of 'while (0 < rows)' */ - PQclear (result); - } + } + closing->reserve_pub = *rhc->reserve_pub; + tail = append_rh (rhc); + tail->type = TALER_EXCHANGEDB_RO_EXCHANGE_TO_BANK; + tail->details.closing = closing; + } /* end of 'while (0 < rows)' */ +} - cleanup: - if (GNUNET_SYSERR == ret) +/** + * Get all of the transaction history associated with the specified + * reserve. + * + * @param cls the `struct PostgresClosure` with the plugin-specific state + * @param session connection to use + * @param reserve_pub public key of the reserve + * @param[out] rhp set to known transaction history (NULL if reserve is unknown) + * @return transaction status + */ +static enum GNUNET_DB_QueryStatus +postgres_get_reserve_history (void *cls, + struct TALER_EXCHANGEDB_Session *session, + const struct TALER_ReservePublicKeyP *reserve_pub, + struct TALER_EXCHANGEDB_ReserveHistory **rhp) +{ + struct ReserveHistoryContext rhc; + struct { + /** + * Name of the prepared statement to run. + */ + const char *statement; + /** + * Function to use to process the results. + */ + GNUNET_PQ_PostgresResultHandler cb; + } work[] = { + /** #TALER_EXCHANGEDB_RO_BANK_TO_EXCHANGE */ + { "reserves_in_get_transactions", + add_bank_to_exchange }, + /** #TALER_EXCHANGEDB_RO_WITHDRAW_COIN */ + { "get_reserves_out", + &add_withdraw_coin }, + /** #TALER_EXCHANGEDB_RO_PAYBACK_COIN */ + { "payback_by_reserve", + &add_payback }, + /** #TALER_EXCHANGEDB_RO_EXCHANGE_TO_BANK */ + { "close_by_reserve", + &add_exchange_to_bank }, + /* List terminator */ + { NULL, + NULL } + }; + enum GNUNET_DB_QueryStatus qs; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_auto_from_type (reserve_pub), + GNUNET_PQ_query_param_end + }; + + rhc.reserve_pub = reserve_pub; + rhc.rh = NULL; + rhc.rh_tail = NULL; + rhc.status = GNUNET_OK; + for (unsigned int i=0;NULL != work[i].cb;i++) + { + qs = GNUNET_PQ_eval_prepared_multi_select (session->conn, + work[i].statement, + params, + work[i].cb, + &rhc); + if ( (0 > qs) || + (GNUNET_OK != rhc.status) ) + break; + } + if ( (qs < 0) || + (rhc.status != GNUNET_OK) ) { common_free_reserve_history (cls, - rh); - rh = NULL; + rhc.rh); + rhc.rh = NULL; + if (qs >= 0) + { + /* status == SYSERR is a very hard error... */ + qs = GNUNET_DB_STATUS_HARD_ERROR; + } } - return rh; + *rhp = rhc.rh; + return qs; } diff --git a/src/exchangedb/test_exchangedb.c b/src/exchangedb/test_exchangedb.c index ba47c2d3c..8c7e4b9c8 100644 --- a/src/exchangedb/test_exchangedb.c +++ b/src/exchangedb/test_exchangedb.c @@ -1447,6 +1447,7 @@ run (void *cls) unsigned int cnt; void *rr; size_t rr_size; + enum GNUNET_DB_QueryStatus qs; dkp = NULL; rh = NULL; @@ -1671,9 +1672,11 @@ run (void *cls) json_decref (sndr); result = 7; - rh = plugin->get_reserve_history (plugin->cls, + qs = plugin->get_reserve_history (plugin->cls, session, - &reserve_pub); + &reserve_pub, + &rh); + FAILIF (0 > qs); FAILIF (NULL == rh); rh_head = rh; for (cnt=0; NULL != rh_head; rh_head=rh_head->next, cnt++) diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h index 2cf553b17..843c62720 100644 --- a/src/include/taler_exchangedb_plugin.h +++ b/src/include/taler_exchangedb_plugin.h @@ -1268,12 +1268,14 @@ struct TALER_EXCHANGEDB_Plugin * @param cls the @e cls of this struct with the plugin-specific state * @param session connection to use * @param reserve_pub public key of the reserve - * @return known transaction history (NULL if reserve is unknown) + * @param[out] rhp set to known transaction history (NULL if reserve is unknown) + * @return transaction status */ - struct TALER_EXCHANGEDB_ReserveHistory * + enum GNUNET_DB_QueryStatus (*get_reserve_history) (void *cls, struct TALER_EXCHANGEDB_Session *session, - const struct TALER_ReservePublicKeyP *reserve_pub); + const struct TALER_ReservePublicKeyP *reserve_pub, + struct TALER_EXCHANGEDB_ReserveHistory **rhp); /** -- cgit v1.2.3