gnunet-svn
[Top][All Lists]
Advanced

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

[GNUnet-SVN] [taler-exchange] branch master updated (7f9e627 -> 3d701e8)


From: gnunet
Subject: [GNUnet-SVN] [taler-exchange] branch master updated (7f9e627 -> 3d701e8)
Date: Mon, 19 Jun 2017 00:18:17 +0200

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

grothoff pushed a change to branch master
in repository exchange.

    from 7f9e627  Styling, no-split and syntax highlighting for texi
     new 75b0879  convert another function for #5010
     new d66a29e  convert another function for #5010
     new d2c7ef5  convert another function for #5010
     new 2ec1b05  refactoring /deposit towards new transaction style (#5010)
     new 2d5b238  rework /reserve/history to address #5010
     new 4cb035c  rename file to match content better
     new dea0f7c  fixing #5010 for /reserve/withdraw
     new 3d701e8  fix leaks and typos

The 8 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 src/exchange-lib/exchange_api_refresh_link.c       |   6 +-
 src/exchange-lib/test_exchange_api.c               |   2 +-
 src/exchange/Makefile.am                           |   3 +-
 src/exchange/taler-exchange-aggregator.c           |  75 +-
 src/exchange/taler-exchange-httpd.c                |   3 +-
 src/exchange/taler-exchange-httpd_db.c             | 619 ++-----------
 src/exchange/taler-exchange-httpd_db.h             |  91 +-
 src/exchange/taler-exchange-httpd_deposit.c        | 218 ++++-
 src/exchange/taler-exchange-httpd_reserve.c        | 188 ----
 src/exchange/taler-exchange-httpd_reserve_status.c | 167 ++++
 ...und.h => taler-exchange-httpd_reserve_status.h} |  31 +-
 .../taler-exchange-httpd_reserve_withdraw.c        | 506 +++++++++++
 ...e.h => taler-exchange-httpd_reserve_withdraw.h} |  30 +-
 src/exchange/taler-exchange-httpd_responses.c      | 145 +---
 src/exchange/taler-exchange-httpd_responses.h      |  70 +-
 src/exchangedb/perf_taler_exchangedb_interpreter.c |  37 +-
 src/exchangedb/plugin_exchangedb_postgres.c        | 959 +++++++++++----------
 src/exchangedb/test_exchangedb.c                   |  23 +-
 src/include/taler_exchangedb_plugin.h              |  52 +-
 19 files changed, 1661 insertions(+), 1564 deletions(-)
 delete mode 100644 src/exchange/taler-exchange-httpd_reserve.c
 create mode 100644 src/exchange/taler-exchange-httpd_reserve_status.c
 copy src/exchange/{taler-exchange-httpd_refund.h => 
taler-exchange-httpd_reserve_status.h} (60%)
 create mode 100644 src/exchange/taler-exchange-httpd_reserve_withdraw.c
 rename src/exchange/{taler-exchange-httpd_reserve.h => 
taler-exchange-httpd_reserve_withdraw.h} (64%)

