gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[GNUnet-SVN] [taler-exchange] 07/08: fixing #5010 for /reserve/withdraw


From: gnunet
Subject: [GNUnet-SVN] [taler-exchange] 07/08: fixing #5010 for /reserve/withdraw
Date: Mon, 19 Jun 2017 00:18:24 +0200

This is an automated email from the git hooks/post-receive script.

grothoff pushed a commit to branch master
in repository exchange.

commit dea0f7c411d6ae5c5410d30f6072478e905cabb4
Author: Christian Grothoff <address@hidden>
AuthorDate: Mon Jun 19 00:00:21 2017 +0200

    fixing #5010 for /reserve/withdraw
---
 src/exchange-lib/test_exchange_api.c               |   2 +-
 src/exchange/taler-exchange-httpd_db.c             | 344 ----------------
 src/exchange/taler-exchange-httpd_db.h             |  23 --
 src/exchange/taler-exchange-httpd_reserve_status.c |  36 +-
 .../taler-exchange-httpd_reserve_withdraw.c        | 454 ++++++++++++++++++---
 src/exchange/taler-exchange-httpd_responses.c      |  91 +----
 src/exchange/taler-exchange-httpd_responses.h      |  51 +--
 src/exchangedb/perf_taler_exchangedb_interpreter.c |  18 +-
 src/exchangedb/plugin_exchangedb_postgres.c        | 117 ++----
 src/exchangedb/test_exchangedb.c                   |   4 +-
 src/include/taler_exchangedb_plugin.h              |  12 +-
 11 files changed, 510 insertions(+), 642 deletions(-)

diff --git a/src/exchange-lib/test_exchange_api.c 
b/src/exchange-lib/test_exchange_api.c
index e435ed7..24b8f7e 100644
--- a/src/exchange-lib/test_exchange_api.c
+++ b/src/exchange-lib/test_exchange_api.c
@@ -3576,7 +3576,7 @@ run (void *cls)
   GNUNET_assert (NULL != exchange);
   timeout_task
     = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_relative_multiply
-                                    (GNUNET_TIME_UNIT_SECONDS, 150),
+                                    (GNUNET_TIME_UNIT_SECONDS, 300),
                                     &do_timeout, NULL);
   GNUNET_SCHEDULER_add_shutdown (&do_shutdown, is);
 }
