summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2017-06-18 22:48:54 +0200
committerChristian Grothoff <christian@grothoff.org>2017-06-19 00:17:15 +0200
commit2d5b238beb4661721afddd3c76262837d13ee8c4 (patch)
tree9bab009c0e95d990612287bb141d565004e42c0d /src
parent2ec1b055a0f574e3837f2fbbb38098db4eb65f6f (diff)
downloadexchange-2d5b238beb4661721afddd3c76262837d13ee8c4.tar.gz
exchange-2d5b238beb4661721afddd3c76262837d13ee8c4.tar.bz2
exchange-2d5b238beb4661721afddd3c76262837d13ee8c4.zip
rework /reserve/history to address #5010
Diffstat (limited to 'src')
-rw-r--r--src/exchange/Makefile.am1
-rw-r--r--src/exchange/taler-exchange-httpd_db.c49
-rw-r--r--src/exchange/taler-exchange-httpd_db.h13
-rw-r--r--src/exchange/taler-exchange-httpd_reserve.c36
-rw-r--r--src/exchange/taler-exchange-httpd_reserve_status.c137
-rw-r--r--src/exchange/taler-exchange-httpd_reserve_status.h49
-rw-r--r--src/exchangedb/perf_taler_exchangedb_interpreter.c9
-rw-r--r--src/exchangedb/plugin_exchangedb_postgres.c577
-rw-r--r--src/exchangedb/test_exchangedb.c7
-rw-r--r--src/include/taler_exchangedb_plugin.h8
10 files changed, 531 insertions, 355 deletions
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
@@ -553,47 +553,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.
*
* @param connection request we are handling
@@ -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
@@ -98,19 +98,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);
-
-
-/**
* Execute a "/reserve/withdraw". Given a reserve and a properly signed
* request to withdraw a coin, check the balance of the reserve and
* if it is sufficient, store the request and return the signed
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
@@ -30,42 +30,6 @@
/**
- * 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
* specifies the key/value of the coin to be withdrawn, and checks
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 <http://www.gnu.org/licenses/>
+*/
+/**
+ * @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 <gnunet/gnunet_util_lib.h>
+#include <jansson.h>
+#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 <http://www.gnu.org/licenses/>
+*/
+/**
+ * @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 <microhttpd.h>
+#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);
/**