diff --git a/src/exchange-lib/exchange_api_refresh_link.c 
b/src/exchange-lib/exchange_api_refresh_link.c
index 9b82513..5b2286e 100644
--- a/src/exchange-lib/exchange_api_refresh_link.c
+++ b/src/exchange-lib/exchange_api_refresh_link.c
@@ -372,9 +372,9 @@ handle_refresh_link_finished (void *cls,
  */
 struct TALER_EXCHANGE_RefreshLinkHandle *
 TALER_EXCHANGE_refresh_link (struct TALER_EXCHANGE_Handle *exchange,
-                         const struct TALER_CoinSpendPrivateKeyP *coin_priv,
-                         TALER_EXCHANGE_RefreshLinkCallback link_cb,
-                         void *link_cb_cls)
+                            const struct TALER_CoinSpendPrivateKeyP *coin_priv,
+                            TALER_EXCHANGE_RefreshLinkCallback link_cb,
+                            void *link_cb_cls)
 {
   struct TALER_EXCHANGE_RefreshLinkHandle *rlh;
   CURL *eh;
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/Makefile.am b/src/exchange/Makefile.am
index 92a8556..2c4c5a9 100644
--- a/src/exchange/Makefile.am
+++ b/src/exchange/Makefile.am
@@ -52,7 +52,8 @@ taler_exchange_httpd_SOURCES = \
   taler-exchange-httpd_payback.c taler-exchange-httpd_payback.h \
   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_reserve_withdraw.c 
taler-exchange-httpd_reserve_withdraw.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-aggregator.c 
b/src/exchange/taler-exchange-aggregator.c
index 89f72de..1024019 100644
--- a/src/exchange/taler-exchange-aggregator.c
+++ b/src/exchange/taler-exchange-aggregator.c
@@ -577,6 +577,8 @@ deposit_cb (void *cls,
             struct GNUNET_TIME_Absolute wire_deadline,
             const json_t *wire)
 {
+  enum GNUNET_DB_QueryStatus qs;
+  
   au->merchant_pub = *merchant_pub;
   if (GNUNET_OK !=
       TALER_amount_subtract (&au->total_amount,
@@ -623,11 +625,12 @@ deposit_cb (void *cls,
     GNUNET_break (0);
     return GNUNET_SYSERR;
   }
-  if (GNUNET_OK !=
-      db_plugin->mark_deposit_done (db_plugin->cls,
-                                    au->session,
-                                    row_id))
+  qs = db_plugin->mark_deposit_done (db_plugin->cls,
+                                    au->session,
+                                    row_id);
+  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
   {
+    /* FIXME #5010 */
     GNUNET_break (0);
     au->failed = GNUNET_YES;
     return GNUNET_SYSERR;
@@ -664,6 +667,7 @@ aggregate_cb (void *cls,
               const json_t *wire)
 {
   struct TALER_Amount delta;
+  enum GNUNET_DB_QueryStatus qs;
 
   GNUNET_break (0 ==
                 memcmp (&au->merchant_pub,
@@ -714,11 +718,12 @@ aggregate_cb (void *cls,
     GNUNET_break (0);
     return GNUNET_SYSERR;
   }
-  if (GNUNET_OK !=
-      db_plugin->mark_deposit_done (db_plugin->cls,
-                                    au->session,
-                                    row_id))
+  qs = db_plugin->mark_deposit_done (db_plugin->cls,
+                                    au->session,
+                                    row_id);
+  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
   {
+    /* FIXME: #5010 */
     GNUNET_break (0);
     au->failed = GNUNET_YES;
     return GNUNET_SYSERR;
@@ -1102,7 +1107,7 @@ run_aggregation (void *cls)
 {
   static int swap;
   struct TALER_EXCHANGEDB_Session *session;
-  unsigned int i;
+  enum GNUNET_DB_QueryStatus qs;
   int ret;
   const struct GNUNET_SCHEDULER_TaskContext *tc;
 
@@ -1246,18 +1251,46 @@ run_aggregation (void *cls)
     }
     /* Mark transactions by row_id as minor */
     ret = GNUNET_OK;
-    if (GNUNET_OK !=
-        db_plugin->mark_deposit_tiny (db_plugin->cls,
-                                      session,
-                                      au->row_id))
-      ret = GNUNET_SYSERR;
-    else
-      for (i=0;i<au->rows_offset;i++)
-        if (GNUNET_OK !=
-            db_plugin->mark_deposit_tiny (db_plugin->cls,
-                                          session,
-                                          au->additional_rows[i]))
-          ret = GNUNET_SYSERR;
+    qs = db_plugin->mark_deposit_tiny (db_plugin->cls,
+                                      session,
+                                      au->row_id);
+    if (0 <= qs)
+    {
+      for (unsigned int i=0;i<au->rows_offset;i++)
+      {
+        qs = db_plugin->mark_deposit_tiny (db_plugin->cls,
+                                          session,
+                                          au->additional_rows[i]);
+       if (0 > qs)
+         break;
+      }
+    }
+    if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+    {
+      db_plugin->rollback (db_plugin->cls,
+                          session);
+      GNUNET_free_non_null (au->additional_rows);
+      if (NULL != au->wire)
+       json_decref (au->wire);
+      GNUNET_free (au);
+      au = NULL;
+      /* start again */
+      task = GNUNET_SCHEDULER_add_now (&run_aggregation,
+                                      NULL);
+      return;
+    }
+    if (GNUNET_DB_STATUS_HARD_ERROR == qs)
+    {
+      db_plugin->rollback (db_plugin->cls,
+                          session);
+      GNUNET_free_non_null (au->additional_rows);
+      if (NULL != au->wire)
+       json_decref (au->wire);
+      GNUNET_free (au);
+      au = NULL;
+      GNUNET_SCHEDULER_shutdown ();
+      return;
+    }
     /* commit */
     (void) commit_or_warn (session);
     GNUNET_free_non_null (au->additional_rows);
diff --git a/src/exchange/taler-exchange-httpd.c 
b/src/exchange/taler-exchange-httpd.c
index dbc270b..67fef71 100644
--- a/src/exchange/taler-exchange-httpd.c
+++ b/src/exchange/taler-exchange-httpd.c
@@ -31,7 +31,8 @@
 #include "taler-exchange-httpd_admin.h"
 #include "taler-exchange-httpd_deposit.h"
 #include "taler-exchange-httpd_refund.h"
-#include "taler-exchange-httpd_reserve.h"
+#include "taler-exchange-httpd_reserve_status.h"
+#include "taler-exchange-httpd_reserve_withdraw.h"
 #include "taler-exchange-httpd_payback.h"
 #include "taler-exchange-httpd_wire.h"
 #include "taler-exchange-httpd_refresh.h"
diff --git a/src/exchange/taler-exchange-httpd_db.c 
b/src/exchange/taler-exchange-httpd_db.c
index 71f82e9..298f6c3 100644
--- a/src/exchange/taler-exchange-httpd_db.c
+++ b/src/exchange/taler-exchange-httpd_db.c
@@ -113,19 +113,92 @@ transaction_start_label: /* we will use goto for retries 
*/   \
 
 
 /**
+ * Run a database transaction for @a connection.
+ * Starts a transaction and calls @a cb.  Upon success,
+ * attempts to commit the transaction.  Upon soft failures,
+ * retries @a cb a few times.  Upon hard or persistent soft
+ * errors, generates an error message for @a connection.
+ * 
+ * @param connection MHD connection to run @a cb for
+ * @param[out] set to MHD response code, if transaction failed
+ * @param cb callback implementing transaction logic
+ * @param cb_cls closure for @a cb, must be read-only!
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
+ */
+int
+TEH_DB_run_transaction (struct MHD_Connection *connection,
+                       int *mhd_ret,
+                       TEH_DB_TransactionCallback cb,
+                       void *cb_cls)
+{
+  struct TALER_EXCHANGEDB_Session *session;
+
+  *mhd_ret = -1; /* invalid value */
+  if (NULL == (session = TEH_plugin->get_session (TEH_plugin->cls)))
+  {
+    GNUNET_break (0);
+    *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+                                                    TALER_EC_DB_SETUP_FAILED);
+    return GNUNET_SYSERR;
+  }
+  for (unsigned int retries = 0;retries < MAX_TRANSACTION_COMMIT_RETRIES; 
retries++)
+  {
+    enum GNUNET_DB_QueryStatus qs;
+
+    if (GNUNET_OK !=                                            
+       TEH_plugin->start (TEH_plugin->cls,                     
+                          session))                            
+    {                                      
+      GNUNET_break (0);                                         
+      *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection, 
+                                                      
TALER_EC_DB_START_FAILED);
+      return GNUNET_SYSERR;
+    }
+    qs = cb (cb_cls,
+            connection,
+            session,
+            mhd_ret);
+    if (0 > qs)
+      TEH_plugin->rollback (TEH_plugin->cls,
+                           session);      
+    if (GNUNET_DB_STATUS_HARD_ERROR == qs)
+      return GNUNET_SYSERR;
+    if (0 <= qs)
+      qs = TEH_plugin->commit (TEH_plugin->cls,
+                              session);                              
+    if (GNUNET_DB_STATUS_HARD_ERROR == qs)
+    {
+      *mhd_ret = TEH_RESPONSE_reply_commit_error (connection,
+                                                 
TALER_EC_DB_COMMIT_FAILED_HARD);
+      return GNUNET_SYSERR;
+    }
+    /* make sure callback did not violate invariants! */
+    GNUNET_assert (-1 == *mhd_ret);
+    if (0 <= qs)
+      return GNUNET_OK;
+  }
+  TALER_LOG_WARNING ("Transaction commit failed %u times\n",
+                    MAX_TRANSACTION_COMMIT_RETRIES);
+  *mhd_ret = TEH_RESPONSE_reply_commit_error (connection,
+                                             
TALER_EC_DB_COMMIT_FAILED_ON_RETRY);
+  return GNUNET_SYSERR;
+}
+
+
+/**
  * Calculate the total value of all transactions performed.
  * Stores @a off plus the cost of all transactions in @a tl
  * in @a ret.
  *
  * @param tl transaction list to process
  * @param off offset to use as the starting value
- * @param ret where the resulting total is to be stored
+ * @param[out] ret where the resulting total is to be stored
  * @return #GNUNET_OK on success, #GNUNET_SYSERR on errors
  */
-static int
-calculate_transaction_list_totals (struct TALER_EXCHANGEDB_TransactionList *tl,
-                                   const struct TALER_Amount *off,
-                                   struct TALER_Amount *ret)
+int
+TEH_DB_calculate_transaction_list_totals (struct 
TALER_EXCHANGEDB_TransactionList *tl,
+                                         const struct TALER_Amount *off,
+                                         struct TALER_Amount *ret)
 {
   struct TALER_Amount spent = *off;
   struct TALER_EXCHANGEDB_TransactionList *pos;
@@ -207,133 +280,6 @@ calculate_transaction_list_totals (struct 
TALER_EXCHANGEDB_TransactionList *tl,
 
 
 /**
- * Execute a deposit.  The validity of the coin and signature
- * have already been checked.  The database must now check that
- * the coin is not (double or over) spent, and execute the
- * transaction (record details, generate success or failure response).
- *
- * @param connection the MHD connection to handle
- * @param deposit information about the deposit
- * @return MHD result code
- */
-int
-TEH_DB_execute_deposit (struct MHD_Connection *connection,
-                        const struct TALER_EXCHANGEDB_Deposit *deposit)
-{
-  struct TALER_EXCHANGEDB_Session *session;
-  struct TALER_EXCHANGEDB_TransactionList *tl;
-  struct TALER_Amount spent;
-  struct TALER_Amount value;
-  struct TALER_Amount amount_without_fee;
-  struct TEH_KS_StateHandle *mks;
-  struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki;
-  int ret;
-
-  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);
-  }
-  if (GNUNET_YES ==
-      TEH_plugin->have_deposit (TEH_plugin->cls,
-                                session,
-                                deposit))
-  {
-    GNUNET_assert (GNUNET_OK ==
-                   TALER_amount_subtract (&amount_without_fee,
-                                          &deposit->amount_with_fee,
-                                          &deposit->deposit_fee));
-    return TEH_RESPONSE_reply_deposit_success (connection,
-                                               &deposit->coin.coin_pub,
-                                               &deposit->h_wire,
-                                               &deposit->h_contract_terms,
-                                               deposit->timestamp,
-                                               deposit->refund_deadline,
-                                               &deposit->merchant_pub,
-                                               &amount_without_fee);
-  }
-
-  /* FIXME: move the 'mks'-logic outside of _db.c? */
-  mks = TEH_KS_acquire ();
-  dki = TEH_KS_denomination_key_lookup (mks,
-                                        &deposit->coin.denom_pub,
-                                       TEH_KS_DKU_DEPOSIT);
-  if (NULL == dki)
-  {
-    TEH_KS_release (mks);
-    return TEH_RESPONSE_reply_internal_db_error (connection,
-                                                 
TALER_EC_DEPOSIT_DB_DENOMINATION_KEY_UNKNOWN);
-  }
-  TALER_amount_ntoh (&value,
-                     &dki->issue.properties.value);
-  TEH_KS_release (mks);
-
-  START_TRANSACTION (session, connection);
-
-  /* fee for THIS transaction */
-  spent = deposit->amount_with_fee;
-  /* add cost of all previous transactions */
-  tl = TEH_plugin->get_coin_transactions (TEH_plugin->cls,
-                                          session,
-                                          &deposit->coin.coin_pub);
-  if (GNUNET_OK !=
-      calculate_transaction_list_totals (tl,
-                                         &spent,
-                                         &spent))
-  {
-    TEH_plugin->rollback (TEH_plugin->cls,
-                          session);
-    TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
-                                            tl);
-    return TEH_RESPONSE_reply_internal_db_error (connection,
-                                                
TALER_EC_DEPOSIT_HISTORY_DB_ERROR);
-  }
-  /* Check that cost of all transactions is smaller than
-     the value of the coin. */
-  if (0 < TALER_amount_cmp (&spent,
-                            &value))
-  {
-    TEH_plugin->rollback (TEH_plugin->cls,
-                          session);
-    ret = TEH_RESPONSE_reply_coin_insufficient_funds (connection,
-                                                      
TALER_EC_DEPOSIT_INSUFFICIENT_FUNDS,
-                                                      tl);
-    TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
-                                            tl);
-    return ret;
-  }
-  TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
-                                          tl);
-  if (GNUNET_OK !=
-      TEH_plugin->insert_deposit (TEH_plugin->cls,
-                                  session,
-                                  deposit))
-  {
-    TALER_LOG_WARNING ("Failed to store /deposit information in database\n");
-    TEH_plugin->rollback (TEH_plugin->cls,
-                          session);
-    return TEH_RESPONSE_reply_internal_db_error (connection,
-                                                
TALER_EC_DEPOSIT_STORE_DB_ERROR);
-  }
-
-  COMMIT_TRANSACTION(session, connection);
-  GNUNET_assert (GNUNET_SYSERR !=
-                 TALER_amount_subtract (&amount_without_fee,
-                                        &deposit->amount_with_fee,
-                                        &deposit->deposit_fee));
-  return TEH_RESPONSE_reply_deposit_success (connection,
-                                             &deposit->coin.coin_pub,
-                                             &deposit->h_wire,
-                                             &deposit->h_contract_terms,
-                                             deposit->timestamp,
-                                             deposit->refund_deadline,
-                                             &deposit->merchant_pub,
-                                             &amount_without_fee);
-}
-
-
-/**
  * Execute a "/refund".  Returns a confirmation that the refund
  * was successful, or a failure if we are not aware of a matching
  * /deposit or if it is too late to do the refund.
@@ -354,6 +300,7 @@ TEH_DB_execute_refund (struct MHD_Connection *connection,
   struct TEH_KS_StateHandle *mks;
   struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki;
   struct TALER_Amount expect_fee;
+  enum GNUNET_DB_QueryStatus qs;
   int ret;
   int deposit_found;
   int refund_found;
@@ -582,10 +529,10 @@ TEH_DB_execute_refund (struct MHD_Connection *connection,
                                           tl);
 
   /* Finally, store new refund data */
-  if (GNUNET_OK !=
-      TEH_plugin->insert_refund (TEH_plugin->cls,
-                                 session,
-                                 refund))
+  qs = TEH_plugin->insert_refund (TEH_plugin->cls,
+                                 session,
+                                 refund);
+  if (GNUNET_DB_STATUS_HARD_ERROR == qs)
   {
     TALER_LOG_WARNING ("Failed to store /refund information in database\n");
     TEH_plugin->rollback (TEH_plugin->cls,
@@ -593,391 +540,15 @@ TEH_DB_execute_refund (struct MHD_Connection *connection,
     return TEH_RESPONSE_reply_internal_db_error (connection,
                                                 
TALER_EC_REFUND_STORE_DB_ERROR);
   }
-  COMMIT_TRANSACTION (session, connection);
-
-  return TEH_RESPONSE_reply_refund_success (connection,
-                                            refund);
-}
-
-
-/**
- * Execute a /reserve/status.  Given the public key of a reserve,
- * return the associated transaction history.
- *
- * @param connection the MHD connection to handle
- * @param reserve_pub public key of the reserve to check
- * @return MHD result code
- */
-int
-TEH_DB_execute_reserve_status (struct MHD_Connection *connection,
-                               const struct TALER_ReservePublicKeyP 
*reserve_pub)
-{
-  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
- * @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;
-
-  /* Check if balance is sufficient */
-  START_TRANSACTION (session, connection);
-  rh = TEH_plugin->get_reserve_history (TEH_plugin->cls,
-                                        session,
-                                        reserve);
-  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))
+  if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
   {
-    /* 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;
+    /* FIXME: #5010: retry! */
   }
-  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;
+  return TEH_RESPONSE_reply_refund_success (connection,
+                                            refund);
 }
 
 
@@ -1030,9 +601,9 @@ refresh_check_melt (struct MHD_Connection *connection,
                                           session,
                                           &coin_details->coin_info.coin_pub);
   if (GNUNET_OK !=
-      calculate_transaction_list_totals (tl,
-                                         &spent,
-                                         &spent))
+      TEH_DB_calculate_transaction_list_totals (tl,
+                                               &spent,
+                                               &spent))
   {
     GNUNET_break (0);
     TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
@@ -1336,7 +907,7 @@ refresh_exchange_coin (struct MHD_Connection *connection,
                                    &ev_sig))
   {
     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                "Returning cashed reply for /refresh/reveal signature\n");
+                "Returning cached reply for /refresh/reveal signature\n");
     return ev_sig;
   }
 