diff --git a/src/exchange/taler-exchange-httpd_db.c 
b/src/exchange/taler-exchange-httpd_db.c
index 4131e12..ebe19a9 100644
--- a/src/exchange/taler-exchange-httpd_db.c
+++ b/src/exchange/taler-exchange-httpd_db.c
@@ -553,350 +553,6 @@ TEH_DB_execute_refund (struct MHD_Connection *connection,
 
 
 /**
- * Try to execute /reserve/withdraw transaction.
- *
- * @param connection request we are handling
- * @param session database session we are using
- * @param key_state key state to lookup denomination pubs
- * @param reserve reserve to withdraw from
- * @param denomination_pub public key of the denomination requested
- * @param dki denomination to withdraw
- * @param blinded_msg blinded message to be signed
- * @param blinded_msg_len number of bytes in @a blinded_msg
- * @param h_blind hash of @a blinded_msg
- * @param signature signature over the withdraw request, to be stored in DB
- * @param[out] denom_sig where to write the resulting signature
- *        (used to release memory in case of transaction failure
- * @return MHD result code
- */
-static int
-execute_reserve_withdraw_transaction (struct MHD_Connection *connection,
-                                      struct TALER_EXCHANGEDB_Session *session,
-                                      struct TEH_KS_StateHandle *key_state,
-                                      const struct TALER_ReservePublicKeyP 
*reserve,
-                                      const struct TALER_DenominationPublicKey 
*denomination_pub,
-                                      const struct 
TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki,
-                                      const char *blinded_msg,
-                                      size_t blinded_msg_len,
-                                      const struct GNUNET_HashCode *h_blind,
-                                      const struct TALER_ReserveSignatureP 
*signature,
-                                      struct TALER_DenominationSignature 
*denom_sig)
-{
-  struct TALER_EXCHANGEDB_ReserveHistory *rh;
-  const struct TALER_EXCHANGEDB_ReserveHistory *pos;
-  struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *tdki;
-  struct TALER_EXCHANGEDB_CollectableBlindcoin collectable;
-  struct TALER_Amount amount_required;
-  struct TALER_Amount deposit_total;
-  struct TALER_Amount withdraw_total;
-  struct TALER_Amount balance;
-  struct TALER_Amount value;
-  struct TALER_Amount fee_withdraw;
-  int res;
-  int ret;
-  enum GNUNET_DB_QueryStatus qs;
-
-  /* Check if balance is sufficient */
-  START_TRANSACTION (session, connection);
-  qs = TEH_plugin->get_reserve_history (TEH_plugin->cls,
-                                        session,
-                                        reserve,
-                                       &rh);
-  (void) qs;
-  /* qs: #5010! */
-  if (NULL == rh)
-  {
-    TEH_plugin->rollback (TEH_plugin->cls,
-                          session);
-    return TEH_RESPONSE_reply_arg_unknown (connection,
-                                          TALER_EC_WITHDRAW_RESERVE_UNKNOWN,
-                                           "reserve_pub");
-  }
-
-  /* calculate amount required including fees */
-  TALER_amount_ntoh (&value,
-                     &dki->issue.properties.value);
-  TALER_amount_ntoh (&fee_withdraw,
-                     &dki->issue.properties.fee_withdraw);
-
-  if (GNUNET_OK !=
-      TALER_amount_add (&amount_required,
-                        &value,
-                        &fee_withdraw))
-  {
-    TEH_plugin->rollback (TEH_plugin->cls,
-                          session);
-    return TEH_RESPONSE_reply_internal_db_error (connection,
-                                                
TALER_EC_WITHDRAW_AMOUNT_FEE_OVERFLOW);
-  }
-
-  /* calculate balance of the reserve */
-  res = 0;
-  for (pos = rh; NULL != pos; pos = pos->next)
-  {
-    switch (pos->type)
-    {
-    case TALER_EXCHANGEDB_RO_BANK_TO_EXCHANGE:
-      if (0 == (res & 1))
-        deposit_total = pos->details.bank->amount;
-      else
-        if (GNUNET_OK !=
-            TALER_amount_add (&deposit_total,
-                              &deposit_total,
-                              &pos->details.bank->amount))
-        {
-          TEH_plugin->rollback (TEH_plugin->cls,
-                                session);
-          return TEH_RESPONSE_reply_internal_db_error (connection,
-                                                      
TALER_EC_WITHDRAW_AMOUNT_DEPOSITS_OVERFLOW);
-        }
-      res |= 1;
-      break;
-    case TALER_EXCHANGEDB_RO_WITHDRAW_COIN:
-      tdki = TEH_KS_denomination_key_lookup (key_state,
-                                             &pos->details.withdraw->denom_pub,
-                                            TEH_KS_DKU_WITHDRAW);
-      if (NULL == tdki)
-      {
-        GNUNET_break (0);
-        TEH_plugin->rollback (TEH_plugin->cls,
-                              session);
-        return TEH_RESPONSE_reply_internal_db_error (connection,
-                                                    
TALER_EC_WITHDRAW_HISTORIC_DENOMINATION_KEY_NOT_FOUND);
-      }
-      TALER_amount_ntoh (&value,
-                         &tdki->issue.properties.value);
-      if (0 == (res & 2))
-        withdraw_total = value;
-      else
-        if (GNUNET_OK !=
-            TALER_amount_add (&withdraw_total,
-                              &withdraw_total,
-                              &value))
-        {
-          TEH_plugin->rollback (TEH_plugin->cls,
-                                session);
-          return TEH_RESPONSE_reply_internal_db_error (connection,
-                                                      
TALER_EC_WITHDRAW_AMOUNT_WITHDRAWALS_OVERFLOW);
-        }
-      res |= 2;
-      break;
-
-    case TALER_EXCHANGEDB_RO_PAYBACK_COIN:
-      if (0 == (res & 1))
-        deposit_total = pos->details.payback->value;
-      else
-        if (GNUNET_OK !=
-            TALER_amount_add (&deposit_total,
-                              &deposit_total,
-                              &pos->details.payback->value))
-        {
-          TEH_plugin->rollback (TEH_plugin->cls,
-                                session);
-          return TEH_RESPONSE_reply_internal_db_error (connection,
-                                                      
TALER_EC_WITHDRAW_AMOUNT_DEPOSITS_OVERFLOW);
-        }
-      res |= 1;
-      break;
-
-    case TALER_EXCHANGEDB_RO_EXCHANGE_TO_BANK:
-      if (0 == (res & 2))
-        withdraw_total = pos->details.bank->amount;
-      else
-        if (GNUNET_OK !=
-            TALER_amount_add (&withdraw_total,
-                              &withdraw_total,
-                              &pos->details.bank->amount))
-        {
-          TEH_plugin->rollback (TEH_plugin->cls,
-                                session);
-          return TEH_RESPONSE_reply_internal_db_error (connection,
-                                                      
TALER_EC_WITHDRAW_AMOUNT_WITHDRAWALS_OVERFLOW);
-        }
-      res |= 2;
-      break;
-    }
-  }
-  if (0 == (res & 1))
-  {
-    /* did not encounter any wire transfer operations, how can we have a 
reserve? */
-    GNUNET_break (0);
-    TEH_plugin->rollback (TEH_plugin->cls,
-                          session);
-    return TEH_RESPONSE_reply_internal_db_error (connection,
-                                                
TALER_EC_WITHDRAW_RESERVE_WITHOUT_WIRE_TRANSFER);
-  }
-  if (0 == (res & 2))
-  {
-    /* did not encounter any withdraw operations, set to zero */
-    TALER_amount_get_zero (deposit_total.currency,
-                           &withdraw_total);
-  }
-  /* All reserve balances should be non-negative */
-  if (GNUNET_SYSERR ==
-      TALER_amount_subtract (&balance,
-                             &deposit_total,
-                             &withdraw_total))
-  {
-    GNUNET_break (0); /* database inconsistent */
-    TEH_plugin->rollback (TEH_plugin->cls,
-                          session);
-    return TEH_RESPONSE_reply_internal_db_error (connection,
-                                                 
TALER_EC_WITHDRAW_RESERVE_HISTORY_IMPOSSIBLE);
-  }
-  if (0 < TALER_amount_cmp (&amount_required,
-                            &balance))
-  {
-    TEH_plugin->rollback (TEH_plugin->cls,
-                          session);
-    res = TEH_RESPONSE_reply_reserve_withdraw_insufficient_funds (connection,
-                                                                  rh);
-    TEH_plugin->free_reserve_history (TEH_plugin->cls,
-                                      rh);
-    return res;
-  }
-  TEH_plugin->free_reserve_history (TEH_plugin->cls,
-                                    rh);
-
-  /* Balance is good, sign the coin! */
-  denom_sig->rsa_signature
-    = GNUNET_CRYPTO_rsa_sign_blinded (dki->denom_priv.rsa_private_key,
-                                      blinded_msg,
-                                      blinded_msg_len);
-  if (NULL == denom_sig->rsa_signature)
-  {
-    GNUNET_break (0);
-    TEH_plugin->rollback (TEH_plugin->cls,
-                          session);
-    return TEH_RESPONSE_reply_internal_error (connection,
-                                             
TALER_EC_WITHDRAW_SIGNATURE_FAILED,
-                                              "Internal error");
-  }
-  collectable.sig = *denom_sig;
-  collectable.denom_pub = *denomination_pub;
-  collectable.amount_with_fee = amount_required;
-  collectable.withdraw_fee = fee_withdraw;
-  collectable.reserve_pub = *reserve;
-  collectable.h_coin_envelope = *h_blind;
-  collectable.reserve_sig = *signature;
-  ret = TEH_plugin->insert_withdraw_info (TEH_plugin->cls,
-                                          session,
-                                          &collectable);
-  if (GNUNET_SYSERR == ret)
-  {
-    GNUNET_break (0);
-    TEH_plugin->rollback (TEH_plugin->cls,
-                          session);
-    return TEH_RESPONSE_reply_internal_db_error (connection,
-                                                
TALER_EC_WITHDRAW_DB_STORE_ERROR);
-  }
-  if (GNUNET_NO == ret)
-    RETRY_TRANSACTION(session, connection);
-  COMMIT_TRANSACTION (session, connection);
-
-  return TEH_RESPONSE_reply_reserve_withdraw_success (connection,
-                                                      &collectable);
-}
-
-
-
-/**
- * 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
- * blinded envelope.
- *
- * @param connection the MHD connection to handle
- * @param reserve public key of the reserve
- * @param denomination_pub public key of the denomination requested
- * @param blinded_msg blinded message to be signed
- * @param blinded_msg_len number of bytes in @a blinded_msg
- * @param signature signature over the withdraw request, to be stored in DB
- * @return MHD result code
- */
-int
-TEH_DB_execute_reserve_withdraw (struct MHD_Connection *connection,
-                                 const struct TALER_ReservePublicKeyP *reserve,
-                                 const struct TALER_DenominationPublicKey 
*denomination_pub,
-                                 const char *blinded_msg,
-                                 size_t blinded_msg_len,
-                                 const struct TALER_ReserveSignatureP 
*signature)
-{
-  struct TALER_EXCHANGEDB_Session *session;
-  struct TEH_KS_StateHandle *key_state;
-  struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki;
-  struct TALER_EXCHANGEDB_CollectableBlindcoin collectable;
-  struct TALER_DenominationSignature denom_sig;
-  struct GNUNET_HashCode h_blind;
-  int res;
-
-  GNUNET_CRYPTO_hash (blinded_msg,
-                      blinded_msg_len,
-                      &h_blind);
-  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);
-  }
-  res = TEH_plugin->get_withdraw_info (TEH_plugin->cls,
-                                       session,
-                                       &h_blind,
-                                       &collectable);
-  if (GNUNET_SYSERR == res)
-  {
-    GNUNET_break (0);
-    return TEH_RESPONSE_reply_internal_db_error (connection,
-                                                
TALER_EC_WITHDRAW_DB_FETCH_ERROR);
-  }
-
-  /* Don't sign again if we have already signed the coin */
-  if (GNUNET_YES == res)
-  {
-    res = TEH_RESPONSE_reply_reserve_withdraw_success (connection,
-                                                       &collectable);
-    GNUNET_CRYPTO_rsa_signature_free (collectable.sig.rsa_signature);
-    GNUNET_CRYPTO_rsa_public_key_free (collectable.denom_pub.rsa_public_key);
-    return res;
-  }
-  GNUNET_assert (GNUNET_NO == res);
-
-  /* FIXME: do we have to do this a second time here? */
-  key_state = TEH_KS_acquire ();
-  dki = TEH_KS_denomination_key_lookup (key_state,
-                                        denomination_pub,
-                                       TEH_KS_DKU_WITHDRAW);
-  if (NULL == dki)
-  {
-    TEH_KS_release (key_state);
-    return TEH_RESPONSE_reply_json_pack (connection,
-                                         MHD_HTTP_NOT_FOUND,
-                                         "{s:s, s:I}",
-                                         "error",
-                                         "Denomination not found",
-                                        "code",
-                                        (json_int_t) 
TALER_EC_WITHDRAW_DENOMINATION_KEY_NOT_FOUND);
-  }
-  denom_sig.rsa_signature = NULL;
-  res = execute_reserve_withdraw_transaction (connection,
-                                              session,
-                                              key_state,
-                                              reserve,
-                                              denomination_pub,
-                                              dki,
-                                              blinded_msg,
-                                              blinded_msg_len,
-                                              &h_blind,
-                                              signature,
-                                              &denom_sig);
-  if (NULL != denom_sig.rsa_signature)
-    GNUNET_CRYPTO_rsa_signature_free (denom_sig.rsa_signature);
-  TEH_KS_release (key_state);
-  return res;
-}
-
-
-/**
  * Parse coin melt requests from a JSON object and write them to
  * the database.
  *
diff --git a/src/exchange/taler-exchange-httpd_db.h 
b/src/exchange/taler-exchange-httpd_db.h
index 0834db5..e3717bd 100644
--- a/src/exchange/taler-exchange-httpd_db.h
+++ b/src/exchange/taler-exchange-httpd_db.h
@@ -98,29 +98,6 @@ TEH_DB_execute_refund (struct MHD_Connection *connection,
 
 
 /**
- * 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
- * blinded envelope.
- *
- * @param connection the MHD connection to handle
- * @param reserve public key of the reserve
- * @param denomination_pub public key of the denomination requested
- * @param blinded_msg blinded message to be signed
- * @param blinded_msg_len number of bytes in @a blinded_msg
- * @param signature signature over the withdraw request, to be stored in DB
- * @return MHD result code
- */
-int
-TEH_DB_execute_reserve_withdraw (struct MHD_Connection *connection,
-                                 const struct TALER_ReservePublicKeyP *reserve,
-                                 const struct TALER_DenominationPublicKey 
*denomination_pub,
-                                 const char *blinded_msg,
-                                 size_t blinded_msg_len,
-                                 const struct TALER_ReserveSignatureP 
*signature);
-
-
-/**
  * @brief Details about a melt operation of an individual coin.
  */
 struct TEH_DB_MeltDetails
