diff options
| -rw-r--r-- | src/exchange/Makefile.am | 1 | ||||
| -rw-r--r-- | src/exchange/taler-exchange-httpd_db.c | 49 | ||||
| -rw-r--r-- | src/exchange/taler-exchange-httpd_db.h | 13 | ||||
| -rw-r--r-- | src/exchange/taler-exchange-httpd_reserve.c | 36 | ||||
| -rw-r--r-- | src/exchange/taler-exchange-httpd_reserve_status.c | 137 | ||||
| -rw-r--r-- | src/exchange/taler-exchange-httpd_reserve_status.h | 49 | ||||
| -rw-r--r-- | src/exchangedb/perf_taler_exchangedb_interpreter.c | 9 | ||||
| -rw-r--r-- | src/exchangedb/plugin_exchangedb_postgres.c | 577 | ||||
| -rw-r--r-- | src/exchangedb/test_exchangedb.c | 7 | ||||
| -rw-r--r-- | src/include/taler_exchangedb_plugin.h | 8 | 
10 files changed, 531 insertions, 355 deletions
| diff --git a/src/exchange/Makefile.am b/src/exchange/Makefile.am index 92a8556a..96e9d7aa 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 2cdf61d8..4131e123 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 6d0b7e35..0834db56 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 78f8ff1d..08c904c5 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 00000000..e13b8f39 --- /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 00000000..7bfd4dd8 --- /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 054df58d..b5845411 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 954fca38..0f18ef58 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 ba47c2d3..8c7e4b9c 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 2cf553b1..843c6272 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);    /** | 