@@ -2390,9 +1961,9 @@ TEH_DB_execute_payback (struct MHD_Connection *connection,
   TALER_amount_get_zero (value->currency,
                          &spent);
   if (GNUNET_OK !=
-      calculate_transaction_list_totals (tl,
-                                         &spent,
-                                         &spent))
+      TEH_DB_calculate_transaction_list_totals (tl,
+                                               &spent,
+                                               &spent))
   {
     GNUNET_break (0);
     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 55faafa..e3717bd 100644
--- a/src/exchange/taler-exchange-httpd_db.h
+++ b/src/exchange/taler-exchange-httpd_db.h
@@ -24,70 +24,77 @@
 #include <microhttpd.h>
 #include "taler_exchangedb_plugin.h"
 
-
 /**
- * Execute a "/deposit".  The validity of the coin and signature
- * have already been checked.  The database must now check that
- * the coin is not (double or over) spent, and execute the
- * transaction (record details, generate success or failure response).
+ * Function implementing a database 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 connection the MHD connection to handle
- * @param deposit information about the deposit
- * @return MHD result code
+ * @param cls closure
+ * @param connection MHD request which triggered the transaction
+ * @param session database session to use
+ * @param[out] mhd_ret set to MHD response status for @a connection,
+ *             if transaction failed (!)
+ * @return transaction status
  */
-int
-TEH_DB_execute_deposit (struct MHD_Connection *connection,
-                        const struct TALER_EXCHANGEDB_Deposit *deposit);
+typedef enum GNUNET_DB_QueryStatus
+(*TEH_DB_TransactionCallback)(void *cls,
+                             struct MHD_Connection *connection,
+                             struct TALER_EXCHANGEDB_Session *session,
+                             int *mhd_ret);
 
 
 /**
- * Execute a "/refund".  Returns a confirmation that the refund
- * was successful, or a failure if we are not aware of a matching
- * /deposit or if it is too late to do the refund.
- *
- * @param connection the MHD connection to handle
- * @param refund refund details
- * @return MHD result code
+ * Run a database transaction for @a connection.
+ * Starts a transaction and calls @a cb.  Upon success,
+ * attempts to commit the transaction.  Upon soft failures,
+ * retries @a cb a few times.  Upon hard or persistent soft
+ * errors, generates an error message for @a connection.
+ * 
+ * @param connection MHD connection to run @a cb for
+ * @param[out] set to MHD response code, if transaction failed
+ * @param cb callback implementing transaction logic
+ * @param cb_cls closure for @a cb, must be read-only!
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
  */
 int
-TEH_DB_execute_refund (struct MHD_Connection *connection,
-                       const struct TALER_EXCHANGEDB_Refund *refund);
+TEH_DB_run_transaction (struct MHD_Connection *connection,
+                       int *mhd_ret,
+                       TEH_DB_TransactionCallback cb,
+                       void *cb_cls);
 
 
 /**
- * Execute a "/reserve/status".  Given the public key of a reserve,
- * return the associated transaction history.
+ * Calculate the total value of all transactions performed.
+ * Stores @a off plus the cost of all transactions in @a tl
+ * in @a ret.
  *
- * @param connection the MHD connection to handle
- * @param reserve_pub public key of the reserve to check
- * @return MHD result code
+ * @param tl transaction list to process
+ * @param off offset to use as the starting value
+ * @param[out] ret where the resulting total is to be stored
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on errors
  */
+// FIXME: maybe move to another module?
 int
-TEH_DB_execute_reserve_status (struct MHD_Connection *connection,
-                               const struct TALER_ReservePublicKeyP 
*reserve_pub);
+TEH_DB_calculate_transaction_list_totals (struct 
TALER_EXCHANGEDB_TransactionList *tl,
+                                         const struct TALER_Amount *off,
+                                         struct TALER_Amount *ret);
 
 
 /**
- * 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.
+ * Execute a "/refund".  Returns a confirmation that the refund
+ * was successful, or a failure if we are not aware of a matching
+ * /deposit or if it is too late to do the refund.
  *
  * @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
+ * @param refund refund details
  * @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);
+TEH_DB_execute_refund (struct MHD_Connection *connection,
+                       const struct TALER_EXCHANGEDB_Refund *refund);
 
 
 /**
diff --git a/src/exchange/taler-exchange-httpd_deposit.c 
b/src/exchange/taler-exchange-httpd_deposit.c
index 85504d8..c9c59ed 100644
--- a/src/exchange/taler-exchange-httpd_deposit.c
+++ b/src/exchange/taler-exchange-httpd_deposit.c
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  Copyright (C) 2014, 2015, 2016 Inria and GNUnet e.V.
+  Copyright (C) 2014-2017 Inria and 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
@@ -37,6 +37,174 @@
 
 
 /**
+ * Send confirmation of deposit success to client.  This function
+ * will create a signed message affirming the given information
+ * and return it to the client.  By this, the exchange affirms that
+ * the coin had sufficient (residual) value for the specified
+ * transaction and that it will execute the requested deposit
+ * operation with the given wiring details.
+ *
+ * @param connection connection to the client
+ * @param coin_pub public key of the coin
+ * @param h_wire hash of wire details
+ * @param h_contract_terms hash of contract details
+ * @param timestamp client's timestamp
+ * @param refund_deadline until when this deposit be refunded
+ * @param merchant merchant public key
+ * @param amount_without_fee fraction of coin value to deposit, without the fee
+ * @return MHD result code
+ */
+static int
+reply_deposit_success (struct MHD_Connection *connection,
+                      const struct TALER_CoinSpendPublicKeyP *coin_pub,
+                      const struct GNUNET_HashCode *h_wire,
+                      const struct GNUNET_HashCode *h_contract_terms,
+                      struct GNUNET_TIME_Absolute timestamp,
+                      struct GNUNET_TIME_Absolute refund_deadline,
+                      const struct TALER_MerchantPublicKeyP *merchant,
+                      const struct TALER_Amount *amount_without_fee)
+{
+  struct TALER_DepositConfirmationPS dc;
+  struct TALER_ExchangePublicKeyP pub;
+  struct TALER_ExchangeSignatureP sig;
+
+  dc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_DEPOSIT);
+  dc.purpose.size = htonl (sizeof (struct TALER_DepositConfirmationPS));
+  dc.h_contract_terms = *h_contract_terms;
+  dc.h_wire = *h_wire;
+  dc.timestamp = GNUNET_TIME_absolute_hton (timestamp);
+  dc.refund_deadline = GNUNET_TIME_absolute_hton (refund_deadline);
+  TALER_amount_hton (&dc.amount_without_fee,
+                     amount_without_fee);
+  dc.coin_pub = *coin_pub;
+  dc.merchant = *merchant;
+  TEH_KS_sign (&dc.purpose,
+               &pub,
+               &sig);
+  return TEH_RESPONSE_reply_json_pack (connection,
+                                       MHD_HTTP_OK,
+                                       "{s:s, s:o, s:o}",
+                                       "status", "DEPOSIT_OK",
+                                       "sig", GNUNET_JSON_from_data_auto 
(&sig),
+                                       "pub", GNUNET_JSON_from_data_auto 
(&pub));
+}
+
+
+/**
+ * Closure for #deposit_transaction.
+ */
+struct DepositContext
+{
+  /**
+   * Information about the deposit request.
+   */
+  const struct TALER_EXCHANGEDB_Deposit *deposit;
+
+  /**
+   * Value of the coin.
+   */
+  struct TALER_Amount value;
+  
+};
+
+
+/**
+ * Execute database transaction for /deposit.  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 DepositContext`
+ * @param connection MHD request context
+ * @param session database session and transaction to use
+ * @param[out] mhd_ret set to MHD status on error
+ * @return transaction status
+ */
+static enum GNUNET_DB_QueryStatus
+deposit_transaction (void *cls,
+                    struct MHD_Connection *connection,
+                    struct TALER_EXCHANGEDB_Session *session,
+                    int *mhd_ret)
+{
+  struct DepositContext *dc = cls;
+  const struct TALER_EXCHANGEDB_Deposit *deposit = dc->deposit;
+  struct TALER_EXCHANGEDB_TransactionList *tl;
+  struct TALER_Amount spent;
+  enum GNUNET_DB_QueryStatus qs;
+  
+  qs = TEH_plugin->have_deposit (TEH_plugin->cls,
+                                session,
+                                deposit);
+  if (qs < 0)
+    return qs;
+  if (1 == qs)
+  {
+    struct TALER_Amount amount_without_fee;
+
+    GNUNET_assert (GNUNET_OK ==
+                   TALER_amount_subtract (&amount_without_fee,
+                                          &deposit->amount_with_fee,
+                                          &deposit->deposit_fee));
+    *mhd_ret = reply_deposit_success (connection,
+                                     &deposit->coin.coin_pub,
+                                     &deposit->h_wire,
+                                     &deposit->h_contract_terms,
+                                     deposit->timestamp,
+                                     deposit->refund_deadline,
+                                     &deposit->merchant_pub,
+                                     &amount_without_fee);
+    /* Treat as 'hard' DB error as we want to rollback and
+       never try again. */
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  }
+
+  /* Start with fee for THIS transaction */
+  spent = deposit->amount_with_fee;
+  /* add cost of all previous transactions */
+  tl = TEH_plugin->get_coin_transactions (TEH_plugin->cls,
+                                          session,
+                                          &deposit->coin.coin_pub);
+  if (GNUNET_OK !=
+      TEH_DB_calculate_transaction_list_totals (tl,
+                                               &spent,
+                                               &spent))
+  {
+    TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
+                                            tl);
+    *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+                                                    
TALER_EC_DEPOSIT_HISTORY_DB_ERROR);
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  }
+  /* Check that cost of all transactions is smaller than
+     the value of the coin. */
+  if (0 < TALER_amount_cmp (&spent,
+                            &dc->value))
+  {
+    *mhd_ret = TEH_RESPONSE_reply_coin_insufficient_funds (connection,
+                                                          
TALER_EC_DEPOSIT_INSUFFICIENT_FUNDS,
+                                                          tl);
+    TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
+                                            tl);
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  }
+  TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
+                                          tl);
+  qs = TEH_plugin->insert_deposit (TEH_plugin->cls,
+                                  session,
+                                  deposit);
+  if (GNUNET_DB_STATUS_HARD_ERROR == qs)
+  {
+    TALER_LOG_WARNING ("Failed to store /deposit information in database\n");
+    *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+                                                    
TALER_EC_DEPOSIT_STORE_DB_ERROR);
+  }
+  return qs;
+}
+
+
+/**
  * We have parsed the JSON information about the deposit, do some
  * basic sanity checks (especially that the signature on the coin is
  * valid, and that this type of coin exists) and then execute the
@@ -51,7 +219,13 @@ verify_and_execute_deposit (struct MHD_Connection 
*connection,
                            const struct TALER_EXCHANGEDB_Deposit *deposit)
 {
   struct TALER_DepositRequestPS dr;
+  int mhd_ret;
+  struct TALER_Amount amount_without_fee;
+  struct DepositContext dc;
+  struct TEH_KS_StateHandle *mks;
+  struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki;
 
+  /* check signature */
   dr.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_DEPOSIT);
   dr.purpose.size = htonl (sizeof (struct TALER_DepositRequestPS));
   dr.h_contract_terms = deposit->h_contract_terms;