diff --git a/src/exchange/taler-exchange-httpd_reserve_status.c 
b/src/exchange/taler-exchange-httpd_reserve_status.c
index e13b8f3..f87afa5 100644
--- a/src/exchange/taler-exchange-httpd_reserve_status.c
+++ b/src/exchange/taler-exchange-httpd_reserve_status.c
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  Copyright (C) 2014, 2015, 2016 GNUnet e.V.
+  Copyright (C) 2014-2017 GNUnet e.V.
 
   TALER is free software; you can redistribute it and/or modify it under the
   terms of the GNU Affero General Public License as published by the Free 
Software
@@ -30,6 +30,36 @@
 
 
 /**
+ * Send reserve status information to client.
+ *
+ * @param connection connection to the client
+ * @param rh reserve history to return
+ * @return MHD result code
+ */
+static int
+reply_reserve_status_success (struct MHD_Connection *connection,
+                             const struct TALER_EXCHANGEDB_ReserveHistory *rh)
+{
+  json_t *json_balance;
+  json_t *json_history;
+  struct TALER_Amount balance;
+
+  json_history = TEH_RESPONSE_compile_reserve_history (rh,
+                                                      &balance);
+  if (NULL == json_history)
+    return TEH_RESPONSE_reply_internal_error (connection,
+                                             TALER_EC_RESERVE_STATUS_DB_ERROR,
+                                              "balance calculation failure");
+  json_balance = TALER_JSON_from_amount (&balance);
+  return TEH_RESPONSE_reply_json_pack (connection,
+                                       MHD_HTTP_OK,
+                                       "{s:o, s:o}",
+                                       "balance", json_balance,
+                                       "history", json_history);
+}
+
+
+/**
  * Closure for #reserve_status_transaction.
  */
 struct ReserveStatusContext
@@ -126,8 +156,8 @@ TEH_RESERVE_handler_reserve_status (struct 
TEH_RequestHandler *rh,
                                          "{s:s, s:s}",
                                          "error", "Reserve not found",
                                          "parameter", "withdraw_pub");
-  mhd_ret = TEH_RESPONSE_reply_reserve_status_success (connection,
-                                                      rsc.rh);
+  mhd_ret = reply_reserve_status_success (connection,
+                                         rsc.rh);
   TEH_plugin->free_reserve_history (TEH_plugin->cls,
                                     rsc.rh);
   return mhd_ret;
diff --git a/src/exchange/taler-exchange-httpd_reserve_withdraw.c 
b/src/exchange/taler-exchange-httpd_reserve_withdraw.c
index 6f6e66a..2bc268d 100644
--- a/src/exchange/taler-exchange-httpd_reserve_withdraw.c
+++ b/src/exchange/taler-exchange-httpd_reserve_withdraw.c
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  Copyright (C) 2014, 2015, 2016 GNUnet e.V.
+  Copyright (C) 2014-2017 GNUnet e.V.
 
   TALER is free software; you can redistribute it and/or modify it under the
   terms of the GNU Affero General Public License as published by the Free 
Software
@@ -30,6 +30,348 @@
 
 
 /**
+ * Send reserve status information to client with the
+ * message that we have insufficient funds for the
+ * requested /reserve/withdraw operation.
+ *
+ * @param connection connection to the client
+ * @param rh reserve history to return
+ * @return MHD result code
+ */
+static int
+reply_reserve_withdraw_insufficient_funds (struct MHD_Connection *connection,
+                                          const struct 
TALER_EXCHANGEDB_ReserveHistory *rh)
+{
+  json_t *json_balance;
+  json_t *json_history;
+  struct TALER_Amount balance;
+
+  json_history = TEH_RESPONSE_compile_reserve_history (rh,
+                                                      &balance);
+  if (NULL == json_history)
+    return TEH_RESPONSE_reply_internal_error (connection,
+                                             
TALER_EC_WITHDRAW_HISTORY_DB_ERROR_INSUFFICIENT_FUNDS,
+                                              "balance calculation failure");
+  json_balance = TALER_JSON_from_amount (&balance);
+  return TEH_RESPONSE_reply_json_pack (connection,
+                                       MHD_HTTP_FORBIDDEN,
+                                       "{s:s, s:I, s:o, s:o}",
+                                       "error", "Insufficient funds",
+                                      "code", (json_int_t) 
TALER_EC_WITHDRAW_INSUFFICIENT_FUNDS,
+                                       "balance", json_balance,
+                                       "history", json_history);
+}
+
+
+/**
+ * Send blinded coin information to client.
+ *
+ * @param connection connection to the client
+ * @param collectable blinded coin to return
+ * @return MHD result code
+ */
+static int
+reply_reserve_withdraw_success (struct MHD_Connection *connection,
+                               const struct 
TALER_EXCHANGEDB_CollectableBlindcoin *collectable)
+{
+  json_t *sig_json;
+
+  sig_json = GNUNET_JSON_from_rsa_signature (collectable->sig.rsa_signature);
+  return TEH_RESPONSE_reply_json_pack (connection,
+                                       MHD_HTTP_OK,
+                                       "{s:o}",
+                                       "ev_sig", sig_json);
+}
+
+
+/**
+ * Context for #withdraw_transaction.
+ */
+struct WithdrawContext
+{
+  /**
+   * Details about the withdrawal request.
+   */
+  struct TALER_WithdrawRequestPS wsrd;
+
+  /**
+   * Value of the coin plus withdraw fee.
+   */
+  struct TALER_Amount amount_required;
+
+  /**
+   * Denomination public key.
+   */
+  struct TALER_DenominationPublicKey denomination_pub;
+
+  /**
+   * Signature over the request.
+   */
+  struct TALER_ReserveSignatureP signature;
+
+  /**
+   * Blinded planchet.
+   */
+  char *blinded_msg;
+
+  /**
+   * Key state to use to inspect previous withdrawal values.
+   */
+  struct TEH_KS_StateHandle *key_state;
+
+  /**
+   * Number of bytes in @e blinded_msg.
+   */
+  size_t blinded_msg_len;
+
+  /**
+   * Details about denomination we are about to withdraw.
+   */
+  struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki;
+
+  /**
+   * Set to the resulting signed coin data to be returned to the client.
+   */
+  struct TALER_EXCHANGEDB_CollectableBlindcoin collectable;
+
+};
+
+
+/**
+ * Function implementing /reserve/withdraw transaction.  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 WithdrawContext *`
+ * @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
+withdraw_transaction (void *cls,
+                     struct MHD_Connection *connection,
+                     struct TALER_EXCHANGEDB_Session *session,
+                     int *mhd_ret)
+{
+  struct WithdrawContext *wc = cls;
+  struct TALER_EXCHANGEDB_ReserveHistory *rh;  
+  struct TALER_Amount deposit_total;
+  struct TALER_Amount withdraw_total;
+  struct TALER_Amount balance;
+  struct TALER_Amount value;
+  struct TALER_Amount fee_withdraw;
+  int res;
+  enum GNUNET_DB_QueryStatus qs;
+  struct TALER_DenominationSignature denom_sig;
+  struct GNUNET_HashCode h_blind;
+
+  GNUNET_CRYPTO_hash (wc->blinded_msg,
+                      wc->blinded_msg_len,
+                      &h_blind);
+  qs = TEH_plugin->get_withdraw_info (TEH_plugin->cls,
+                                     session,
+                                     &h_blind,
+                                     &wc->collectable);
+  if (0 > qs)
+  {
+    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+    if (GNUNET_DB_STATUS_HARD_ERROR == qs)
+      *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+                                                      
TALER_EC_WITHDRAW_DB_FETCH_ERROR);
+    return qs;
+  }
+
+  /* Don't sign again if we have already signed the coin */
+  if (1 == qs)
+    return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
+  GNUNET_assert (0 == qs);
+
+  /* Check if balance is sufficient */
+  qs = TEH_plugin->get_reserve_history (TEH_plugin->cls,
+                                        session,
+                                        &wc->wsrd.reserve_pub,
+                                       &rh);
+  if (0 > qs)
+  {
+    if (GNUNET_DB_STATUS_HARD_ERROR == qs)
+      *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+                                                      
TALER_EC_WITHDRAW_DB_FETCH_ERROR); 
+    return qs;
+  }
+  if (NULL == rh)
+  {
+    *mhd_ret = TEH_RESPONSE_reply_arg_unknown (connection,
+                                              
TALER_EC_WITHDRAW_RESERVE_UNKNOWN,
+                                              "reserve_pub");
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  }
+
+  /* calculate balance of the reserve */
+  res = 0;
+  for (const struct TALER_EXCHANGEDB_ReserveHistory *pos = rh;
+       NULL != pos;
+       pos = pos->next)
+  {
+    switch (pos->type)
+    {
+    case TALER_EXCHANGEDB_RO_BANK_TO_EXCHANGE:
+      if (0 == (res & 1))
+        deposit_total = pos->details.bank->amount;
+      else
+        if (GNUNET_OK !=
+            TALER_amount_add (&deposit_total,
+                              &deposit_total,
+                              &pos->details.bank->amount))
+        {
+          *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+                                                          
TALER_EC_WITHDRAW_AMOUNT_DEPOSITS_OVERFLOW);
+         return GNUNET_DB_STATUS_HARD_ERROR;
+        }
+      res |= 1;
+      break;
+    case TALER_EXCHANGEDB_RO_WITHDRAW_COIN:
+      {
+       struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *tdki;
+       
+       tdki = TEH_KS_denomination_key_lookup (wc->key_state,
+                                              
&pos->details.withdraw->denom_pub,
+                                              TEH_KS_DKU_WITHDRAW);
+       if (NULL == tdki)
+        {
+         GNUNET_break (0);
+         *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+                                                          
TALER_EC_WITHDRAW_HISTORIC_DENOMINATION_KEY_NOT_FOUND);
+         return GNUNET_DB_STATUS_HARD_ERROR;
+       }
+       TALER_amount_ntoh (&value,
+                          &tdki->issue.properties.value);
+       if (0 == (res & 2))
+         withdraw_total = value;
+       else
+         if (GNUNET_OK !=
+             TALER_amount_add (&withdraw_total,
+                               &withdraw_total,
+                               &value))
+         {
+           *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+                                                            
TALER_EC_WITHDRAW_AMOUNT_WITHDRAWALS_OVERFLOW);
+           return GNUNET_DB_STATUS_HARD_ERROR;
+         }
+       res |= 2;
+       break;
+      }
+    case TALER_EXCHANGEDB_RO_PAYBACK_COIN:
+      if (0 == (res & 1))
+        deposit_total = pos->details.payback->value;
+      else
+        if (GNUNET_OK !=
+            TALER_amount_add (&deposit_total,
+                              &deposit_total,
+                              &pos->details.payback->value))
+        {
+          *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+                                                          
TALER_EC_WITHDRAW_AMOUNT_DEPOSITS_OVERFLOW);
+         return GNUNET_DB_STATUS_HARD_ERROR;
+        }
+      res |= 1;
+      break;
+
+    case TALER_EXCHANGEDB_RO_EXCHANGE_TO_BANK:
+      if (0 == (res & 2))
+        withdraw_total = pos->details.bank->amount;
+      else
+        if (GNUNET_OK !=
+            TALER_amount_add (&withdraw_total,
+                              &withdraw_total,
+                              &pos->details.bank->amount))
+        {
+          *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+                                                          
TALER_EC_WITHDRAW_AMOUNT_WITHDRAWALS_OVERFLOW);
+         return GNUNET_DB_STATUS_HARD_ERROR;
+        }
+      res |= 2;
+      break;
+    }
+  }
+  if (0 == (res & 1))
+  {
+    /* did not encounter any wire transfer operations, how can we have a 
reserve? */
+    GNUNET_break (0);
+    *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+                                                    
TALER_EC_WITHDRAW_RESERVE_WITHOUT_WIRE_TRANSFER);
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  }
+  if (0 == (res & 2))
+  {
+    /* did not encounter any withdraw operations, set to zero */
+    TALER_amount_get_zero (deposit_total.currency,
+                           &withdraw_total);
+  }
+  /* All reserve balances should be non-negative */
+  if (GNUNET_SYSERR ==
+      TALER_amount_subtract (&balance,
+                             &deposit_total,
+                             &withdraw_total))
+  {
+    GNUNET_break (0); /* database inconsistent */
+    *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+                                                    
TALER_EC_WITHDRAW_RESERVE_HISTORY_IMPOSSIBLE);
+    return GNUNET_DB_STATUS_HARD_ERROR;        
+  }
+  if (0 < TALER_amount_cmp (&wc->amount_required,
+                            &balance))
+  {
+    *mhd_ret = reply_reserve_withdraw_insufficient_funds (connection,
+                                                         rh);
+    TEH_plugin->free_reserve_history (TEH_plugin->cls,
+                                      rh);
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  }
+  TEH_plugin->free_reserve_history (TEH_plugin->cls,
+                                    rh);
+
+  /* Balance is good, sign the coin! */
+  denom_sig.rsa_signature
+    = GNUNET_CRYPTO_rsa_sign_blinded (wc->dki->denom_priv.rsa_private_key,
+                                      wc->blinded_msg,
+                                      wc->blinded_msg_len);
+  if (NULL == denom_sig.rsa_signature)
+  {
+    GNUNET_break (0);
+    *mhd_ret = TEH_RESPONSE_reply_internal_error (connection,
+                                                 
TALER_EC_WITHDRAW_SIGNATURE_FAILED,
+                                                 "Internal error");
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  }
+  wc->collectable.sig = denom_sig;
+  wc->collectable.denom_pub = wc->denomination_pub;
+  wc->collectable.amount_with_fee = wc->amount_required;
+  wc->collectable.withdraw_fee = fee_withdraw;
+  wc->collectable.reserve_pub = wc->wsrd.reserve_pub;
+  wc->collectable.h_coin_envelope = h_blind;
+  wc->collectable.reserve_sig = wc->signature;
+  qs = TEH_plugin->insert_withdraw_info (TEH_plugin->cls,
+                                        session,
+                                        &wc->collectable);
+  if (0 > qs)
+  {
+    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+    GNUNET_CRYPTO_rsa_signature_free (denom_sig.rsa_signature);
+    if (GNUNET_DB_STATUS_HARD_ERROR == qs)
+      *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+                                                      
TALER_EC_WITHDRAW_DB_STORE_ERROR);
+    return qs;
+  }
+  return qs;
+}
+
+
+/**
  * 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
@@ -52,29 +394,22 @@ TEH_RESERVE_handler_reserve_withdraw (struct 
TEH_RequestHandler *rh,
                                       const char *upload_data,
                                       size_t *upload_data_size)
 {
+  struct WithdrawContext wc;
   json_t *root;
-  struct TALER_WithdrawRequestPS wsrd;
   int res;
-  struct TALER_DenominationPublicKey denomination_pub;
-  char *blinded_msg;
-  size_t blinded_msg_len;
+  int mhd_ret;
   struct TALER_Amount amount;
-  struct TALER_Amount amount_with_fee;
   struct TALER_Amount fee_withdraw;
-  struct TALER_ReserveSignatureP signature;
-  struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki;
-  struct TEH_KS_StateHandle *ks;
-
   struct GNUNET_JSON_Specification spec[] = {
     GNUNET_JSON_spec_varsize ("coin_ev",
-                              (void **) &blinded_msg,
-                              &blinded_msg_len),
+                              (void **) &wc.blinded_msg,
+                              &wc.blinded_msg_len),
     GNUNET_JSON_spec_fixed_auto ("reserve_pub",
-                                 &wsrd.reserve_pub),
+                                 &wc.wsrd.reserve_pub),
     GNUNET_JSON_spec_fixed_auto ("reserve_sig",
-                                 &signature),
+                                 &wc.signature),
     TALER_JSON_spec_denomination_public_key ("denom_pub",
-                                             &denomination_pub),
+                                             &wc.denomination_pub),
     GNUNET_JSON_spec_end ()
   };
 
@@ -93,60 +428,79 @@ TEH_RESERVE_handler_reserve_withdraw (struct 
TEH_RequestHandler *rh,
   json_decref (root);
   if (GNUNET_OK != res)
     return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
-  ks = TEH_KS_acquire ();
-  dki = TEH_KS_denomination_key_lookup (ks,
-                                        &denomination_pub,
-                                       TEH_KS_DKU_WITHDRAW);
-  if (NULL == dki)
+  wc.key_state = TEH_KS_acquire ();
+  wc.dki = TEH_KS_denomination_key_lookup (wc.key_state,
+                                          &wc.denomination_pub,
+                                          TEH_KS_DKU_WITHDRAW);
+  if (NULL == wc.dki)
   {
     GNUNET_JSON_parse_free (spec);
-    TEH_KS_release (ks);
+    TEH_KS_release (wc.key_state);
     return TEH_RESPONSE_reply_arg_unknown (connection,
                                           
TALER_EC_WITHDRAW_DENOMINATION_KEY_NOT_FOUND,
                                            "denom_pub");
   }
   TALER_amount_ntoh (&amount,
-                     &dki->issue.properties.value);
+                     &wc.dki->issue.properties.value);
   TALER_amount_ntoh (&fee_withdraw,
-                     &dki->issue.properties.fee_withdraw);
-  GNUNET_assert (GNUNET_OK ==
-                 TALER_amount_add (&amount_with_fee,
-                                   &amount,
-                                   &fee_withdraw));
-  TALER_amount_hton (&wsrd.amount_with_fee,
-                     &amount_with_fee);
-  TALER_amount_hton (&wsrd.withdraw_fee,
+                     &wc.dki->issue.properties.fee_withdraw);
+  if (GNUNET_OK !=
+      TALER_amount_add (&wc.amount_required,
+                       &amount,
+                       &fee_withdraw))
+  {
+    GNUNET_JSON_parse_free (spec);
+    TEH_KS_release (wc.key_state);
+    return TEH_RESPONSE_reply_internal_error (connection,
+                                             
TALER_EC_WITHDRAW_AMOUNT_FEE_OVERFLOW,
+                                             "amount overflow for value plus 
withdraw fee");
+  }
+  TALER_amount_hton (&wc.wsrd.amount_with_fee,
+                     &wc.amount_required);
+  TALER_amount_hton (&wc.wsrd.withdraw_fee,
                      &fee_withdraw);
-  TEH_KS_release (ks);
   /* verify signature! */