@@ -76,8 +250,43 @@ verify_and_execute_deposit (struct MHD_Connection 
*connection,
                                                  "coin_sig");
   }
 
-  return TEH_DB_execute_deposit (connection,
-                                 deposit);
+  /* check denomination */
+  mks = TEH_KS_acquire ();
+  dki = TEH_KS_denomination_key_lookup (mks,
+                                        &deposit->coin.denom_pub,
+                                       TEH_KS_DKU_DEPOSIT);
+  if (NULL == dki)
+  {
+    TEH_KS_release (mks);
+    return TEH_RESPONSE_reply_internal_db_error (connection,
+                                                
TALER_EC_DEPOSIT_DB_DENOMINATION_KEY_UNKNOWN);
+  }
+  TALER_amount_ntoh (&dc.value,
+                     &dki->issue.properties.value);
+  TEH_KS_release (mks);
+
+  /* execute transaction */
+  dc.deposit = deposit;
+  if (GNUNET_OK !=
+      TEH_DB_run_transaction (connection,
+                             &mhd_ret,
+                             &deposit_transaction,
+                             &dc))
+    return mhd_ret;
+
+  /* generate regular response */
+  GNUNET_assert (GNUNET_SYSERR !=
+                 TALER_amount_subtract (&amount_without_fee,
+                                        &deposit->amount_with_fee,
+                                        &deposit->deposit_fee));
+  return reply_deposit_success (connection,
+                               &deposit->coin.coin_pub,
+                               &deposit->h_wire,
+                               &deposit->h_contract_terms,
+                               deposit->timestamp,
+                               deposit->refund_deadline,
+                               &deposit->merchant_pub,
+                               &amount_without_fee);
 }
 
 