-  wsrd.purpose.size = htonl (sizeof (struct TALER_WithdrawRequestPS));
-  wsrd.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW);
-
-  GNUNET_CRYPTO_rsa_public_key_hash (denomination_pub.rsa_public_key,
-                                     &wsrd.h_denomination_pub);
-  GNUNET_CRYPTO_hash (blinded_msg,
-                      blinded_msg_len,
-                      &wsrd.h_coin_envelope);
+  wc.wsrd.purpose.size
+    = htonl (sizeof (struct TALER_WithdrawRequestPS));
+  wc.wsrd.purpose.purpose
+    = htonl (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW);
+  GNUNET_CRYPTO_rsa_public_key_hash (wc.denomination_pub.rsa_public_key,
+                                     &wc.wsrd.h_denomination_pub);
+  GNUNET_CRYPTO_hash (wc.blinded_msg,
+                      wc.blinded_msg_len,
+                      &wc.wsrd.h_coin_envelope);
   if (GNUNET_OK !=
       GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW,
-                                  &wsrd.purpose,
-                                  &signature.eddsa_signature,
-                                  &wsrd.reserve_pub.eddsa_pub))
+                                  &wc.wsrd.purpose,
+                                  &wc.signature.eddsa_signature,
+                                  &wc.wsrd.reserve_pub.eddsa_pub))
   {
     TALER_LOG_WARNING ("Client supplied invalid signature for 
/reserve/withdraw request\n");
     GNUNET_JSON_parse_free (spec);
+    TEH_KS_release (wc.key_state);
     return TEH_RESPONSE_reply_signature_invalid (connection,
                                                 
TALER_EC_WITHDRAW_RESERVE_SIGNATURE_INVALID,
                                                  "reserve_sig");
   }
-  res = TEH_DB_execute_reserve_withdraw (connection,
-                                         &wsrd.reserve_pub,
-                                         &denomination_pub,
-                                         blinded_msg,
-                                         blinded_msg_len,
-                                         &signature);
+
+  if (GNUNET_OK !=
+      TEH_DB_run_transaction (connection,
+                             &mhd_ret,
+                             &withdraw_transaction,
+                             &wc))
+  {
+    TEH_KS_release (wc.key_state);
+    GNUNET_JSON_parse_free (spec);
+    return mhd_ret;
+  }
+  TEH_KS_release (wc.key_state);
   GNUNET_JSON_parse_free (spec);
-  return res;
+
+  mhd_ret = reply_reserve_withdraw_success (connection,
+                                           &wc.collectable);
+  GNUNET_CRYPTO_rsa_signature_free (wc.collectable.sig.rsa_signature);
+  return mhd_ret;
 }
 
-/* end of taler-exchange-httpd_reserve.c */
+
+/* end of taler-exchange-httpd_reserve_withdraw.c */
diff --git a/src/exchange/taler-exchange-httpd_responses.c 
b/src/exchange/taler-exchange-httpd_responses.c
index 771b8ad..48df955 100644
--- a/src/exchange/taler-exchange-httpd_responses.c
+++ b/src/exchange/taler-exchange-httpd_responses.c
@@ -685,9 +685,9 @@ TEH_RESPONSE_reply_coin_insufficient_funds (struct 
MHD_Connection *connection,
  * @param[out] balance set to current reserve balance
  * @return json representation of the @a rh, NULL on error
  */
-static json_t *
-compile_reserve_history (const struct TALER_EXCHANGEDB_ReserveHistory *rh,
-                         struct TALER_Amount *balance)
+json_t *
+TEH_RESPONSE_compile_reserve_history (const struct 
TALER_EXCHANGEDB_ReserveHistory *rh,
+                                     struct TALER_Amount *balance)
 {
   struct TALER_Amount deposit_total;
   struct TALER_Amount withdraw_total;
@@ -959,91 +959,6 @@ TEH_RESPONSE_reply_refund_success (struct MHD_Connection 
*connection,
 
 
 /**
- * Send reserve status information to client.
- *
- * @param connection connection to the client
- * @param rh reserve history to return
- * @return MHD result code
- */
-int
-TEH_RESPONSE_reply_reserve_status_success (struct MHD_Connection *connection,
-                                           const struct 
TALER_EXCHANGEDB_ReserveHistory *rh)
-{
-  json_t *json_balance;
-  json_t *json_history;
-  struct TALER_Amount balance;
-
-  json_history = compile_reserve_history (rh,
-                                          &balance);
-  if (NULL == json_history)
-    return TEH_RESPONSE_reply_internal_error (connection,
-                                             TALER_EC_RESERVE_STATUS_DB_ERROR,
-                                              "balance calculation failure");
-  json_balance = TALER_JSON_from_amount (&balance);
-  return TEH_RESPONSE_reply_json_pack (connection,
-                                       MHD_HTTP_OK,
-                                       "{s:o, s:o}",
-                                       "balance", json_balance,
-                                       "history", json_history);
-}
-
-
-/**
- * Send reserve status information to client with the
- * message that we have insufficient funds for the
- * requested /reserve/withdraw operation.
- *
- * @param connection connection to the client
- * @param rh reserve history to return
- * @return MHD result code
- */
-int
-TEH_RESPONSE_reply_reserve_withdraw_insufficient_funds (struct MHD_Connection 
*connection,
-                                                        const struct 
TALER_EXCHANGEDB_ReserveHistory *rh)
-{
-  json_t *json_balance;
-  json_t *json_history;
-  struct TALER_Amount balance;
-
-  json_history = compile_reserve_history (rh,
-                                          &balance);
-  if (NULL == json_history)
-    return TEH_RESPONSE_reply_internal_error (connection,
-                                             
TALER_EC_WITHDRAW_HISTORY_DB_ERROR_INSUFFICIENT_FUNDS,
-                                              "balance calculation failure");
-  json_balance = TALER_JSON_from_amount (&balance);
-  return TEH_RESPONSE_reply_json_pack (connection,
-                                       MHD_HTTP_FORBIDDEN,
-                                       "{s:s, s:I, s:o, s:o}",
-                                       "error", "Insufficient funds",
-                                      "code", (json_int_t) 
TALER_EC_WITHDRAW_INSUFFICIENT_FUNDS,
-                                       "balance", json_balance,
-                                       "history", json_history);
-}
-
-
-/**
- * Send blinded coin information to client.
- *
- * @param connection connection to the client
- * @param collectable blinded coin to return
- * @return MHD result code
- */
-int
-TEH_RESPONSE_reply_reserve_withdraw_success (struct MHD_Connection *connection,
-                                            const struct 
TALER_EXCHANGEDB_CollectableBlindcoin *collectable)
-{
-  json_t *sig_json;
-
-  sig_json = GNUNET_JSON_from_rsa_signature (collectable->sig.rsa_signature);
-  return TEH_RESPONSE_reply_json_pack (connection,
-                                       MHD_HTTP_OK,
-                                       "{s:o}",
-                                       "ev_sig", sig_json);
-}
-
-
-/**
  * Send a response for a failed "/refresh/melt" request.  The
  * transaction history of the given coin demonstrates that the
  * @a residual value of the coin is below the @a requested
diff --git a/src/exchange/taler-exchange-httpd_responses.h 
b/src/exchange/taler-exchange-httpd_responses.h
index 047bb4f..aef5bf9 100644
--- a/src/exchange/taler-exchange-httpd_responses.h
+++ b/src/exchange/taler-exchange-httpd_responses.h
@@ -244,6 +244,19 @@ TEH_RESPONSE_reply_invalid_json (struct MHD_Connection 
*connectionx);
 
 
 /**
+ * Compile the history of a reserve into a JSON object
+ * and calculate the total balance.
+ *
+ * @param rh reserve history to JSON-ify
+ * @param[out] balance set to current reserve balance
+ * @return json representation of the @a rh, NULL on error
+ */
+json_t *
+TEH_RESPONSE_compile_reserve_history (const struct 
TALER_EXCHANGEDB_ReserveHistory *rh,
+                                     struct TALER_Amount *balance);
+
+
+/**
  * Send proof that a request is invalid to client because of
  * insufficient funds.  This function will create a message with all
  * of the operations affecting the coin that demonstrate that the coin
@@ -412,44 +425,6 @@ TEH_RESPONSE_reply_track_transfer_details (struct 
MHD_Connection *connection,
 
 
 /**
- * Send reserve status information to client.
- *
- * @param connection connection to the client
- * @param rh reserve history to return
- * @return MHD result code
- */
-int
-TEH_RESPONSE_reply_reserve_status_success (struct MHD_Connection *connection,
-                                           const struct 
TALER_EXCHANGEDB_ReserveHistory *rh);
-
-
-/**
- * Send reserve status information to client with the
- * message that we have insufficient funds for the
- * requested /reserve/withdraw operation.
- *
- * @param connection connection to the client
- * @param rh reserve history to return
- * @return MHD result code
- */
-int
-TEH_RESPONSE_reply_reserve_withdraw_insufficient_funds (struct MHD_Connection 
*connection,
-                                                        const struct 
TALER_EXCHANGEDB_ReserveHistory *rh);
-
-
-/**
- * Send blinded coin information to client.
- *
- * @param connection connection to the client
- * @param collectable blinded coin to return
- * @return MHD result code
- */
-int
-TEH_RESPONSE_reply_reserve_withdraw_success (struct MHD_Connection *connection,
-                                             const struct 
TALER_EXCHANGEDB_CollectableBlindcoin *collectable);
-
-
-/**
  * Send a confirmation response to a "/refresh/melt" request.
  *
  * @param connection the connection to send the response to
diff --git a/src/exchangedb/perf_taler_exchangedb_interpreter.c 
b/src/exchangedb/perf_taler_exchangedb_interpreter.c
index b584541..5e5c6a3 100644
--- a/src/exchangedb/perf_taler_exchangedb_interpreter.c
+++ b/src/exchangedb/perf_taler_exchangedb_interpreter.c
@@ -1496,31 +1496,31 @@ interpret (struct 
PERF_TALER_EXCHANGEDB_interpreter_state *state)
       case PERF_TALER_EXCHANGEDB_CMD_INSERT_WITHDRAW:
         {
           unsigned int coin_index;
-          int ret;
+          enum GNUNET_DB_QueryStatus qs;
           struct PERF_TALER_EXCHANGEDB_Coin *coin;
 
           coin_index = state->cmd[state->i].details.insert_withdraw.index_coin;
           coin = state->cmd[coin_index].exposed.data.coin;
-          ret = state->plugin->insert_withdraw_info (state->plugin->cls,
+          qs = state->plugin->insert_withdraw_info (state->plugin->cls,
                                                      state->session,
                                                      &coin->blind);
-          GNUNET_assert (GNUNET_SYSERR != ret);
+          GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs);
         }
         break;
 
       case PERF_TALER_EXCHANGEDB_CMD_GET_WITHDRAW:
         {
           unsigned int source_index;
-          int ret;
+          enum GNUNET_DB_QueryStatus qs;
           struct PERF_TALER_EXCHANGEDB_Data *data;
 
           source_index = 
state->cmd[state->i].details.get_denomination.index_denom;
           data = &state->cmd[source_index].exposed;
-          ret = state->plugin->get_withdraw_info (state->plugin->cls,
-                                                  state->session,
-                                                  
&data->data.coin->blind.h_coin_envelope,
-                                                  &data->data.coin->blind);
-          GNUNET_assert (GNUNET_SYSERR != ret);
+          qs = state->plugin->get_withdraw_info (state->plugin->cls,
+                                                state->session,
+                                                
&data->data.coin->blind.h_coin_envelope,
+                                                &data->data.coin->blind);
+          GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs);
         }
         break;
 
diff --git a/src/exchangedb/plugin_exchangedb_postgres.c 
b/src/exchangedb/plugin_exchangedb_postgres.c
index 0f18ef5..41b5f72 100644
--- a/src/exchangedb/plugin_exchangedb_postgres.c
+++ b/src/exchangedb/plugin_exchangedb_postgres.c
@@ -2242,70 +2242,39 @@ postgres_get_latest_reserve_in_reference (void *cls,
  *                `h_coin_envelope` in the @a collectable to be returned)
  * @param collectable corresponding collectable coin (blind signature)
  *                    if a coin is found
- * @return #GNUNET_SYSERR on internal error
- *         #GNUNET_NO if the collectable was not found
- *         #GNUNET_YES on success
+ * @return statement execution status
  */
-static int
+static enum GNUNET_DB_QueryStatus
 postgres_get_withdraw_info (void *cls,
                             struct TALER_EXCHANGEDB_Session *session,
                             const struct GNUNET_HashCode *h_blind,
                             struct TALER_EXCHANGEDB_CollectableBlindcoin 
*collectable)
 {
-  PGresult *result;
   struct GNUNET_PQ_QueryParam params[] = {
     GNUNET_PQ_query_param_auto_from_type (h_blind),
     GNUNET_PQ_query_param_end
   };
-  int ret;
-
-  ret = GNUNET_SYSERR;
-  result = GNUNET_PQ_exec_prepared (session->conn,
-                                   "get_withdraw_info",
-                                   params);
-
-  if (PGRES_TUPLES_OK != PQresultStatus (result))
-  {
-    QUERY_ERR (result, session->conn);
-    goto cleanup;
-  }
-  if (0 == PQntuples (result))
-  {
-    ret = GNUNET_NO;
-    goto cleanup;
-  }
-  {
-    struct GNUNET_PQ_ResultSpec rs[] = {
-      GNUNET_PQ_result_spec_rsa_public_key ("denom_pub",
-                                           
&collectable->denom_pub.rsa_public_key),
-      GNUNET_PQ_result_spec_rsa_signature ("denom_sig",
-                                          &collectable->sig.rsa_signature),
-      GNUNET_PQ_result_spec_auto_from_type ("reserve_sig",
-                                           &collectable->reserve_sig),
-      GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
-                                           &collectable->reserve_pub),
-      TALER_PQ_result_spec_amount ("amount_with_fee",
-                                   &collectable->amount_with_fee),
-      TALER_PQ_result_spec_amount ("fee_withdraw",
-                                   &collectable->withdraw_fee),
-      GNUNET_PQ_result_spec_end
-    };
+  struct GNUNET_PQ_ResultSpec rs[] = {
+    GNUNET_PQ_result_spec_rsa_public_key ("denom_pub",
+                                         
&collectable->denom_pub.rsa_public_key),
+    GNUNET_PQ_result_spec_rsa_signature ("denom_sig",
+                                        &collectable->sig.rsa_signature),
+    GNUNET_PQ_result_spec_auto_from_type ("reserve_sig",
+                                         &collectable->reserve_sig),
+    GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
+                                         &collectable->reserve_pub),
+    TALER_PQ_result_spec_amount ("amount_with_fee",
+                                &collectable->amount_with_fee),
+    TALER_PQ_result_spec_amount ("fee_withdraw",
+                                &collectable->withdraw_fee),
+    GNUNET_PQ_result_spec_end
+  };
 
-    if (GNUNET_OK !=
-        GNUNET_PQ_extract_result (result,
-                                  rs,
-                                  0))
-    {
-      GNUNET_break (0);
-      goto cleanup;
-    }
-  }
   collectable->h_coin_envelope = *h_blind;
-  ret = GNUNET_YES;
-
- cleanup:
-  PQclear (result);
-  return ret;
+  return GNUNET_PQ_eval_prepared_singleton_select (session->conn,
+                                                  "get_withdraw_info",
+                                                  params,
+                                                  rs);
 }
 
 
@@ -2317,17 +2286,14 @@ postgres_get_withdraw_info (void *cls,
  * @param session database connection to use
  * @param collectable corresponding collectable coin (blind signature)
  *                    if a coin is found
- * @return #GNUNET_SYSERR on internal error
- *         #GNUNET_NO if we failed but should retry the transaction
- *         #GNUNET_YES on success
+ * @return query execution status
  */
-static int
+static enum GNUNET_DB_QueryStatus
 postgres_insert_withdraw_info (void *cls,
                                struct TALER_EXCHANGEDB_Session *session,
                                const struct 
TALER_EXCHANGEDB_CollectableBlindcoin *collectable)
 {
   struct PostgresClosure *pg = cls;
-  PGresult *result;
   struct TALER_EXCHANGEDB_Reserve reserve;
   struct GNUNET_HashCode denom_pub_hash;
   struct GNUNET_TIME_Absolute now;
@@ -2347,28 +2313,27 @@ postgres_insert_withdraw_info (void *cls,
   now = GNUNET_TIME_absolute_get ();
   GNUNET_CRYPTO_rsa_public_key_hash (collectable->denom_pub.rsa_public_key,
                                     &denom_pub_hash);
-  result = GNUNET_PQ_exec_prepared (session->conn,
-                                   "insert_withdraw_info",
-                                   params);
-  if (PGRES_COMMAND_OK != PQresultStatus (result))
+  qs = GNUNET_PQ_eval_prepared_non_select (session->conn,
+                                          "insert_withdraw_info",
+                                          params);
+  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
   {
-    QUERY_ERR (result, session->conn);
-    PQclear (result);
-    return GNUNET_SYSERR;
+    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+    return qs;
   }
-  PQclear (result);
 
   /* update reserve balance */
   reserve.pub = collectable->reserve_pub;
   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
-      postgres_reserve_get (cls,
-                            session,
-                            &reserve))
+      (qs = postgres_reserve_get (cls,
+                                 session,
+                                 &reserve)))
   {
-    /* FIXME: #5010 */
     /* Should have been checked before we got here... */
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
+    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+    if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
+      qs = GNUNET_DB_STATUS_HARD_ERROR;
+    return qs;
   }
   if (GNUNET_SYSERR ==
       TALER_amount_subtract (&reserve.balance,
@@ -2381,7 +2346,7 @@ postgres_insert_withdraw_info (void *cls,
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                 "Withdrawal from reserve `%s' refused due to balance 
missmatch. Retrying.\n",
                 TALER_B2S (&collectable->reserve_pub));
-    return GNUNET_NO;
+    return GNUNET_DB_STATUS_SOFT_ERROR;
   }
   expiry = GNUNET_TIME_absolute_add (now,
                                      pg->idle_reserve_expiration_time);
@@ -2390,13 +2355,13 @@ postgres_insert_withdraw_info (void *cls,
   qs = reserves_update (cls,
                        session,
                        &reserve);
-  if (0 >= qs)
+  GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR != qs);
+  if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
   {
     GNUNET_break (0);
-
-    return GNUNET_SYSERR;
+    qs = GNUNET_DB_STATUS_HARD_ERROR;
   }
-  return GNUNET_OK;
+  return qs;
 }
 
 
diff --git a/src/exchangedb/test_exchangedb.c b/src/exchangedb/test_exchangedb.c
index 8c7e4b9..ac1301d 100644
--- a/src/exchangedb/test_exchangedb.c
+++ b/src/exchangedb/test_exchangedb.c
@@ -1584,7 +1584,7 @@ run (void *cls)
   cbc.amount_with_fee = value;
   GNUNET_assert (GNUNET_OK ==
                  TALER_amount_get_zero (CURRENCY, &cbc.withdraw_fee));
-  FAILIF (GNUNET_OK !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->insert_withdraw_info (plugin->cls,
                                         session,
                                         &cbc));
@@ -1604,7 +1604,7 @@ run (void *cls)
                        &reserve_pub2,
                        sizeof (reserve_pub)));
 