@@ -199,6 +408,7 @@ TEH_DEPOSIT_handler_deposit (struct TEH_RequestHandler *rh,
     /* FIXME: #3887: if DK was revoked, we might want to give a 403 and not a 
404! */
     TEH_KS_release (key_state);
     TALER_LOG_WARNING ("Unknown denomination key in /deposit request\n");
+    GNUNET_JSON_parse_free (spec);
     return TEH_RESPONSE_reply_arg_unknown (connection,
                                           
TALER_EC_DEPOSIT_DENOMINATION_KEY_UNKNOWN,
                                            "denom_pub");
@@ -211,6 +421,7 @@ TEH_DEPOSIT_handler_deposit (struct TEH_RequestHandler *rh,
   {
     TALER_LOG_WARNING ("Invalid coin passed for /deposit\n");
     TEH_KS_release (key_state);
+    GNUNET_JSON_parse_free (spec);
     return TEH_RESPONSE_reply_signature_invalid (connection,
                                                 
TALER_EC_DEPOSIT_DENOMINATION_SIGNATURE_INVALID,
                                                  "ub_sig");
@@ -223,6 +434,7 @@ TEH_DEPOSIT_handler_deposit (struct TEH_RequestHandler *rh,
                             &deposit.amount_with_fee))
   {
     GNUNET_break_op (0);
+    GNUNET_JSON_parse_free (spec);
     return TEH_RESPONSE_reply_external_error (connection,
                                              
TALER_EC_DEPOSIT_NEGATIVE_VALUE_AFTER_FEE,
                                               "deposited amount smaller than 
depositing fee");
diff --git a/src/exchange/taler-exchange-httpd_reserve.c 
b/src/exchange/taler-exchange-httpd_reserve.c
deleted file mode 100644
index 78f8ff1..0000000
--- a/src/exchange/taler-exchange-httpd_reserve.c
+++ /dev/null
@@ -1,188 +0,0 @@
-/*
-  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.c
- * @brief Handle /reserve/ 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.h"
-#include "taler-exchange-httpd_parsing.h"
-#include "taler-exchange-httpd_responses.h"
-#include "taler-exchange-httpd_keystate.h"
-
-
-/**
- * Handle a "/reserve/status" request.  Parses the
- * given "reserve_pub" argument (which should contain the
- * EdDSA public key of a reserve) and then respond with the
- * status of the reserve.
- *
- * @param rh context of the handler
- * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
- * @return MHD result code
- */
-int
-TEH_RESERVE_handler_reserve_status (struct TEH_RequestHandler *rh,
-                                    struct MHD_Connection *connection,
-                                    void **connection_cls,
-                                    const char *upload_data,
-                                    size_t *upload_data_size)
-{
-  struct TALER_ReservePublicKeyP reserve_pub;
-  int res;
-
-  res = TEH_PARSE_mhd_request_arg_data (connection,
-                                        "reserve_pub",
-                                        &reserve_pub,
-                                        sizeof (struct 
TALER_ReservePublicKeyP));
-  if (GNUNET_SYSERR == res)
-    return MHD_NO; /* internal error */
-  if (GNUNET_NO == res)
-    return MHD_YES; /* parse error */
-  return TEH_DB_execute_reserve_status (connection,
-                                        &reserve_pub);
-}
-
-
-/**
- * Handle a "/reserve/withdraw" request.  Parses the "reserve_pub"
- * EdDSA key of the reserve and the requested "denom_pub" which
- * specifies the key/value of the coin to be withdrawn, and checks
- * that the signature "reserve_sig" makes this a valid withdrawal
- * request from the specified reserve.  If so, the envelope
- * with the blinded coin "coin_ev" is passed down to execute the
- * withdrawl operation.
- *
- * @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_withdraw (struct TEH_RequestHandler *rh,
-                                      struct MHD_Connection *connection,
-                                      void **connection_cls,
-                                      const char *upload_data,
-                                      size_t *upload_data_size)
-{
-  json_t *root;
-  struct TALER_WithdrawRequestPS wsrd;
-  int res;
-  struct TALER_DenominationPublicKey denomination_pub;
-  char *blinded_msg;
-  size_t blinded_msg_len;
-  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),
-    GNUNET_JSON_spec_fixed_auto ("reserve_pub",
-                                 &wsrd.reserve_pub),
-    GNUNET_JSON_spec_fixed_auto ("reserve_sig",
-                                 &signature),
-    TALER_JSON_spec_denomination_public_key ("denom_pub",
-                                             &denomination_pub),
-    GNUNET_JSON_spec_end ()
-  };
-
-  res = TEH_PARSE_post_json (connection,
-                             connection_cls,
-                             upload_data,
-                             upload_data_size,
-                             &root);
-  if (GNUNET_SYSERR == res)
-    return MHD_NO;
-  if ( (GNUNET_NO == res) || (NULL == root) )
-    return MHD_YES;
-  res = TEH_PARSE_json_data (connection,
-                             root,
-                             spec);
-  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)
-  {
-    GNUNET_JSON_parse_free (spec);
-    TEH_KS_release (ks);
-    return TEH_RESPONSE_reply_arg_unknown (connection,
-                                          
TALER_EC_WITHDRAW_DENOMINATION_KEY_NOT_FOUND,
-                                           "denom_pub");
-  }
-  TALER_amount_ntoh (&amount,
-                     &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,
-                     &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);
-  if (GNUNET_OK !=
-      GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW,
-                                  &wsrd.purpose,
-                                  &signature.eddsa_signature,
-                                  &wsrd.reserve_pub.eddsa_pub))
-  {
-    TALER_LOG_WARNING ("Client supplied invalid signature for 
/reserve/withdraw request\n");
-    GNUNET_JSON_parse_free (spec);
-    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);
-  GNUNET_JSON_parse_free (spec);
-  return res;
-}
-
-/* end of taler-exchange-httpd_reserve.c */
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 0000000..f87afa5
--- /dev/null
+++ b/src/exchange/taler-exchange-httpd_reserve_status.c
@@ -0,0 +1,167 @@
+/*
+  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.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"
+
+
+/**
+ * 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
+{
+  /**
+   * 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 = 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_refund.h 
b/src/exchange/taler-exchange-httpd_reserve_status.h
similarity index 60%
copy from src/exchange/taler-exchange-httpd_refund.h
copy to src/exchange/taler-exchange-httpd_reserve_status.h
index 1fa52b1..7bfd4dd 100644
--- a/src/exchange/taler-exchange-httpd_refund.h
+++ b/src/exchange/taler-exchange-httpd_reserve_status.h
@@ -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
@@ -14,26 +14,23 @@
   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
 */
 /**
- * @file taler-exchange-httpd_refund.h
- * @brief Handle /refund requests
+ * @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_REFUND_H
-#define TALER_EXCHANGE_HTTPD_REFUND_H
+#ifndef TALER_EXCHANGE_HTTPD_RESERVE_STATUS_H
+#define TALER_EXCHANGE_HTTPD_RESERVE_STATUS_H
 
-#include <gnunet/gnunet_util_lib.h>
 #include <microhttpd.h>
 #include "taler-exchange-httpd.h"
 
-
 /**
- * Handle a "/refund" request.  Parses the JSON, and, if successful,
- * passes the JSON data to #verify_and_execute_refund() to
- * further check the details of the operation specified.  If
- * everything checks out, this will ultimately lead to the "/refund"
- * being executed, or rejected.
+ * 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
@@ -43,10 +40,10 @@
  * @return MHD result code
   */
 int
-TEH_REFUND_handler_refund (struct TEH_RequestHandler *rh,
-                          struct MHD_Connection *connection,
-                          void **connection_cls,
-                          const char *upload_data,
-                          size_t *upload_data_size);
+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/exchange/taler-exchange-httpd_reserve_withdraw.c 
b/src/exchange/taler-exchange-httpd_reserve_withdraw.c
new file mode 100644
index 0000000..2bc268d
--- /dev/null
+++ b/src/exchange/taler-exchange-httpd_reserve_withdraw.c
@@ -0,0 +1,506 @@
+/*
+  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_withdraw.c
+ * @brief Handle /reserve/withdraw 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_withdraw.h"
+#include "taler-exchange-httpd_parsing.h"
+#include "taler-exchange-httpd_responses.h"
+#include "taler-exchange-httpd_keystate.h"
+
+
+/**
+ * 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
+ * that the signature "reserve_sig" makes this a valid withdrawal
+ * request from the specified reserve.  If so, the envelope
+ * with the blinded coin "coin_ev" is passed down to execute the
+ * withdrawl operation.
+ *
+ * @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_withdraw (struct TEH_RequestHandler *rh,
+                                      struct MHD_Connection *connection,
+                                      void **connection_cls,
+                                      const char *upload_data,
+                                      size_t *upload_data_size)
+{
+  struct WithdrawContext wc;
+  json_t *root;
+  int res;
+  int mhd_ret;
+  struct TALER_Amount amount;
+  struct TALER_Amount fee_withdraw;
+  struct GNUNET_JSON_Specification spec[] = {
+    GNUNET_JSON_spec_varsize ("coin_ev",
+                              (void **) &wc.blinded_msg,
+                              &wc.blinded_msg_len),
+    GNUNET_JSON_spec_fixed_auto ("reserve_pub",
+                                 &wc.wsrd.reserve_pub),
+    GNUNET_JSON_spec_fixed_auto ("reserve_sig",
+                                 &wc.signature),
+    TALER_JSON_spec_denomination_public_key ("denom_pub",
+                                             &wc.denomination_pub),
+    GNUNET_JSON_spec_end ()
+  };
+
+  res = TEH_PARSE_post_json (connection,
+                             connection_cls,
+                             upload_data,
+                             upload_data_size,
+                             &root);
+  if (GNUNET_SYSERR == res)
+    return MHD_NO;
+  if ( (GNUNET_NO == res) || (NULL == root) )
+    return MHD_YES;
+  res = TEH_PARSE_json_data (connection,
+                             root,
+                             spec);
+  json_decref (root);
+  if (GNUNET_OK != res)
+    return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
+  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 (wc.key_state);
+    return TEH_RESPONSE_reply_arg_unknown (connection,
+                                          
TALER_EC_WITHDRAW_DENOMINATION_KEY_NOT_FOUND,
+                                           "denom_pub");
+  }
+  TALER_amount_ntoh (&amount,
+                     &wc.dki->issue.properties.value);
+  TALER_amount_ntoh (&fee_withdraw,
+                     &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);
+  /* verify signature! */
+  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,
+                                  &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");
+  }
+
+  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);
+
+  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_withdraw.c */
diff --git a/src/exchange/taler-exchange-httpd_reserve.h 
b/src/exchange/taler-exchange-httpd_reserve_withdraw.h
similarity index 64%
rename from src/exchange/taler-exchange-httpd_reserve.h
rename to src/exchange/taler-exchange-httpd_reserve_withdraw.h
index 1db7ea7..0d5914d 100644
--- a/src/exchange/taler-exchange-httpd_reserve.h
+++ b/src/exchange/taler-exchange-httpd_reserve_withdraw.h
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  Copyright (C) 2014 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
@@ -14,38 +14,18 @@
   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
 */
 /**
- * @file taler-exchange-httpd_reserve.h
- * @brief Handle /reserve/ requests
+ * @file taler-exchange-httpd_reserve_withdraw.h
+ * @brief Handle /reserve/withdraw requests
  * @author Florian Dold
  * @author Benedikt Mueller
  * @author Christian Grothoff
  */
-#ifndef TALER_EXCHANGE_HTTPD_RESERVE_H
-#define TALER_EXCHANGE_HTTPD_RESERVE_H
+#ifndef TALER_EXCHANGE_HTTPD_RESERVE_WITHDRAW_H
+#define TALER_EXCHANGE_HTTPD_RESERVE_WITHDRAW_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);
-
 
 /**
  * Handle a "/reserve/withdraw" request.  Parses the "reserve_pub"
diff --git a/src/exchange/taler-exchange-httpd_responses.c 
b/src/exchange/taler-exchange-httpd_responses.c
index 01b5606..48df955 100644
--- a/src/exchange/taler-exchange-httpd_responses.c
+++ b/src/exchange/taler-exchange-httpd_responses.c
@@ -462,60 +462,6 @@ TEH_RESPONSE_reply_invalid_json (struct MHD_Connection 
*connection)
 
 
 /**
- * Send confirmation of deposit success to client.  This function
- * will create a signed message affirming the given information
- * and return it to the client.  By this, the exchange affirms that
- * the coin had sufficient (residual) value for the specified
- * transaction and that it will execute the requested deposit
- * operation with the given wiring details.
- *
- * @param connection connection to the client
- * @param coin_pub public key of the coin
- * @param h_wire hash of wire details
- * @param h_contract_terms hash of contract details
- * @param timestamp client's timestamp
- * @param refund_deadline until when this deposit be refunded
- * @param merchant merchant public key
- * @param amount_without_fee fraction of coin value to deposit, without the fee
- * @return MHD result code
- */
-int
-TEH_RESPONSE_reply_deposit_success (struct MHD_Connection *connection,
-                                    const struct TALER_CoinSpendPublicKeyP 
*coin_pub,
-                                    const struct GNUNET_HashCode *h_wire,
-                                    const struct GNUNET_HashCode 
*h_contract_terms,
-                                    struct GNUNET_TIME_Absolute timestamp,
-                                    struct GNUNET_TIME_Absolute 
refund_deadline,
-                                    const struct TALER_MerchantPublicKeyP 
*merchant,
-                                    const struct TALER_Amount 
*amount_without_fee)
-{
-  struct TALER_DepositConfirmationPS dc;
-  struct TALER_ExchangePublicKeyP pub;
-  struct TALER_ExchangeSignatureP sig;
-
-  dc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_DEPOSIT);
-  dc.purpose.size = htonl (sizeof (struct TALER_DepositConfirmationPS));
-  dc.h_contract_terms = *h_contract_terms;
-  dc.h_wire = *h_wire;
-  dc.timestamp = GNUNET_TIME_absolute_hton (timestamp);
-  dc.refund_deadline = GNUNET_TIME_absolute_hton (refund_deadline);
-  TALER_amount_hton (&dc.amount_without_fee,
-                     amount_without_fee);
-  dc.coin_pub = *coin_pub;
-  dc.merchant = *merchant;
-  TEH_KS_sign (&dc.purpose,
-               &pub,
-               &sig);
-  return TEH_RESPONSE_reply_json_pack (connection,
-                                       MHD_HTTP_OK,
-                                       "{s:s, s:o, s:o}",
-                                       "status", "DEPOSIT_OK",
-                                       "sig", GNUNET_JSON_from_data_auto 
(&sig),
-                                       "pub", GNUNET_JSON_from_data_auto 
(&pub));
-}
-
-
-/**
  * Compile the transaction history of a coin into a JSON object.
  *
  * @param tl transaction history to JSON-ify
@@ -739,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;
@@ -1013,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 6a33b65..aef5bf9 100644
--- a/src/exchange/taler-exchange-httpd_responses.h
+++ b/src/exchange/taler-exchange-httpd_responses.h
@@ -244,32 +244,16 @@ TEH_RESPONSE_reply_invalid_json (struct MHD_Connection 
*connectionx);
 
 
 /**
- * Send confirmation of deposit success to client. This function
- * will create a signed message affirming the given information
- * and return it to the client.  By this, the exchange affirms that
- * the coin had sufficient (residual) value for the specified
- * transaction and that it will execute the requested deposit
- * operation with the given wiring details.
+ * Compile the history of a reserve into a JSON object
+ * and calculate the total balance.
  *
- * @param connection connection to the client
- * @param coin_pub public key of the coin
- * @param h_wire hash of wire details
- * @param h_contract_terms hash of proposal data
- * @param timestamp client's timestamp
- * @param refund_deadline until when this deposit be refunded
- * @param merchant merchant public key
- * @param amount_without_fee fraction of coin value to deposit (without fee)
- * @return MHD result code
+ * @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
  */
-int
-TEH_RESPONSE_reply_deposit_success (struct MHD_Connection *connection,
-                                    const struct TALER_CoinSpendPublicKeyP 
*coin_pub,
-                                    const struct GNUNET_HashCode *h_wire,
-                                    const struct GNUNET_HashCode 
*h_contract_terms,
-                                    struct GNUNET_TIME_Absolute timestamp,
-                                    struct GNUNET_TIME_Absolute 
refund_deadline,
-                                    const struct TALER_MerchantPublicKeyP 
*merchant,
-                                    const struct TALER_Amount 
*amount_without_fee);
+json_t *
+TEH_RESPONSE_compile_reserve_history (const struct 
TALER_EXCHANGEDB_ReserveHistory *rh,
+                                     struct TALER_Amount *balance);
 
 
 /**
@@ -441,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 2e43075..5e5c6a3 100644
--- a/src/exchangedb/perf_taler_exchangedb_interpreter.c
+++ b/src/exchangedb/perf_taler_exchangedb_interpreter.c
@@ -1334,15 +1334,15 @@ interpret (struct 
PERF_TALER_EXCHANGEDB_interpreter_state *state)
       case PERF_TALER_EXCHANGEDB_CMD_INSERT_DEPOSIT:
         {
           int deposit_index;
-          int ret;
+          enum GNUNET_DB_QueryStatus qs;
           struct TALER_EXCHANGEDB_Deposit *deposit;
 
           deposit_index = 
state->cmd[state->i].details.insert_deposit.index_deposit;
           deposit = state->cmd[deposit_index].exposed.data.deposit;
-          ret = state->plugin->insert_deposit (state->plugin->cls,
+          qs = state->plugin->insert_deposit (state->plugin->cls,
                                                         state->session,
                                                         deposit);
-          GNUNET_assert (GNUNET_SYSERR != ret);
+          GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs);
           state->cmd[state->i].exposed.data.deposit = deposit;
         }
         break;
@@ -1350,7 +1350,7 @@ interpret (struct PERF_TALER_EXCHANGEDB_interpreter_state 
*state)
       case PERF_TALER_EXCHANGEDB_CMD_GET_DEPOSIT:
         {
           unsigned int source_index;
-          int ret;
+          enum GNUNET_DB_QueryStatus ret;
           struct PERF_TALER_EXCHANGEDB_Data *data;
 
           source_index = 
state->cmd[state->i].details.get_deposit.index_deposit;
@@ -1358,7 +1358,7 @@ interpret (struct PERF_TALER_EXCHANGEDB_interpreter_state 
*state)
           ret = state->plugin->have_deposit (state->plugin->cls,
                                              state->session,
                                              data->data.deposit);
-          GNUNET_assert (GNUNET_SYSERR != ret);
+          GNUNET_assert (0 >= ret);
         }
         break;
 
@@ -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);
@@ -1493,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 510f8ce..41b5f72 100644
--- a/src/exchangedb/plugin_exchangedb_postgres.c
+++ b/src/exchangedb/plugin_exchangedb_postgres.c
@@ -1951,11 +1951,9 @@ postgres_reserve_get (void *cls,
  * @param session the database connection
  * @param reserve the reserve structure whose data will be used to update the
  *          corresponding record in the database.
- * @return #GNUNET_OK upon successful update;
- *         #GNUNET_NO if we failed but should retry the transaction
- *         #GNUNET_SYSERR upon any error
+ * @return transaction status
  */
-static int
+static enum GNUNET_DB_QueryStatus
 reserves_update (void *cls,
                  struct TALER_EXCHANGEDB_Session *session,
                  const struct TALER_EXCHANGEDB_Reserve *reserve)
@@ -1967,9 +1965,9 @@ reserves_update (void *cls,
     GNUNET_PQ_query_param_end
   };
 
-  return execute_prepared_non_select (session,
-                                      "reserve_update",
-                                      params);
+  return GNUNET_PQ_eval_prepared_non_select (session->conn,
+                                            "reserve_update",
+                                            params);
 }
 
 
@@ -2128,7 +2126,8 @@ postgres_reserves_in_insert (void *cls,
        back for duplicate transactions; like this, we should virtually
        never actually have to rollback anything. */
     struct TALER_EXCHANGEDB_Reserve updated_reserve;
-
+    enum GNUNET_DB_QueryStatus qs;
+    
     updated_reserve.pub = reserve.pub;
     if (GNUNET_OK !=
         TALER_amount_add (&updated_reserve.balance,
@@ -2142,11 +2141,17 @@ postgres_reserves_in_insert (void *cls,
     }
     updated_reserve.expiry = GNUNET_TIME_absolute_max (expiry,
                                                        reserve.expiry);
-    if (GNUNET_OK !=
-        reserves_update (cls,
-                         session,
-                         &updated_reserve))
-      goto rollback;
+    qs = reserves_update (cls,
+                         session,
+                         &updated_reserve);
+    if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+      goto rollback; /* FIXME: #5010 */
+    if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
+    {
+      postgres_rollback (cls,
+                        session);
+      return GNUNET_SYSERR;
+    }
   }
   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
       postgres_commit (cls,
@@ -2237,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);
 }
 
 
@@ -2312,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;
@@ -2337,33 +2308,32 @@ postgres_insert_withdraw_info (void *cls,
     TALER_PQ_query_param_amount (&collectable->amount_with_fee),
     GNUNET_PQ_query_param_end
   };
-  int ret;
+  enum GNUNET_DB_QueryStatus qs;
 
   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,
@@ -2376,314 +2346,381 @@ 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);
   reserve.expiry = GNUNET_TIME_absolute_max (expiry,
                                              reserve.expiry);
-  ret = reserves_update (cls,
-                         session,
-                         &reserve);
-  if (GNUNET_SYSERR == ret)
+  qs = reserves_update (cls,
+                       session,
+                       &reserve);
+  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 ret;
+  return qs;
 }
 
 
 /**
- * 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;
 }
 
 
@@ -2693,11 +2730,11 @@ postgres_get_reserve_history (void *cls,
  * @param cls the `struct PostgresClosure` with the plugin-specific state
  * @param session database connection
  * @param deposit deposit to search for
- * @return #GNUNET_YES if we know this operation,
- *         #GNUNET_NO if this exact deposit is unknown to us
- *         #GNUNET_SYSERR on DB error
+ * @return 1 if we know this operation,
+ *         0 if this exact deposit is unknown to us,
+ *         otherwise transaction error status
  */