-  FAILIF (GNUNET_YES !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->get_withdraw_info (plugin->cls,
                                      session,
                                      &cbc.h_coin_envelope,
diff --git a/src/include/taler_exchangedb_plugin.h 
b/src/include/taler_exchangedb_plugin.h
index 843c627..af6b554 100644
--- a/src/include/taler_exchangedb_plugin.h
+++ b/src/include/taler_exchangedb_plugin.h
@@ -1232,11 +1232,9 @@ struct TALER_EXCHANGEDB_Plugin
    *                `h_coin_envelope` in the @a collectable to be returned)
    * @param collectable corresponding collectable coin (blind signature)
    *                    if a coin is found
-   * @return #GNUNET_SYSERR on internal error
-   *         #GNUNET_NO if the collectable was not found
-   *         #GNUNET_YES on success
+   * @return statement execution status
    */
-  int
+  enum GNUNET_DB_QueryStatus 
   (*get_withdraw_info) (void *cls,
                         struct TALER_EXCHANGEDB_Session *session,
                         const struct GNUNET_HashCode *h_blind,
@@ -1251,11 +1249,9 @@ struct TALER_EXCHANGEDB_Plugin
    * @param session database connection to use
    * @param collectable corresponding collectable coin (blind signature)
    *                    if a coin is found
-   * @return #GNUNET_SYSERR on internal error
-   *         #GNUNET_NO if the collectable was not found
-   *         #GNUNET_YES on success
+   * @return statement execution status
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*insert_withdraw_info) (void *cls,
                            struct TALER_EXCHANGEDB_Session *session,
                            const struct TALER_EXCHANGEDB_CollectableBlindcoin 
*collectable);

-- 
To stop receiving notification emails like this one, please contact
address@hidden



reply via email to

[Prev in Thread] Current Thread [Next in Thread]