-static int
+static enum GNUNET_DB_QueryStatus
 postgres_have_deposit (void *cls,
                        struct TALER_EXCHANGEDB_Session *session,
                        const struct TALER_EXCHANGEDB_Deposit *deposit)
@@ -2708,75 +2745,52 @@ postgres_have_deposit (void *cls,
     GNUNET_PQ_query_param_auto_from_type (&deposit->merchant_pub),
     GNUNET_PQ_query_param_end
   };
-  PGresult *result;
-
-  result = GNUNET_PQ_exec_prepared (session->conn,
-                                   "get_deposit",
-                                   params);
-  if (PGRES_TUPLES_OK !=
-      PQresultStatus (result))
-  {
-    BREAK_DB_ERR (result, session->conn);
-    PQclear (result);
-    return GNUNET_SYSERR;
-  }
-  if (0 == PQntuples (result))
-  {
-    PQclear (result);
-    return GNUNET_NO;
-  }
+  struct TALER_EXCHANGEDB_Deposit deposit2;
+  struct GNUNET_PQ_ResultSpec rs[] = {
+    TALER_PQ_result_spec_amount ("amount_with_fee",
+                                &deposit2.amount_with_fee),
+    GNUNET_PQ_result_spec_absolute_time ("timestamp",
+                                        &deposit2.timestamp),
+    GNUNET_PQ_result_spec_absolute_time ("refund_deadline",
+                                        &deposit2.refund_deadline),
+    GNUNET_PQ_result_spec_absolute_time ("wire_deadline",
+                                        &deposit2.wire_deadline),
+    GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
+                                         &deposit2.h_contract_terms),
+    GNUNET_PQ_result_spec_auto_from_type ("h_wire",
+                                         &deposit2.h_wire),
+    GNUNET_PQ_result_spec_end
+  };
+  enum GNUNET_DB_QueryStatus qs;
 
+  qs = GNUNET_PQ_eval_prepared_singleton_select (session->conn,
+                                                "get_deposit",
+                                                params,
+                                                rs);
+  if (0 >= qs)
+    return qs;
   /* Now we check that the other information in @a deposit
      also matches, and if not report inconsistencies. */
-  {
-    struct TALER_EXCHANGEDB_Deposit deposit2;
-    struct GNUNET_PQ_ResultSpec rs[] = {
-      TALER_PQ_result_spec_amount ("amount_with_fee",
-                                   &deposit2.amount_with_fee),
-      GNUNET_PQ_result_spec_absolute_time ("timestamp",
-                                          &deposit2.timestamp),
-      GNUNET_PQ_result_spec_absolute_time ("refund_deadline",
-                                          &deposit2.refund_deadline),
-      GNUNET_PQ_result_spec_absolute_time ("wire_deadline",
-                                          &deposit2.wire_deadline),
-      GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
-                                           &deposit2.h_contract_terms),
-      GNUNET_PQ_result_spec_auto_from_type ("h_wire",
-                                           &deposit2.h_wire),
-      GNUNET_PQ_result_spec_end
-    };
-    if (GNUNET_OK !=
-        GNUNET_PQ_extract_result (result,
-                                  rs,
-                                  0))
-    {
-      GNUNET_break (0);
-      PQclear (result);
-      return GNUNET_SYSERR;
-    }
-    if ( (0 != TALER_amount_cmp (&deposit->amount_with_fee,
-                                 &deposit2.amount_with_fee)) ||
-         (deposit->timestamp.abs_value_us !=
-          deposit2.timestamp.abs_value_us) ||
-         (deposit->refund_deadline.abs_value_us !=
-          deposit2.refund_deadline.abs_value_us) ||
-         (0 != memcmp (&deposit->h_contract_terms,
-                       &deposit2.h_contract_terms,
-                       sizeof (struct GNUNET_HashCode))) ||
-         (0 != memcmp (&deposit->h_wire,
-                       &deposit2.h_wire,
-                       sizeof (struct GNUNET_HashCode))) )
-    {
-      /* Inconsistencies detected! Does not match!  (We might want to
-         expand the API with a 'get_deposit' function to return the
-         original transaction details to be used for an error message
-         in the future!) #3838 */
-      PQclear (result);
-      return GNUNET_NO;
-    }
-  }
-  PQclear (result);
-  return GNUNET_YES;
+  if ( (0 != TALER_amount_cmp (&deposit->amount_with_fee,
+                              &deposit2.amount_with_fee)) ||
+       (deposit->timestamp.abs_value_us !=
+       deposit2.timestamp.abs_value_us) ||
+       (deposit->refund_deadline.abs_value_us !=
+       deposit2.refund_deadline.abs_value_us) ||
+       (0 != memcmp (&deposit->h_contract_terms,
+                    &deposit2.h_contract_terms,
+                    sizeof (struct GNUNET_HashCode))) ||
+       (0 != memcmp (&deposit->h_wire,
+                    &deposit2.h_wire,
+                    sizeof (struct GNUNET_HashCode))) )
+  {
+    /* Inconsistencies detected! Does not match!  (We might want to
+       expand the API with a 'get_deposit' function to return the
+       original transaction details to be used for an error message
+       in the future!) #3838 */
+    return 0; /* Counts as if the transaction was not there */
+  }
+  return 1;
 }
 
 
@@ -2788,11 +2802,9 @@ postgres_have_deposit (void *cls,
  * @param cls the @e cls of this struct with the plugin-specific state
  * @param session connection to the database
  * @param rowid identifies the deposit row to modify
- * @return #GNUNET_OK on success,
- *         #GNUNET_NO on transient error
- *         #GNUNET_SYSERR on error
+ * @return query result status
  */
-static int
+static enum GNUNET_DB_QueryStatus
 postgres_mark_deposit_tiny (void *cls,
                             struct TALER_EXCHANGEDB_Session *session,
                             uint64_t rowid)
@@ -2802,9 +2814,9 @@ postgres_mark_deposit_tiny (void *cls,
     GNUNET_PQ_query_param_end
   };
 
-  return execute_prepared_non_select (session,
-                                      "mark_deposit_tiny",
-                                      params);
+  return GNUNET_PQ_eval_prepared_non_select (session->conn,
+                                            "mark_deposit_tiny",
+                                            params);
 }
 
 
@@ -2885,11 +2897,9 @@ postgres_test_deposit_done (void *cls,
  * @param cls the @e cls of this struct with the plugin-specific state
  * @param session connection to the database
  * @param rowid identifies the deposit row to modify
- * @return #GNUNET_OK on success,
- *         #GNUNET_NO on transient error,
- *         #GNUNET_SYSERR on error
+ * @return query result status
  */
-static int
+static enum GNUNET_DB_QueryStatus
 postgres_mark_deposit_done (void *cls,
                             struct TALER_EXCHANGEDB_Session *session,
                             uint64_t rowid)
@@ -2899,9 +2909,9 @@ postgres_mark_deposit_done (void *cls,
     GNUNET_PQ_query_param_end
   };
 
-  return execute_prepared_non_select (session,
-                                      "mark_deposit_done",
-                                      params);
+  return GNUNET_PQ_eval_prepared_non_select (session->conn,
+                                            "mark_deposit_done",
+                                            params);
 }
 
 
@@ -3180,11 +3190,9 @@ get_known_coin (void *cls,
  * @param cls plugin closure
  * @param session the shared database session
  * @param coin_info the public coin info
- * @return #GNUNET_SYSERR upon error;
- *         #GNUNET_NO on transient error
- *         #GNUNET_OK upon success
+ * @return query result status
  */
-static int
+static enum GNUNET_DB_QueryStatus
 insert_known_coin (void *cls,
                    struct TALER_EXCHANGEDB_Session *session,
                    const struct TALER_CoinPublicInfo *coin_info)
@@ -3199,9 +3207,9 @@ insert_known_coin (void *cls,
 
   GNUNET_CRYPTO_rsa_public_key_hash (coin_info->denom_pub.rsa_public_key,
                                     &denom_pub_hash);
-  return execute_prepared_non_select (session,
-                                      "insert_known_coin",
-                                      params);
+  return GNUNET_PQ_eval_prepared_non_select (session->conn,
+                                            "insert_known_coin",
+                                            params);
 }
 
 
@@ -3211,16 +3219,15 @@ insert_known_coin (void *cls,
  * @param cls the `struct PostgresClosure` with the plugin-specific state
  * @param session connection to the database
  * @param deposit deposit information to store
- * @return #GNUNET_OK on success,
- *         #GNUNET_NO on transient error
- *         #GNUNET_SYSERR on error
+ * @return query result status
  */
-static int
+static enum GNUNET_DB_QueryStatus
 postgres_insert_deposit (void *cls,
                          struct TALER_EXCHANGEDB_Session *session,
                          const struct TALER_EXCHANGEDB_Deposit *deposit)
 {
   int ret;
+  enum GNUNET_DB_QueryStatus qs;
   struct GNUNET_PQ_QueryParam params[] = {
     GNUNET_PQ_query_param_auto_from_type (&deposit->coin.coin_pub),
     TALER_PQ_query_param_amount (&deposit->amount_with_fee),
@@ -3247,19 +3254,19 @@ postgres_insert_deposit (void *cls,
   }
   if (GNUNET_NO == ret)         /* if not, insert it */
   {
-    if (GNUNET_OK !=
-        (ret = insert_known_coin (cls,
-                                  session,
-                                  &deposit->coin)))
+    qs = insert_known_coin (cls,
+                           session,
+                           &deposit->coin);
+    if (0 > qs)
     {
-      GNUNET_break (GNUNET_NO == ret);
-      return ret;
+      GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+      return qs;
     }
   }
 
-  return execute_prepared_non_select (session,
-                                      "insert_deposit",
-                                      params);
+  return GNUNET_PQ_eval_prepared_non_select (session->conn,
+                                            "insert_deposit",
+                                            params);
 }
 
 
@@ -3269,11 +3276,9 @@ postgres_insert_deposit (void *cls,
  * @param cls the @e cls of this struct with the plugin-specific state
  * @param session connection to the database
  * @param refund refund information to store
- * @return #GNUNET_OK on success
- *         #GNUNET_NO on transient error
- *         #GNUNET_SYSERR on error
+ * @return query result status
  */
-static int
+static enum GNUNET_DB_QueryStatus
 postgres_insert_refund (void *cls,
                         struct TALER_EXCHANGEDB_Session *session,
                         const struct TALER_EXCHANGEDB_Refund *refund)
@@ -3291,9 +3296,9 @@ postgres_insert_refund (void *cls,
   GNUNET_assert (GNUNET_YES ==
                  TALER_amount_cmp_currency (&refund->refund_amount,
                                             &refund->refund_fee));
-  return execute_prepared_non_select (session,
-                                      "insert_refund",
-                                      params);
+  return GNUNET_PQ_eval_prepared_non_select (session->conn,
+                                            "insert_refund",
+                                            params);
 }
 
 
@@ -3415,6 +3420,7 @@ postgres_create_refresh_session (void *cls,
     GNUNET_PQ_query_param_end
   };
   int ret;
+  enum GNUNET_DB_QueryStatus qs;
 
   /* check if the coin is already known */
   ret = get_known_coin (cls,
@@ -3428,12 +3434,12 @@ postgres_create_refresh_session (void *cls,
   }
   if (GNUNET_NO == ret)         /* if not, insert it */
   {
-    if (GNUNET_OK !=
-        (ret = insert_known_coin (cls,
-                                  session,
-                                  &refresh_session->melt.coin)))
+    qs = insert_known_coin (cls,
+                           session,
+                           &refresh_session->melt.coin);
+    if (0 > qs)
     {
-      GNUNET_break (GNUNET_NO == ret);
+      GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
       return GNUNET_SYSERR;
     }
   }
@@ -4979,15 +4985,15 @@ postgres_insert_reserve_closed (void *cls,
     return GNUNET_NO;
   }
   GNUNET_break (GNUNET_NO == ret);
-  ret = reserves_update (cls,
-                         session,
-                         &reserve);
-  if (GNUNET_SYSERR == ret)
+  qs = reserves_update (cls,
+                       session,
+                       &reserve);
+  if (0 >= qs)
   {
     GNUNET_break (0);
     return GNUNET_SYSERR;
   }
-  return ret;
+  return GNUNET_OK;
 }
 
 
@@ -6140,6 +6146,7 @@ postgres_insert_payback_request (void *cls,
     GNUNET_PQ_query_param_end
   };
   int ret;
+  enum GNUNET_DB_QueryStatus qs;
 
   /* check if the coin is already known */
   ret = get_known_coin (cls,
@@ -6153,12 +6160,12 @@ postgres_insert_payback_request (void *cls,
   }
   if (GNUNET_NO == ret)         /* if not, insert it */
   {
-    if (GNUNET_OK !=
-        (ret = insert_known_coin (cls,
-                                  session,
-                                  coin)))
+    qs = insert_known_coin (cls,
+                           session,
+                           coin);
+    if (0 > qs)
     {
-      GNUNET_break (GNUNET_NO == ret);
+      GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
       return ret;
     }
   }
@@ -6197,15 +6204,15 @@ postgres_insert_payback_request (void *cls,
                                      pg->idle_reserve_expiration_time);
   reserve.expiry = GNUNET_TIME_absolute_max (expiry,
                                              reserve.expiry);
-  ret = reserves_update (cls,
+  qs = reserves_update (cls,
                          session,
                          &reserve);
-  if (GNUNET_SYSERR == ret)
+  if (0 >= qs)
   {
     GNUNET_break (0);
     return GNUNET_SYSERR;
   }
-  return ret;
+  return GNUNET_OK;
 }
 
 
diff --git a/src/exchangedb/test_exchangedb.c b/src/exchangedb/test_exchangedb.c
index adb7237..ac1301d 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;
@@ -1583,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));
@@ -1603,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,
@@ -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++)
@@ -1768,11 +1771,11 @@ run (void *cls)
   deposit.amount_with_fee = value;
   deposit.deposit_fee = fee_deposit;
   result = 8;
-  FAILIF (GNUNET_OK !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->insert_deposit (plugin->cls,
                                   session,
                                   &deposit));
-  FAILIF (GNUNET_YES !=
+  FAILIF (1 !=
           plugin->have_deposit (plugin->cls,
                                 session,
                                 &deposit));
@@ -1801,7 +1804,7 @@ run (void *cls)
   FAILIF (GNUNET_OK !=
           plugin->start (plugin->cls,
                          session));
-  FAILIF (GNUNET_OK !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->mark_deposit_tiny (plugin->cls,
                                      session,
                                      deposit_rowid));
@@ -1839,13 +1842,13 @@ run (void *cls)
   result = 10;
   deposit2 = deposit;
   RND_BLK (&deposit2.merchant_pub); /* should fail if merchant is different */
-  FAILIF (GNUNET_NO !=
+  FAILIF (0 !=
           plugin->have_deposit (plugin->cls,
                                 session,
                                 &deposit2));
   deposit2.merchant_pub = deposit.merchant_pub;
   RND_BLK (&deposit2.coin.coin_pub); /* should fail if coin is different */
-  FAILIF (GNUNET_NO !=
+  FAILIF (0 !=
           plugin->have_deposit (plugin->cls,
                                 session,
                                 &deposit2));
@@ -1860,7 +1863,7 @@ run (void *cls)
   refund.rtransaction_id = GNUNET_CRYPTO_random_u64 
(GNUNET_CRYPTO_QUALITY_WEAK, UINT64_MAX);
   refund.refund_amount = deposit.amount_with_fee;
   refund.refund_fee = fee_refund;
-  FAILIF (GNUNET_OK !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->insert_refund (plugin->cls,
                                  session,
                                  &refund));
diff --git a/src/include/taler_exchangedb_plugin.h 
b/src/include/taler_exchangedb_plugin.h
index 924bfe7..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);
@@ -1268,12 +1264,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);
 
 
   /**
@@ -1293,11 +1291,11 @@ struct TALER_EXCHANGEDB_Plugin
    * @param cls the @e cls of this struct with the plugin-specific state
    * @param session database connection
    * @param deposit deposit to search for
-   * @return #GNUNET_YES if we know this operation,
-   *         #GNUNET_NO if this exact deposit is unknown to us,
-   *         #GNUNET_SYSERR on DB error
+   * @return 1 if we know this operation,
+   *         0 if this exact deposit is unknown to us,
+   *         otherwise transaction error status
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*have_deposit) (void *cls,
                    struct TALER_EXCHANGEDB_Session *session,
                    const struct TALER_EXCHANGEDB_Deposit *deposit);
@@ -1309,11 +1307,9 @@ struct TALER_EXCHANGEDB_Plugin
    * @param cls the @e cls of this struct with the plugin-specific state
    * @param session connection to the database
    * @param deposit deposit information to store
-   * @return #GNUNET_OK on success,
-   *         #GNUNET_NO on transient error
-   *         #GNUNET_SYSERR on error
+   * @return query result status
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*insert_deposit) (void *cls,
                      struct TALER_EXCHANGEDB_Session *session,
                      const struct TALER_EXCHANGEDB_Deposit *deposit);
@@ -1325,11 +1321,9 @@ struct TALER_EXCHANGEDB_Plugin
    * @param cls the @e cls of this struct with the plugin-specific state
    * @param session connection to the database
    * @param refund refund information to store
-   * @return #GNUNET_OK on success,
-   *         #GNUNET_NO on transient error
-   *         #GNUNET_SYSERR on error
+   * @return query result status
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*insert_refund) (void *cls,
                     struct TALER_EXCHANGEDB_Session *session,
                     const struct TALER_EXCHANGEDB_Refund *refund);
@@ -1343,11 +1337,9 @@ struct TALER_EXCHANGEDB_Plugin
    * @param cls the @e cls of this struct with the plugin-specific state
    * @param session connection to the database
    * @param deposit_rowid identifies the deposit row to modify
-   * @return #GNUNET_OK on success
-   *         #GNUNET_NO on transient error
-   *         #GNUNET_SYSERR on error
+   * @return query result status
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*mark_deposit_tiny) (void *cls,
                         struct TALER_EXCHANGEDB_Session *session,
                         uint64_t rowid);
@@ -1377,11 +1369,9 @@ struct TALER_EXCHANGEDB_Plugin
    * @param cls the @e cls of this struct with the plugin-specific state
    * @param session connection to the database
    * @param deposit_rowid identifies the deposit row to modify
-   * @return #GNUNET_OK on success,
-   *         #GNUNET_NO on transient error
-   *         #GNUNET_SYSERR on error
+   * @return query result status
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*mark_deposit_done) (void *cls,
                         struct TALER_EXCHANGEDB_Session *session,
                         uint64_t rowid);

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



reply via email to

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