gnunet-svn
[Top][All Lists]
Advanced

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

[GNUnet-SVN] [taler-exchange] branch stable updated (850d84a -> e140b41)


From: gnunet
Subject: [GNUnet-SVN] [taler-exchange] branch stable updated (850d84a -> e140b41)
Date: Thu, 06 Jul 2017 15:06:34 +0200

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

marcello pushed a change to branch stable
in repository exchange.

    from 850d84a  assert non-NULL
     add 0c52279  bump versions for release
     add 780b716  add missing files for distcheck
     add cad6476  match GNUnet API rename, work on #5010
     add a57cf67  a bit more work on #5010
     add 110d0cc  start test case for #4956
     add 30f1d7b  add new test to ignore files
     add 0ef7116  do not show -v option twice in -h
     add 1eb739c  add --test option to taler-exchange-wirewatch
     add 16b7c26  modify fakebank API to allow arbitrary subjects, not just 
well-fromed WTIDs
     add becb713  fix doxygen
     add ba17729  tolerate empty transaction list when returning history
     add 0580168  automated refunds of expired reserves now work and tested, 
fixes #4956
     add 5176b25  error code for merchant's /refund
     add 19633be  error code for merchant's /refund
     add d7e7cf5  error code for merchant's /refund
     add 274ec61  error code for merchant's /refund
     add b874a4c  remove dead or unnecessary macros
     add eb35468  fix compiler warning due to possibly unknown switch enum 
values being handled poorly
     add 0e6c4ba  work on #5010
     add 7f9e627  Styling, no-split and syntax highlighting for texi
     add 75b0879  convert another function for #5010
     add d66a29e  convert another function for #5010
     add d2c7ef5  convert another function for #5010
     add 2ec1b05  refactoring /deposit towards new transaction style (#5010)
     add 2d5b238  rework /reserve/history to address #5010
     add 4cb035c  rename file to match content better
     add dea0f7c  fixing #5010 for /reserve/withdraw
     add 3d701e8  fix leaks and typos
     add 92e6744  address #5010 for /refund
     add 51ee201  address #5010 for /payback
     add ae7850a  split tracking into track_transfer.c and track_transaction.c
     add beea8eb  refactor /track logic towards new structure
     add a0c66e7  fix #5010 for /track/transfer
     add 703c54a  fix #5010 for /track/transaction
     add f8e6214  split httpd_refresh.c into refresh_melt, refresh_link and 
refresh_reveal
     add 247f82c  move reply_transfer_pending to where it belongs
     add 39db1ae  address #5010 for /refresh/link
     add 0530964  fixing #5010 for /refresh/reveal
     add 87e1654  incomplete work on fixing #5010 for /refresh/melt
     add fbff951  address #5010 for /refresh/melt
     add d4884c0  Fix #5010 for keystate
     add ff86339  adapt /admin/add/incoming to #5010
     add 6acb027  address #5010 for wirewatch
     add 9aa2e94  working on #5010 for aggregator
     add 0599b3b  need extra font to build on my system
     add 2d662e3  fix #5010 for taler-exchange-aggregator
     add c42d544  convert rest of exchangedb plugin API to fix #5010-issues
     add 560051e  migrating auditordb to new API to address #5010
     add 4c6ef74  update taler-auditor to fix #5010 in auditor as well, 
technically done, but untested
     add ba95b05  log interesting errors always
     add 0ba8825  expand set of EC codes to better cover merchant /refund 
(likely still not perfect)
     add f048de9  preparations for #4840
     add d77c416  implement logic to check protocol version compatibility 
(#5035)
     add f995079  eliminate dead macros
     add 5d6dfde  fix #4955 in auditordb, clean up fix in exchangedb
     add 932d2b3  signature macro to confirm refunds
     add 34887e5  importing a 'PS' struct from merchant
     add 1910d66  tolerate total-amount==fee in aggregator
     add e140b41  log amount involved

No new revisions were added by this update.

Summary of changes:
 ChangeLog                                          |    7 +
 README                                             |   13 +-
 configure.ac                                       |    4 +-
 doc/Makefile.am                                    |    5 +-
 doc/brown-paper.css                                |   63 +
 doc/docstyle.css                                   |   76 +
 doc/highlight.pack.js                              |    2 +
 doc/paper/taler.tex                                |    1 +
 doc/syntax.texi                                    |   44 +
 doc/taler-exchange-wirewatch.1                     |    3 +
 doc/taler-exchange.texi                            |    4 +
 src/auditor/taler-auditor.c                        |  954 +--
 src/auditordb/plugin_auditordb_postgres.c          | 2782 +++----
 src/auditordb/test_auditordb.c                     |  120 +-
 src/bank-lib/Makefile.am                           |    2 +-
 src/bank-lib/bank_api_common.c                     |    5 +-
 src/bank-lib/fakebank.c                            |  137 +-
 src/bank-lib/test_bank_interpreter.c               |   50 +-
 src/benchmark/taler-exchange-benchmark.c           |    4 +-
 src/exchange-lib/Makefile.am                       |    2 +-
 src/exchange-lib/exchange_api_handle.c             |   77 +-
 src/exchange-lib/exchange_api_refresh_link.c       |    6 +-
 src/exchange-lib/test_exchange_api.c               |   32 +-
 src/exchange/.gitignore                            |    1 +
 src/exchange/Makefile.am                           |   28 +-
 src/exchange/taler-exchange-aggregator.c           |  831 ++-
 src/exchange/taler-exchange-httpd.c                |   12 +-
 src/exchange/taler-exchange-httpd_admin.c          |  147 +-
 src/exchange/taler-exchange-httpd_db.c             | 2414 +-----
 src/exchange/taler-exchange-httpd_db.h             |  264 +-
 src/exchange/taler-exchange-httpd_deposit.c        |  221 +-
 src/exchange/taler-exchange-httpd_keystate.c       |  231 +-
 src/exchange/taler-exchange-httpd_payback.c        |  276 +-
 src/exchange/taler-exchange-httpd_refresh.c        |  755 --
 src/exchange/taler-exchange-httpd_refresh.h        |   94 -
 src/exchange/taler-exchange-httpd_refresh_link.c   |  326 +
 ...posit.h => taler-exchange-httpd_refresh_link.h} |   25 +-
 src/exchange/taler-exchange-httpd_refresh_melt.c   |  918 +++
 ...efund.h => taler-exchange-httpd_refresh_melt.h} |   32 +-
 src/exchange/taler-exchange-httpd_refresh_reveal.c |  769 ++
 ...und.h => taler-exchange-httpd_refresh_reveal.h} |   32 +-
 src/exchange/taler-exchange-httpd_refund.c         |  357 +-
 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      |  691 +-
 src/exchange/taler-exchange-httpd_responses.h      |  344 +-
 .../taler-exchange-httpd_track_transaction.c       |  368 +
 ....h => taler-exchange-httpd_track_transaction.h} |   26 +-
 src/exchange/taler-exchange-httpd_track_transfer.c |  486 ++
 ...ing.h => taler-exchange-httpd_track_transfer.h} |   27 +-
 src/exchange/taler-exchange-httpd_tracking.c       |  159 -
 src/exchange/taler-exchange-wirewatch.c            |   77 +-
 ...=> test-taler-exchange-wirewatch-postgres.conf} |    4 +-
 src/exchange/test_taler_exchange_aggregator.c      |   10 +-
 src/exchange/test_taler_exchange_wirewatch.c       |  848 +++
 src/exchangedb/perf_taler_exchangedb_interpreter.c |  109 +-
 src/exchangedb/plugin_exchangedb_postgres.c        | 7851 ++++++++++----------
 src/exchangedb/test_exchangedb.c                   |  218 +-
 src/include/taler_auditordb_plugin.h               |  137 +-
 src/include/taler_error_codes.h                    |   62 +-
 src/include/taler_exchange_service.h               |   63 +-
 src/include/taler_exchangedb_plugin.h              |  317 +-
 src/include/taler_fakebank_lib.h                   |   27 +-
 src/include/taler_signatures.h                     |   24 +
 src/json/Makefile.am                               |    2 +-
 src/util/Makefile.am                               |    2 +-
 src/wire/Makefile.am                               |    5 +-
 70 files changed, 12752 insertions(+), 12153 deletions(-)
 create mode 100644 doc/brown-paper.css
 create mode 100644 doc/docstyle.css
 create mode 100644 doc/highlight.pack.js
 create mode 100644 doc/syntax.texi
 delete mode 100644 src/exchange/taler-exchange-httpd_refresh.c
 delete mode 100644 src/exchange/taler-exchange-httpd_refresh.h
 create mode 100644 src/exchange/taler-exchange-httpd_refresh_link.c
 copy src/exchange/{taler-exchange-httpd_deposit.h => 
taler-exchange-httpd_refresh_link.h} (63%)
 create mode 100644 src/exchange/taler-exchange-httpd_refresh_melt.c
 copy src/exchange/{taler-exchange-httpd_refund.h => 
taler-exchange-httpd_refresh_melt.h} (60%)
 create mode 100644 src/exchange/taler-exchange-httpd_refresh_reveal.c
 copy src/exchange/{taler-exchange-httpd_refund.h => 
taler-exchange-httpd_refresh_reveal.h} (55%)
 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%)
 create mode 100644 src/exchange/taler-exchange-httpd_track_transaction.c
 copy src/exchange/{taler-exchange-httpd_tracking.h => 
taler-exchange-httpd_track_transaction.h} (65%)
 create mode 100644 src/exchange/taler-exchange-httpd_track_transfer.c
 rename src/exchange/{taler-exchange-httpd_tracking.h => 
taler-exchange-httpd_track_transfer.h} (64%)
 delete mode 100644 src/exchange/taler-exchange-httpd_tracking.c
 copy src/exchange/{test-taler-exchange-aggregator-postgres.conf => 
test-taler-exchange-wirewatch-postgres.conf} (94%)
 create mode 100644 src/exchange/test_taler_exchange_wirewatch.c

diff --git a/ChangeLog b/ChangeLog
index ee0cae8..c161add 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,10 @@
+Sun Jun 11 17:03:56 CEST 2017
+       Finish implementation and testing of automated refunding
+       of expired reserves (#4956). -CG
+
+Tue Jun  6 13:53:34 CEST 2017
+       Releasing taler-exchange 0.3.0. -CG
+
 Mon Apr 17 01:29:07 CEST 2017
        Add support for HTTP body compression (#4982). -CG
 
diff --git a/README b/README
index 78665b5..d5d76eb 100644
--- a/README
+++ b/README
@@ -17,11 +17,14 @@ Taler is currently developed by a worldwide group of 
independent free
 software developers and the DECENTRALISE team at Inria Rennes.  Taler
 is free software and a GNU package (http://www.gnu.org/).
 
-This is not even a release yet, but some raw development prototype
-that does not work yet.  This package also only includes the Taler
-exchange, not the other components of the system.
+This is an alpha release with a few known bugs, lacking a few
+important features, documentation, testing, performance tuning and an
+external security audit.  However, you can run the code and it largely
+works fine.  that does not work yet.  This package also only includes
+the Taler exchange, not the other components of the system.
 
 Documentation about Taler can be found at http://taler.net/.
+Our bug tracker is at https://gnunet.org/bugs/.
 
 
 Dependencies:
@@ -30,8 +33,8 @@ Dependencies:
 These are the direct dependencies for running a Taler exchange:
 
 - GNUnet            >= 0.10.2
-- GNU libmicrohttpd >= 0.9.38
-- Postgres          >= 9.3
+- GNU libmicrohttpd >= 0.9.55
+- Postgres          >= 9.5
 
 
 
diff --git a/configure.ac b/configure.ac
index 715e94e..65efe78 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2,7 +2,7 @@
 # Process this file with autoconf to produce a configure script.
 #
 #  This file is part of TALER
-#  Copyright (C) 2014, 2015, 2016 GNUnet e.V. and Inria
+#  Copyright (C) 2014, 2015, 2016, 2017 GNUnet e.V. and Inria
 #
 #  TALER is free software; you can redistribute it and/or modify it under the
 #  terms of the GNU General Public License as published by the Free Software
@@ -17,7 +17,7 @@
 #
 #
 AC_PREREQ([2.69])
-AC_INIT([taler-exchange], [0.2.0], address@hidden)
+AC_INIT([taler-exchange], [0.3.0], address@hidden)
 AC_CONFIG_SRCDIR([src/util/util.c])
 AC_CONFIG_HEADERS([taler_config.h])
 # support for non-recursive builds
diff --git a/doc/Makefile.am b/doc/Makefile.am
index f8d04db..f8c8741 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -16,6 +16,8 @@ man_MANS = \
   taler-exchange-wirewatch.1 \
   taler.conf.5
 
+AM_MAKEINFOHTMLFLAGS = --no-split --css-ref=docstyle.css 
--css-ref=brown-paper.css
+
 DISTCLEANFILES = \
   taler-exchange.cps \
   taler-exchange.dvi
@@ -24,7 +26,8 @@ info_TEXINFOS = \
   taler-exchange.texi
 extra_TEXINFOS = \
   fdl-1.3.texi \
-  agpl.texi
+  agpl.texi \
+  syntax.texi
 EXTRA_DIST = \
   coding-style.txt \
   $(man_MANS) \
diff --git a/doc/brown-paper.css b/doc/brown-paper.css
new file mode 100644
index 0000000..65e2e79
--- /dev/null
+++ b/doc/brown-paper.css
@@ -0,0 +1,63 @@
+/*
+
+Brown Paper style from goldblog.com.ua (c) Zaripov Yura <address@hidden>
+
+*/
+
+.hljs {
+  display: block;
+  overflow-x: auto;
+  padding: 0.5em;
+}
+
+.hljs-keyword,
+.hljs-selector-tag,
+.hljs-literal {
+  color:#005599;
+  font-weight:bold;
+}
+
+.hljs,
+.hljs-subst {
+  color: #363c69;
+}
+
+.hljs-string,
+.hljs-title,
+.hljs-section,
+.hljs-type,
+.hljs-attribute,
+.hljs-symbol,
+.hljs-bullet,
+.hljs-built_in,
+.hljs-addition,
+.hljs-variable,
+.hljs-template-tag,
+.hljs-template-variable,
+.hljs-link,
+.hljs-name {
+  color: #2c009f;
+}
+
+.hljs-comment,
+.hljs-quote,
+.hljs-meta,
+.hljs-deletion {
+  color: #802022;
+}
+
+.hljs-keyword,
+.hljs-selector-tag,
+.hljs-literal,
+.hljs-doctag,
+.hljs-title,
+.hljs-section,
+.hljs-type,
+.hljs-name,
+.hljs-strong {
+  font-weight: bold;
+}
+
+.hljs-emphasis {
+  font-style: italic;
+}
diff --git a/doc/docstyle.css b/doc/docstyle.css
new file mode 100644
index 0000000..c91d3f7
--- /dev/null
+++ b/doc/docstyle.css
@@ -0,0 +1,76 @@
+html, body {
+   font-size: 1em;
+   text-align: left;
+   text-decoration: none;
+}
+html { background-color: #e7e7e7; }
+
+body {
+   max-width: 74.92em;
+   margin: 0 auto;
+   padding: .5em 1em 1em 1em;
+   background-color: white;
+   border: .1em solid #c0c0c0;
+}
+
+h1, h2, h3, h4 { color: #333; }
+h5, h6, dt { color: #222; }
+
+
+a h3 {
+  color: #005090;
+}
+
+a[href] { color: #005090; }
+a[href]:visited { color: #100070; }
+a[href]:active, a[href]:hover {
+   color: #100070;
+   text-decoration: none;
+}
+
+.linkrow {
+  margin: 3em 0;
+}
+
+.linkrow {
+  text-align: center;
+}
+
+div.example { padding: .8em 1.2em .4em; }
+pre.example { padding: .8em 1.2em; }
+div.example, pre.example {
+   margin: 1em 0 1em 3% ;
+   -webkit-border-radius: .3em;
+   -moz-border-radius: .3em;
+   border-radius: .3em;
+   border: 1px solid #d4cbb6;
+   background-color: #f2efe4;
+}
+div.example > pre.example {
+   padding: 0 0 .4em;
+   margin: 0;
+   border: none;
+}
+
+
+/* This makes the very long tables of contents in Gnulib and other
+   manuals easier to read. */
+.contents ul, .shortcontents ul { font-weight: bold; }
+.contents ul ul, .shortcontents ul ul { font-weight: normal; }
+.contents ul { list-style: none; }
+
+/* For colored navigation bars (Emacs manual): make the bar extend
+   across the whole width of the page and give it a decent height. */
+.header, .node { margin: 0 -1em; padding: 0 1em; }
+.header p, .node p { line-height: 2em; }
+
+/* For navigation links */
+.node a, .header a { display: inline-block; line-height: 2em; }
+.node a:hover, .header a:hover { background: #f2efe4; }
+
+table.cartouche {
+  border-collapse: collapse;
+  border-color: darkred;
+  border-style: solid;
+  border-width: 3px;
+}
diff --git a/doc/highlight.pack.js b/doc/highlight.pack.js
new file mode 100644
index 0000000..b2f9ff2
--- /dev/null
+++ b/doc/highlight.pack.js
@@ -0,0 +1,2 @@
+/*! highlight.js v9.12.0 | BSD3 License | git.io/hljslicense */
+!function(e){var n="object"==typeof window&&window||"object"==typeof 
self&&self;"undefined"!=typeof 
exports?e(exports):n&&(n.hljs=e({}),"function"==typeof 
define&&define.amd&&define([],function(){return n.hljs}))}(function(e){function 
n(e){return 
e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")}function 
t(e){return e.nodeName.toLowerCase()}function r(e,n){var t=e&&e.exec(n);return 
t&&0===t.index}function a(e){return k.test(e)}function i(e){var 
n,t,r,i,o=e.className+" ";i [...]
\ No newline at end of file
diff --git a/doc/paper/taler.tex b/doc/paper/taler.tex
index bfe8987..f341d46 100644
--- a/doc/paper/taler.tex
+++ b/doc/paper/taler.tex
@@ -58,6 +58,7 @@
 \usetikzlibrary{calc}
 \usepackage{eurosym}
 \usepackage[T1]{fontenc}
+\usepackage{lmodern}
 \usepackage{verbatim}
 \usepackage[utf8]{inputenc}
 
diff --git a/doc/syntax.texi b/doc/syntax.texi
new file mode 100644
index 0000000..8aca39d
--- /dev/null
+++ b/doc/syntax.texi
@@ -0,0 +1,44 @@
address@hidden Syntax highlighting for texinfo's HTML output
+
address@hidden
+<script src="highlight.pack.js"></script>
+<script>
+var hls = [];
+var syntaxAuto = true;
+addEventListener("DOMContentLoaded", function() {
+  // Highlight blocks with fixed language
+  for (let x of hls) {
+    let next = x[0].nextElementSibling;
+    console.log("next", next);
+    let blocks = next.querySelectorAll("pre.example");
+    for (let i = 0; i < blocks.length; i++) {
+      blocks[i].classList.add("language-" + x[1]);
+      hljs.highlightBlock(blocks[i]);
+    }
+  } 
+  // auto-detect other blocks if not disabled
+  if (syntaxAuto) {
+    let blocks = document.querySelectorAll("pre.example");
+    for (let i = 0; i < blocks.length; i++) {
+      hljs.highlightBlock(blocks[i]);
+    }
+  }
+});
+</script>
address@hidden html
+
address@hidden setsyntax{lang}
address@hidden
+<script>
+hls.push([document.currentScript, "\lang\"]);
+</script>
address@hidden html
address@hidden macro
+
address@hidden setsyntaxnoauto{}
address@hidden
+<script>
+syntaxAuto = false;
+</script>
address@hidden html
address@hidden macro
diff --git a/doc/taler-exchange-wirewatch.1 b/doc/taler-exchange-wirewatch.1
index 72006e6..41a4afa 100644
--- a/doc/taler-exchange-wirewatch.1
+++ b/doc/taler-exchange-wirewatch.1
@@ -19,6 +19,9 @@ Use the specified wire plugin and its configuration to talk 
to the bank.
 .IP "\-h, \-\-help"
 Print short help on options.
 .B
+.IP "\-T,  \-\-test"
+Run in test mode and exit when idle.
+.B
 .IP "\-v, \-\-version"
 Print version information.
 .B
diff --git a/doc/taler-exchange.texi b/doc/taler-exchange.texi
index c9b1b30..97a0d61 100644
--- a/doc/taler-exchange.texi
+++ b/doc/taler-exchange.texi
@@ -6,6 +6,8 @@
 @paragraphindent 0
 @exampleindent 4
 
address@hidden syntax.texi
+
 @copying
 This manual is for the GNU Taler Exchange
 (version @value{VERSION}, @value{UPDATED}),
@@ -306,6 +308,7 @@ If you did not specify a prefix, GNUnet will install to
 
 To download and install the GNU Taler exchange, proceeds as follows:
 
address@hidden shell
 @example
 $ git clone git://taler.net/exchange
 $ cd exchange
@@ -555,6 +558,7 @@ via an environment variable: 
@cite{TALER_EXCHANGEDB_POSTGRES_CONFIG}.
 via configuration option @cite{db_conn_str}, under section 
@cite{[exchangedb-BACKEND]}. For example, the demo exchange is configured as 
follows:
 @end itemize
 
address@hidden ini
 @example
 [exchange]
 ...
diff --git a/src/auditor/taler-auditor.c b/src/auditor/taler-auditor.c
index 42a167d..f7ea947 100644
--- a/src/auditor/taler-auditor.c
+++ b/src/auditor/taler-auditor.c
@@ -351,16 +351,16 @@ static struct GNUNET_CONTAINER_MultiHashMap 
*denominations;
  * @param[out] dki set to detailed information about @a denom_pub, NULL if not 
found, must
  *                 NOT be freed by caller
  * @param[out] dh set to the hash of @a denom_pub, may be NULL
- * @return #GNUNET_OK on success, #GNUNET_NO for not found, #GNUNET_SYSERR for 
DB error
+ * @return transaction status code
  */
-static int
+static enum GNUNET_DB_QueryStatus
 get_denomination_info (const struct TALER_DenominationPublicKey *denom_pub,
                        const struct 
TALER_EXCHANGEDB_DenominationKeyInformationP **dki,
                        struct GNUNET_HashCode *dh)
 {
   struct GNUNET_HashCode hc;
   struct TALER_EXCHANGEDB_DenominationKeyInformationP *dkip;
-  int ret;
+  enum GNUNET_DB_QueryStatus qs;
 
   if (NULL == dh)
     dh = &hc;
@@ -375,19 +375,18 @@ get_denomination_info (const struct 
TALER_DenominationPublicKey *denom_pub,
   {
     /* cache hit */
     *dki = dkip;
-    return GNUNET_OK;
+    return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
   }
   dkip = GNUNET_new (struct TALER_EXCHANGEDB_DenominationKeyInformationP);
-  ret = edb->get_denomination_info (edb->cls,
-                                    esession,
-                                    denom_pub,
-                                    dkip);
-  if (GNUNET_OK != ret)
+  qs = edb->get_denomination_info (edb->cls,
+                                  esession,
+                                  denom_pub,
+                                  dkip);
+  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
   {
     GNUNET_free (dkip);
-    GNUNET_break (GNUNET_NO == ret);
     *dki = NULL;
-    return ret;
+    return qs;
   }
   {
     struct TALER_Amount value;
@@ -405,7 +404,7 @@ get_denomination_info (const struct 
TALER_DenominationPublicKey *denom_pub,
                                                     dh,
                                                     dkip,
                                                     
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
-  return GNUNET_OK;
+  return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
 }
 
 
@@ -516,28 +515,28 @@ struct ReserveSummary
  * initialized (so we can determine the currency).
  *
  * @param[in,out] rs reserve summary to (fully) initialize
- * @return #GNUNET_OK on success, #GNUNET_SYSERR on DB errors
+ * @return transaction status code
  */
-static int
+static enum GNUNET_DB_QueryStatus
 load_auditor_reserve_summary (struct ReserveSummary *rs)
 {
-  int ret;
+  enum GNUNET_DB_QueryStatus qs;
   uint64_t rowid;
 
-  ret = adb->get_reserve_info (adb->cls,
-                               asession,
-                               &rs->reserve_pub,
-                               &master_pub,
-                               &rowid,
-                               &rs->a_balance,
-                               &rs->a_withdraw_fee_balance,
-                               &rs->a_expiration_date);
-  if (GNUNET_SYSERR == ret)
+  qs = adb->get_reserve_info (adb->cls,
+                             asession,
+                             &rs->reserve_pub,
+                             &master_pub,
+                             &rowid,
+                             &rs->a_balance,
+                             &rs->a_withdraw_fee_balance,
+                             &rs->a_expiration_date);
+  if (0 > qs)
   {
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
+    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+    return qs;
   }
-  if (GNUNET_NO == ret)
+  if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
   {
     rs->had_ri = GNUNET_NO;
     GNUNET_assert (GNUNET_OK ==
@@ -550,7 +549,7 @@ load_auditor_reserve_summary (struct ReserveSummary *rs)
                 "Creating fresh reserve `%s' with starting balance %s\n",
                 TALER_B2S (&rs->reserve_pub),
                 TALER_amount2s (&rs->a_balance));
-    return GNUNET_OK;
+    return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
   }
   rs->had_ri = GNUNET_YES;
   if ( (GNUNET_YES !=
@@ -565,13 +564,13 @@ load_auditor_reserve_summary (struct ReserveSummary *rs)
                               "currencies for reserve differ");
     /* TODO: find a sane way to continue... */
     GNUNET_break (0);
-    return GNUNET_SYSERR;
+    return GNUNET_DB_STATUS_HARD_ERROR;
   }
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Auditor remembers reserve `%s' has balance %s\n",
               TALER_B2S (&rs->reserve_pub),
               TALER_amount2s (&rs->a_balance));
-  return GNUNET_OK;
+  return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
 }
 
 
@@ -603,6 +602,11 @@ struct ReserveContext
    */
   struct TALER_Amount total_fee_balance;
 
+  /**
+   * Transaction status code, set to error codes if applicable.
+   */
+  enum GNUNET_DB_QueryStatus qs;
+  
 };
 
 
@@ -633,6 +637,7 @@ handle_reserve_in (void *cls,
   struct GNUNET_HashCode key;
   struct ReserveSummary *rs;
   struct GNUNET_TIME_Absolute expiry;
+  enum GNUNET_DB_QueryStatus qs;
 
   /* should be monotonically increasing */
   GNUNET_assert (rowid >= pp.last_reserve_in_serial_id);
@@ -654,11 +659,11 @@ handle_reserve_in (void *cls,
     GNUNET_assert (GNUNET_OK ==
                    TALER_amount_get_zero (credit->currency,
                                           &rs->total_fee));
-    if (GNUNET_OK !=
-        load_auditor_reserve_summary (rs))
+    if (0 > (qs = load_auditor_reserve_summary (rs)))
     {
       GNUNET_break (0);
       GNUNET_free (rs);
+      rc->qs = qs;
       return GNUNET_SYSERR;
     }
     GNUNET_assert (GNUNET_OK ==
@@ -720,26 +725,28 @@ handle_reserve_out (void *cls,
   struct TALER_Amount withdraw_fee;
   struct GNUNET_TIME_Absolute valid_start;
   struct GNUNET_TIME_Absolute expire_withdraw;
-  int ret;
+  enum GNUNET_DB_QueryStatus qs;
 
   /* should be monotonically increasing */
   GNUNET_assert (rowid >= pp.last_reserve_out_serial_id);
   pp.last_reserve_out_serial_id = rowid + 1;
 
   /* lookup denomination pub data (make sure denom_pub is valid, establish 
fees) */
-  ret = get_denomination_info (denom_pub,
-                               &dki,
-                               &wsrd.h_denomination_pub);
-  if (GNUNET_SYSERR == ret)
+  qs = get_denomination_info (denom_pub,
+                             &dki,
+                             &wsrd.h_denomination_pub);
+  if (0 > qs)
   {
-    GNUNET_break (0);
+    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+    rc->qs = qs;
     return GNUNET_SYSERR;
   }
-  if (GNUNET_NO == ret)
+  if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
   {
     report_row_inconsistency ("withdraw",
                               rowid,
                               "denomination key not found (foreign key 
constraint violated)");
+    rc->qs = GNUNET_DB_STATUS_HARD_ERROR;
     return GNUNET_OK;
   }
 
@@ -790,11 +797,12 @@ handle_reserve_out (void *cls,
     GNUNET_assert (GNUNET_OK ==
                    TALER_amount_get_zero (amount_with_fee->currency,
                                           &rs->total_fee));
-    if (GNUNET_OK !=
-        load_auditor_reserve_summary (rs))
+    qs = load_auditor_reserve_summary (rs);
+    if (0 > qs)
     {
-      GNUNET_break (0);
+      GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
       GNUNET_free (rs);
+      rc->qs = qs;
       return GNUNET_SYSERR;
     }
     GNUNET_assert (GNUNET_OK ==
@@ -856,7 +864,7 @@ handle_payback_by_reserve (void *cls,
   struct TALER_PaybackRequestPS pr;
   struct TALER_MasterSignatureP msig;
   uint64_t rev_rowid;
-  int ret;
+  enum GNUNET_DB_QueryStatus qs;
   const char *rev;
 
   /* should be monotonically increasing */
@@ -892,17 +900,18 @@ handle_payback_by_reserve (void *cls,
                                           &pr.h_denom_pub);
   if (NULL == rev)
   {
-    ret = edb->get_denomination_revocation (edb->cls,
-                                           esession,
-                                           &pr.h_denom_pub,
-                                           &msig,
-                                           &rev_rowid);
-    if (GNUNET_SYSERR == ret)
+    qs = edb->get_denomination_revocation (edb->cls,
+                                          esession,
+                                          &pr.h_denom_pub,
+                                          &msig,
+                                          &rev_rowid);
+    if (0 > qs)
     {
-      GNUNET_break (0);
+      GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+      rc->qs = qs;
       return GNUNET_SYSERR;
     }
-    if (GNUNET_NO == ret)
+    if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
     {
       report_row_inconsistency ("payback",
                                rowid,
@@ -955,11 +964,12 @@ handle_payback_by_reserve (void *cls,
     GNUNET_assert (GNUNET_OK ==
                    TALER_amount_get_zero (amount->currency,
                                           &rs->total_fee));
-    if (GNUNET_OK !=
-        load_auditor_reserve_summary (rs))
+    qs = load_auditor_reserve_summary (rs);
+    if (0 > qs)
     {
-      GNUNET_break (0);
+      GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
       GNUNET_free (rs);
+      rc->qs = qs;
       return GNUNET_SYSERR;
     }
     GNUNET_assert (GNUNET_OK ==
@@ -1014,6 +1024,7 @@ handle_reserve_closed (void *cls,
   struct ReserveContext *rc = cls;
   struct GNUNET_HashCode key;
   struct ReserveSummary *rs;
+  enum GNUNET_DB_QueryStatus qs;
 
   /* should be monotonically increasing */
   GNUNET_assert (rowid >= pp.last_reserve_close_serial_id);
@@ -1033,11 +1044,12 @@ handle_reserve_closed (void *cls,
     GNUNET_assert (GNUNET_OK ==
                    TALER_amount_get_zero (amount_with_fee->currency,
                                           &rs->total_in));
-    if (GNUNET_OK !=
-        load_auditor_reserve_summary (rs))
+    qs = load_auditor_reserve_summary (rs);
+    if (0 > qs)
     {
-      GNUNET_break (0);
+      GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
       GNUNET_free (rs);
+      rc->qs = qs;
       return GNUNET_SYSERR;
     }
     GNUNET_assert (GNUNET_OK ==
@@ -1085,14 +1097,15 @@ verify_reserve_balance (void *cls,
   struct ReserveSummary *rs = value;
   struct TALER_EXCHANGEDB_Reserve reserve;
   struct TALER_Amount balance;
+  enum GNUNET_DB_QueryStatus qs;
   int ret;
 
   ret = GNUNET_OK;
   reserve.pub = rs->reserve_pub;
-  if (GNUNET_OK !=
-      edb->reserve_get (edb->cls,
-                        esession,
-                        &reserve))
+  qs = edb->reserve_get (edb->cls,
+                         esession,
+                         &reserve);
+  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
   {
     char *diag;
 
@@ -1103,6 +1116,12 @@ verify_reserve_balance (void *cls,
                               UINT64_MAX,
                               diag);
     GNUNET_free (diag);
+    if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
+    {
+      GNUNET_break (0);
+      qs = GNUNET_DB_STATUS_HARD_ERROR;
+    }
+    rc->qs = qs;
     return GNUNET_OK;
   }
 
@@ -1199,19 +1218,15 @@ verify_reserve_balance (void *cls,
                   "Final balance of reserve `%s' is %s, dropping it\n",
                   TALER_B2S (&rs->reserve_pub),
                   TALER_amount2s (&balance));
-      ret = adb->del_reserve_info (adb->cls,
-                                   asession,
-                                   &rs->reserve_pub,
-                                   &master_pub);
-      if (GNUNET_SYSERR == ret)
-      {
-        GNUNET_break (0);
-        goto cleanup;
-      }
-      if (GNUNET_NO == ret)
+      qs = adb->del_reserve_info (adb->cls,
+                                 asession,
+                                 &rs->reserve_pub,
+                                 &master_pub);
+      if (0 >= qs)
       {
-        GNUNET_break (0);
-        ret = GNUNET_SYSERR;
+        GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+       ret = GNUNET_SYSERR;
+       rc->qs = qs;
         goto cleanup;
       }
     }
@@ -1232,22 +1247,27 @@ verify_reserve_balance (void *cls,
               TALER_amount2s (&balance));
 
   if (rs->had_ri)
-    ret = adb->update_reserve_info (adb->cls,
-                                    asession,
-                                    &rs->reserve_pub,
-                                    &master_pub,
-                                    &balance,
-                                    &rs->a_withdraw_fee_balance,
-                                    rs->a_expiration_date);
+    qs = adb->update_reserve_info (adb->cls,
+                                  asession,
+                                  &rs->reserve_pub,
+                                  &master_pub,
+                                  &balance,
+                                  &rs->a_withdraw_fee_balance,
+                                  rs->a_expiration_date);
   else
-    ret = adb->insert_reserve_info (adb->cls,
-                                    asession,
-                                    &rs->reserve_pub,
-                                    &master_pub,
-                                    &balance,
-                                    &rs->a_withdraw_fee_balance,
-                                    rs->a_expiration_date);
-
+    qs = adb->insert_reserve_info (adb->cls,
+                                  asession,
+                                  &rs->reserve_pub,
+                                  &master_pub,
+                                  &balance,
+                                  &rs->a_withdraw_fee_balance,
+                                  rs->a_expiration_date);
+  if (0 >= qs)
+  {
+    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+    ret = GNUNET_SYSERR;
+    rc->qs = qs;
+  }
  cleanup:
   GNUNET_assert (GNUNET_YES ==
                  GNUNET_CONTAINER_multihashmap_remove (rc->reserves,
@@ -1262,27 +1282,29 @@ verify_reserve_balance (void *cls,
  * Analyze reserves for being well-formed.
  *
  * @param cls NULL
- * @return #GNUNET_OK on success, #GNUNET_SYSERR on invariant violation
+ * @return transaction status code
  */
-static int
+static enum GNUNET_DB_QueryStatus
 analyze_reserves (void *cls)
 {
   struct ReserveContext rc;
-  int ret;
+  enum GNUNET_DB_QueryStatus qsx;
+  enum GNUNET_DB_QueryStatus qs;
 
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Analyzing reserves\n");
-  ret = adb->get_reserve_summary (adb->cls,
-                                  asession,
-                                  &master_pub,
-                                  &rc.total_balance,
-                                  &rc.total_fee_balance);
-  if (GNUNET_SYSERR == ret)
+  rc.qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
+  qsx = adb->get_reserve_summary (adb->cls,
+                                 asession,
+                                 &master_pub,
+                                 &rc.total_balance,
+                                 &rc.total_fee_balance);
+  if (qsx < 0)
   {
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
+    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qsx);
+    return qsx;
   }
-  if (GNUNET_NO == ret)
+  if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qsx)
   {
     GNUNET_assert (GNUNET_OK ==
                    TALER_amount_get_zero (currency,
@@ -1297,45 +1319,45 @@ analyze_reserves (void *cls)
   rc.revoked = GNUNET_CONTAINER_multihashmap_create (4,
                                                     GNUNET_NO);
 
-  if (GNUNET_SYSERR ==
-      edb->select_reserves_in_above_serial_id (edb->cls,
-                                               esession,
-                                               pp.last_reserve_in_serial_id,
-                                               &handle_reserve_in,
-                                               &rc))
+  qs = edb->select_reserves_in_above_serial_id (edb->cls,
+                                               esession,
+                                               pp.last_reserve_in_serial_id,
+                                               &handle_reserve_in,
+                                               &rc);
+  if (qs < 0)
   {
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
+    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+    return qs;
   }
-  if (GNUNET_SYSERR ==
-      edb->select_reserves_out_above_serial_id (edb->cls,
-                                                esession,
-                                                pp.last_reserve_out_serial_id,
-                                                &handle_reserve_out,
-                                                &rc))
+  qs = edb->select_reserves_out_above_serial_id (edb->cls,
+                                                esession,
+                                                pp.last_reserve_out_serial_id,
+                                                &handle_reserve_out,
+                                                &rc);
+  if (qs < 0)
   {
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
+    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+    return qs;
   }
-  if (GNUNET_SYSERR ==
-      edb->select_payback_above_serial_id (edb->cls,
-                                           esession,
-                                           pp.last_reserve_payback_serial_id,
-                                           &handle_payback_by_reserve,
-                                           &rc))
+  qs = edb->select_payback_above_serial_id (edb->cls,
+                                           esession,
+                                           pp.last_reserve_payback_serial_id,
+                                           &handle_payback_by_reserve,
+                                           &rc);
+  if (qs < 0)
   {
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
+    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+    return qs;
   }
-  if (GNUNET_SYSERR ==
-      edb->select_reserve_closed_above_serial_id (edb->cls,
-                                                 esession,
-                                                 
pp.last_reserve_close_serial_id,
-                                                 &handle_reserve_closed,
-                                                 &rc))
+  qs = edb->select_reserve_closed_above_serial_id (edb->cls,
+                                                  esession,
+                                                  
pp.last_reserve_close_serial_id,
+                                                  &handle_reserve_closed,
+                                                  &rc);
+  if (qs < 0)
   {
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
+    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+    return qs;
   }
 
   GNUNET_CONTAINER_multihashmap_iterate (rc.reserves,
@@ -1346,31 +1368,33 @@ analyze_reserves (void *cls)
   GNUNET_CONTAINER_multihashmap_destroy (rc.reserves);
   GNUNET_CONTAINER_multihashmap_destroy (rc.revoked);
 
+  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != rc.qs)
+    return qs;
 
-  if (GNUNET_NO == ret)
+  if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qsx)
   {
-    ret = adb->insert_reserve_summary (adb->cls,
-                                       asession,
-                                       &master_pub,
-                                       &rc.total_balance,
-                                       &rc.total_fee_balance);
+    qs = adb->insert_reserve_summary (adb->cls,
+                                     asession,
+                                     &master_pub,
+                                     &rc.total_balance,
+                                     &rc.total_fee_balance);
   }
   else
   {
-    ret = adb->update_reserve_summary (adb->cls,
-                                       asession,
-                                       &master_pub,
-                                       &rc.total_balance,
-                                       &rc.total_fee_balance);
+    qs = adb->update_reserve_summary (adb->cls,
+                                     asession,
+                                     &master_pub,
+                                     &rc.total_balance,
+                                     &rc.total_fee_balance);
   }
-  if (GNUNET_OK != ret)
+  if (0 >= qs)
   {
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
+    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+    return qs;
   }
   report_reserve_balance (&rc.total_balance,
                           &rc.total_fee_balance);
-  return GNUNET_OK;
+  return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
 }
 
 
@@ -1476,7 +1500,7 @@ struct AggregationContext
   /**
    * Final result status.
    */
-  int ret;
+  enum GNUNET_DB_QueryStatus qs;
 };
 
 
@@ -1550,9 +1574,9 @@ struct WireCheckContext
   const char *method;
 
   /**
-   * Set to #GNUNET_SYSERR if there are inconsistencies.
+   * Database transaction status.
    */
-  int ok;
+  enum GNUNET_DB_QueryStatus qs;
 
 };
 
@@ -1862,14 +1886,17 @@ wire_transfer_information_cb (void *cls,
   struct TALER_Amount coin_value_without_fee;
   struct TALER_EXCHANGEDB_TransactionList *tl;
   const struct TALER_CoinPublicInfo *coin;
+  enum GNUNET_DB_QueryStatus qs;
 
   /* Obtain coin's transaction history */
-  tl = edb->get_coin_transactions (edb->cls,
+  qs = edb->get_coin_transactions (edb->cls,
                                    esession,
-                                   coin_pub);
-  if (NULL == tl)
+                                   coin_pub,
+                                  &tl);
+  if ( (qs < 0) ||
+       (NULL == tl) )
   {
-    wcc->ok = GNUNET_SYSERR;
+    wcc->qs = qs;
     report_row_inconsistency ("aggregation",
                               rowid,
                               "no transaction history for coin claimed in 
aggregation");
@@ -1894,23 +1921,23 @@ wire_transfer_information_cb (void *cls,
     break;
   }
   GNUNET_assert (NULL != coin); /* hard check that switch worked */
-  if (GNUNET_OK !=
-      get_denomination_info (&coin->denom_pub,
-                             &dki,
-                             NULL))
+  qs = get_denomination_info (&coin->denom_pub,
+                             &dki,
+                             NULL);
+  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
   {
-    /* This should be impossible from database constraints */
-    GNUNET_break (0);
+    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     edb->free_coin_transaction_list (edb->cls,
                                      tl);
-    wcc->ok = GNUNET_SYSERR;
+    wcc->qs = qs;
     report_row_inconsistency ("aggregation",
                               rowid,
                               "could not find denomination key for coin 
claimed in aggregation");
     return;
   }
 
-  /* Check transaction history to see if it supports aggregate valuation */
+  /* Check transaction history to see if it supports aggregate
+     valuation */
   check_transaction_history (coin_pub,
                              h_contract_terms,
                              merchant_pub,
@@ -1923,7 +1950,7 @@ wire_transfer_information_cb (void *cls,
                              coin_value,
                              coin_fee))
   {
-    wcc->ok = GNUNET_SYSERR;
+    wcc->qs = GNUNET_DB_STATUS_HARD_ERROR;
     report_row_inconsistency ("aggregation",
                               rowid,
                               "inconsistent coin value and fee claimed in 
aggregation");
@@ -1933,7 +1960,7 @@ wire_transfer_information_cb (void *cls,
       TALER_amount_cmp (&computed_value,
                         &coin_value_without_fee))
   {
-    wcc->ok = GNUNET_SYSERR;
+    wcc->qs = GNUNET_DB_STATUS_HARD_ERROR;
     report_row_inconsistency ("aggregation",
                               rowid,
                               "coin transaction history and aggregation 
disagree about coin's contribution");
@@ -1942,7 +1969,7 @@ wire_transfer_information_cb (void *cls,
       TALER_amount_cmp (&computed_fees,
                         coin_fee))
   {
-    wcc->ok = GNUNET_SYSERR;
+    wcc->qs = GNUNET_DB_STATUS_HARD_ERROR;
     report_row_inconsistency ("aggregation",
                               rowid,
                               "coin transaction history and aggregation 
disagree about applicable fees");
@@ -1954,7 +1981,7 @@ wire_transfer_information_cb (void *cls,
   if (0 != strcmp (wire_method,
                    wcc->method))
   {
-    wcc->ok = GNUNET_SYSERR;
+    wcc->qs = GNUNET_DB_STATUS_HARD_ERROR;
     report_row_inconsistency ("aggregation",
                               rowid,
                               "wire method of aggregate do not match wire 
transfer");
@@ -1963,7 +1990,7 @@ wire_transfer_information_cb (void *cls,
                    &wcc->h_wire,
                    sizeof (struct GNUNET_HashCode)))
   {
-    wcc->ok = GNUNET_SYSERR;
+    wcc->qs = GNUNET_DB_STATUS_HARD_ERROR;
     report_row_inconsistency ("aggregation",
                               rowid,
                               "account details of aggregate do not match 
account details of wire transfer");
@@ -1973,7 +2000,7 @@ wire_transfer_information_cb (void *cls,
   {
     /* This should be impossible from database constraints */
     GNUNET_break (0);
-    wcc->ok = GNUNET_SYSERR;
+    wcc->qs = GNUNET_DB_STATUS_HARD_ERROR;
     report_row_inconsistency ("aggregation",
                               rowid,
                               "date given in aggregate does not match wire 
transfer date");
@@ -1984,7 +2011,7 @@ wire_transfer_information_cb (void *cls,
                              coin_value,
                              coin_fee))
   {
-    wcc->ok = GNUNET_SYSERR;
+    wcc->qs = GNUNET_DB_STATUS_HARD_ERROR;
     report_row_inconsistency ("aggregation",
                               rowid,
                               "could not calculate contribution of coin");
@@ -1992,10 +2019,15 @@ wire_transfer_information_cb (void *cls,
   }
 
   /* Add coin's contribution to total aggregate value */
-  GNUNET_assert (GNUNET_OK ==
-                 TALER_amount_add (&wcc->total_deposits,
-                                   &wcc->total_deposits,
-                                   &contribution));
+  if (GNUNET_OK !=
+      TALER_amount_add (&wcc->total_deposits,
+                       &wcc->total_deposits,
+                       &contribution))
+  {
+    GNUNET_break (0);
+    wcc->qs = GNUNET_DB_STATUS_HARD_ERROR;
+    return;
+  }
 }
 
 
@@ -2028,7 +2060,7 @@ get_wire_fee (struct AggregationContext *ac,
 
   /* Lookup fee in exchange database */
   wfi = GNUNET_new (struct WireFeeInfo);
-  if (GNUNET_OK !=
+  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
       edb->get_wire_fee (edb->cls,
                          esession,
                          type,
@@ -2123,6 +2155,7 @@ check_wire_out_cb (void *cls,
   const struct TALER_Amount *wire_fee;
   struct TALER_Amount final_amount;
   struct TALER_Amount exchange_gain;
+  enum GNUNET_DB_QueryStatus qs;
 
   /* should be monotonically increasing */
   GNUNET_assert (rowid >= pp.last_wire_out_serial_id);
@@ -2145,18 +2178,24 @@ check_wire_out_cb (void *cls,
     return GNUNET_OK;
   }
   wcc.method = json_string_value (method);
-  wcc.ok = GNUNET_OK;
+  wcc.qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
   wcc.date = date;
   TALER_amount_get_zero (amount->currency,
                          &wcc.total_deposits);
   TALER_JSON_hash (wire,
                    &wcc.h_wire);
-  edb->lookup_wire_transfer (edb->cls,
-                             esession,
-                             wtid,
-                             &wire_transfer_information_cb,
-                             &wcc);
-  if (GNUNET_OK != wcc.ok)
+  qs = edb->lookup_wire_transfer (edb->cls,
+                                 esession,
+                                 wtid,
+                                 &wire_transfer_information_cb,
+                                 &wcc);
+  if (0 > qs)
+  {
+    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+    ac->qs = qs;
+    return GNUNET_SYSERR;
+  }
+  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != wcc.qs)
   {
     report_row_inconsistency ("wire_out",
                               rowid,
@@ -2171,7 +2210,7 @@ check_wire_out_cb (void *cls,
   if (NULL == wire_fee)
   {
     GNUNET_break (0);
-    ac->ret = GNUNET_SYSERR;
+    ac->qs = GNUNET_DB_STATUS_HARD_ERROR;
     return GNUNET_SYSERR;
   }
   if (GNUNET_SYSERR ==
@@ -2212,7 +2251,7 @@ check_wire_out_cb (void *cls,
                              &final_amount))
   {
     GNUNET_break (0);
-    ac->ret = GNUNET_SYSERR;
+    ac->qs = GNUNET_DB_STATUS_HARD_ERROR;
     return GNUNET_SYSERR;
   }
 
@@ -2223,7 +2262,7 @@ check_wire_out_cb (void *cls,
                         &exchange_gain))
   {
     GNUNET_break (0);
-    ac->ret = GNUNET_SYSERR;
+    ac->qs = GNUNET_DB_STATUS_HARD_ERROR;
     return GNUNET_SYSERR;
   }
 
@@ -2249,45 +2288,47 @@ check_wire_out_cb (void *cls,
  * Analyze the exchange aggregator's payment processing.
  *
  * @param cls closure
- * @return #GNUNET_OK on success, #GNUNET_SYSERR on hard errors
+ * @return transaction status code
  */
-static int
+static enum GNUNET_DB_QueryStatus
 analyze_aggregations (void *cls)
 {
   struct AggregationContext ac;
   struct WirePlugin *wc;
   struct WireFeeInfo *wfi;
-  int have_balance;
+  enum GNUNET_DB_QueryStatus qsx;
+  enum GNUNET_DB_QueryStatus qs;
 
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Analyzing aggregations\n");
-  ac.ret = GNUNET_OK;
+  ac.qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
   ac.wire_head = NULL;
   ac.wire_tail = NULL;
   ac.fee_head = NULL;
   ac.fee_tail = NULL;
-  have_balance = adb->get_wire_fee_summary (adb->cls,
-                                            asession,
-                                            &master_pub,
-                                            &ac.total_aggregation_fees);
-  if (GNUNET_SYSERR == have_balance)
+  qsx = adb->get_wire_fee_summary (adb->cls,
+                                  asession,
+                                  &master_pub,
+                                  &ac.total_aggregation_fees);
+  if (0 > qsx)
   {
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
+    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qsx);
+    return qsx;
   }
-  if (GNUNET_NO == have_balance)
+  if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qsx)
     GNUNET_assert (GNUNET_OK ==
                    TALER_amount_get_zero (currency,
                                           &ac.total_aggregation_fees));
-  if (GNUNET_SYSERR ==
-      edb->select_wire_out_above_serial_id (edb->cls,
-                                            esession,
-                                            pp.last_wire_out_serial_id,
-                                            &check_wire_out_cb,
-                                            &ac))
+  ac.qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
+  qs = edb->select_wire_out_above_serial_id (edb->cls,
+                                            esession,
+                                            pp.last_wire_out_serial_id,
+                                            &check_wire_out_cb,
+                                            &ac);
+  if (0 > qs)
   {
-    GNUNET_break (0);
-    ac.ret = GNUNET_SYSERR;
+    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+    ac.qs = qs;
   }
   while (NULL != (wc = ac.wire_head))
   {
@@ -2305,28 +2346,28 @@ analyze_aggregations (void *cls)
                                  wfi);
     GNUNET_free (wfi);
   }
-  if (GNUNET_OK != ac.ret)
+  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != ac.qs)
   {
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
+    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == ac.qs);
+    return ac.qs;
   }
-  if (GNUNET_NO == have_balance)
-    ac.ret = adb->insert_wire_fee_summary (adb->cls,
-                                           asession,
-                                           &master_pub,
-                                           &ac.total_aggregation_fees);
+  if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qsx)
+    ac.qs = adb->insert_wire_fee_summary (adb->cls,
+                                         asession,
+                                         &master_pub,
+                                         &ac.total_aggregation_fees);
   else
-    ac.ret = adb->update_wire_fee_summary (adb->cls,
-                                           asession,
-                                           &master_pub,
-                                           &ac.total_aggregation_fees);
-  if (GNUNET_OK != ac.ret)
+    ac.qs = adb->update_wire_fee_summary (adb->cls,
+                                         asession,
+                                         &master_pub,
+                                         &ac.total_aggregation_fees);
+  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != ac.qs)
   {
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
+    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == ac.qs);
+    return ac.qs;
   }
   report_aggregation_fee_balance (&ac.total_aggregation_fees);
-  return GNUNET_OK;
+  return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
 }
 
 
@@ -2400,8 +2441,6 @@ struct CoinContext
   /**
    * Current financial risk of the exchange operator with respect
    * to key compromise.
-   *
-   * TODO: not yet properly used!
    */
   struct TALER_Amount risk;
 
@@ -2411,9 +2450,9 @@ struct CoinContext
   unsigned int summaries_off;
 
   /**
-   * #GNUNET_OK as long as we are fine to commit the result to the #adb.
+   * Transaction status code.
    */
-  int ret;
+  enum GNUNET_DB_QueryStatus qs;
 
 };
 
@@ -2423,32 +2462,32 @@ struct CoinContext
  *
  * @param denom_hash hash of the public key of the denomination
  * @param[out] ds summary to initialize
- * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
+ * @return transaction status code
  */
-static int
+static enum GNUNET_DB_QueryStatus
 init_denomination (const struct GNUNET_HashCode *denom_hash,
                    struct DenominationSummary *ds)
 {
-  int ret;
+  enum GNUNET_DB_QueryStatus qs;
 
-  ret = adb->get_denomination_balance (adb->cls,
+  qs = adb->get_denomination_balance (adb->cls,
                                        asession,
                                        denom_hash,
                                        &ds->denom_balance,
                                        &ds->denom_risk);
-  if (GNUNET_OK == ret)
+  if (0 > qs)
+  {
+    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+    return qs;
+  }
+  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
   {
     ds->in_db = GNUNET_YES;
     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                 "Starting balance for denomination `%s' is %s\n",
                 GNUNET_h2s (denom_hash),
                 TALER_amount2s (&ds->denom_balance));
-    return GNUNET_OK;
-  }
-  if (GNUNET_SYSERR == ret)
-  {
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
+    return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
   }
   GNUNET_assert (GNUNET_OK ==
                  TALER_amount_get_zero (currency,
@@ -2460,7 +2499,7 @@ init_denomination (const struct GNUNET_HashCode 
*denom_hash,
               "Starting balance for denomination `%s' is %s\n",
               GNUNET_h2s (denom_hash),
               TALER_amount2s (&ds->denom_balance));
-  return GNUNET_OK;
+  return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
 }
 
 
@@ -2485,9 +2524,8 @@ get_denomination_summary (struct CoinContext *cc,
     return ds;
   ds = GNUNET_new (struct DenominationSummary);
   ds->dki = dki;
-  if (GNUNET_OK !=
-      init_denomination (dh,
-                         ds))
+  if (0 > (cc->qs = init_denomination (dh,
+                                      ds)))
   {
     GNUNET_break (0);
     GNUNET_free (ds);
@@ -2523,7 +2561,7 @@ sync_denomination (void *cls,
   struct GNUNET_TIME_Absolute now;
   struct GNUNET_TIME_Absolute expire_deposit;
   struct GNUNET_TIME_Absolute expire_deposit_grace;
-  int ret;
+  enum GNUNET_DB_QueryStatus qs;
 
   now = GNUNET_TIME_absolute_get ();
   expire_deposit = GNUNET_TIME_absolute_ntoh (dki->properties.expire_deposit);
@@ -2535,12 +2573,12 @@ sync_denomination (void *cls,
     /* Denominationkey has expired, book remaining balance of
        outstanding coins as revenue; and reduce cc->risk exposure. */
     if (ds->in_db)
-      ret = adb->del_denomination_balance (adb->cls,
-                                           asession,
-                                           denom_hash);
+      qs = adb->del_denomination_balance (adb->cls,
+                                         asession,
+                                         denom_hash);
     else
-      ret = GNUNET_OK;
-    if ( (GNUNET_OK == ret) &&
+      qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
+    if ( (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) &&
          ( (0 != ds->denom_risk.value) ||
            (0 != ds->denom_risk.fraction) ) )
     {
@@ -2555,10 +2593,10 @@ sync_denomination (void *cls,
         /* Holy smokes, our risk assessment was inconsistent!
            This is really, really bad. */
         GNUNET_break (0);
-        cc->ret = GNUNET_SYSERR;
+        cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
       }
     }
-    if ( (GNUNET_OK == ret) &&
+    if ( (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) &&
          ( (0 != ds->denom_balance.value) ||
            (0 != ds->denom_balance.fraction) ) )
     {
@@ -2567,17 +2605,17 @@ sync_denomination (void *cls,
                   "Denomination `%s' expired, booking %s in expiration 
profits\n",
                   GNUNET_h2s (denom_hash),
                   TALER_amount2s (&ds->denom_balance));
-      if (GNUNET_OK !=
-          adb->insert_historic_denom_revenue (adb->cls,
-                                              asession,
-                                              &master_pub,
-                                              denom_hash,
-                                              expire_deposit,
-                                              &ds->denom_balance))
+      if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+          (qs = adb->insert_historic_denom_revenue (adb->cls,
+                                                   asession,
+                                                   &master_pub,
+                                                   denom_hash,
+                                                   expire_deposit,
+                                                   &ds->denom_balance)))
       {
         /* Failed to store profits? Bad database */
-        GNUNET_break (0);
-        cc->ret = GNUNET_SYSERR;
+       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+        cc->qs = qs;
       }
     }
   }
@@ -2588,29 +2626,31 @@ sync_denomination (void *cls,
                 GNUNET_h2s (denom_hash),
                 TALER_amount2s (&ds->denom_balance));
     if (ds->in_db)
-      ret = adb->update_denomination_balance (adb->cls,
-                                              asession,
-                                              denom_hash,
-                                              &ds->denom_balance,
-                                              &ds->denom_risk);
+      qs = adb->update_denomination_balance (adb->cls,
+                                            asession,
+                                            denom_hash,
+                                            &ds->denom_balance,
+                                            &ds->denom_risk);
     else
-      ret = adb->insert_denomination_balance (adb->cls,
-                                              asession,
-                                              denom_hash,
-                                              &ds->denom_balance,
-                                              &ds->denom_risk);
+      qs = adb->insert_denomination_balance (adb->cls,
+                                            asession,
+                                            denom_hash,
+                                            &ds->denom_balance,
+                                            &ds->denom_risk);
   }
-  if (GNUNET_OK != ret)
+  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
   {
-    GNUNET_break (0);
-    cc->ret = GNUNET_SYSERR;
+    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+    cc->qs = qs;
   }
   GNUNET_assert (GNUNET_YES ==
                  GNUNET_CONTAINER_multihashmap_remove (cc->denom_summaries,
                                                        denom_hash,
                                                        ds));
   GNUNET_free (ds);
-  return cc->ret;
+  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != cc->qs)
+    return GNUNET_SYSERR;
+  return GNUNET_OK;
 }
 
 
@@ -2649,21 +2689,28 @@ withdraw_cb (void *cls,
   struct GNUNET_HashCode dh;
   const struct TALER_EXCHANGEDB_DenominationKeyInformationP *dki;
   struct TALER_Amount value;
+  enum GNUNET_DB_QueryStatus qs;
 
   GNUNET_assert (rowid >= pp.last_withdraw_serial_id); /* should be 
monotonically increasing */
   pp.last_withdraw_serial_id = rowid + 1;
 
-  if (GNUNET_OK !=
-      get_denomination_info (denom_pub,
-                             &dki,
-                             &dh))
+  qs = get_denomination_info (denom_pub,
+                             &dki,
+                             &dh);
+  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
   {
-    GNUNET_break (0);
+    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+    cc->qs = qs;
     return GNUNET_SYSERR;
   }
   ds = get_denomination_summary (cc,
                                  dki,
                                  &dh);
+  if (NULL == ds)
+  {
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
   TALER_amount_ntoh (&value,
                      &dki->properties.value);
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
@@ -2676,6 +2723,7 @@ withdraw_cb (void *cls,
                         &value))
   {
     GNUNET_break (0);
+    cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
     return GNUNET_SYSERR;
   }
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
@@ -2688,6 +2736,7 @@ withdraw_cb (void *cls,
                         &value))
   {
     GNUNET_break (0);
+    cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
     return GNUNET_SYSERR;
   }
   if (GNUNET_OK !=
@@ -2696,6 +2745,7 @@ withdraw_cb (void *cls,
                         &value))
   {
     GNUNET_break (0);
+    cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
     return GNUNET_SYSERR;
   }
   if (GNUNET_OK !=
@@ -2704,6 +2754,7 @@ withdraw_cb (void *cls,
                         &value))
   {
     GNUNET_break (0);
+    cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
     return GNUNET_SYSERR;
   }
   return GNUNET_OK;
@@ -2745,16 +2796,18 @@ refresh_session_cb (void *cls,
   struct DenominationSummary *dso;
   struct TALER_Amount amount_without_fee;
   struct TALER_Amount tmp;
+  enum GNUNET_DB_QueryStatus qs;
 
   GNUNET_assert (rowid >= pp.last_melt_serial_id); /* should be monotonically 
increasing */
   pp.last_melt_serial_id = rowid + 1;
 
-  if (GNUNET_OK !=
-      get_denomination_info (denom_pub,
-                             &dki,
-                             NULL))
+  qs = get_denomination_info (denom_pub,
+                             &dki,
+                             NULL);
+  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
   {
-    GNUNET_break (0);
+    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+    cc->qs = qs;
     return GNUNET_SYSERR;
   }
 
@@ -2792,15 +2845,15 @@ refresh_session_cb (void *cls,
     GNUNET_assert (GNUNET_OK ==
                    TALER_amount_get_zero (amount_with_fee->currency,
                                           &refresh_cost));
-
-    if (GNUNET_OK !=
-        edb->get_refresh_order (edb->cls,
-                                esession,
-                                session_hash,
-                                num_newcoins,
-                                new_dp))
+    qs = edb->get_refresh_order (edb->cls,
+                                esession,
+                                session_hash,
+                                num_newcoins,
+                                new_dp);
+    if (0 >= qs)
     {
-      GNUNET_break (0);
+      GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+      cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
       return GNUNET_SYSERR;
     }
     /* Update outstanding amounts for all new coin's denominations, and check
@@ -2809,12 +2862,13 @@ refresh_session_cb (void *cls,
     for (unsigned int i=0;i<num_newcoins;i++)
     {
       /* lookup new coin denomination key */
-      if (GNUNET_OK !=
-          get_denomination_info (&new_dp[i],
-                                 &new_dki[i],
-                                 NULL))
+      qs = get_denomination_info (&new_dp[i],
+                                 &new_dki[i],
+                                 NULL);
+      if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
       {
-        GNUNET_break (0);
+        GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+       cc->qs = qs;
         err = GNUNET_YES;
       }
       GNUNET_CRYPTO_rsa_public_key_free (new_dp[i].rsa_public_key);
@@ -2844,6 +2898,7 @@ refresh_session_cb (void *cls,
                               &value)) )
       {
         GNUNET_break (0);
+       cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
         return GNUNET_SYSERR;
       }
     }
@@ -2860,6 +2915,7 @@ refresh_session_cb (void *cls,
                                  &melt_fee))
       {
         GNUNET_break (0);
+       cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
         return GNUNET_SYSERR;
       }
     }
@@ -2884,6 +2940,11 @@ refresh_session_cb (void *cls,
       dsi = get_denomination_summary (cc,
                                       new_dki[i],
                                       &new_dki[i]->properties.denom_hash);
+      if (NULL == dsi)
+      {
+       GNUNET_break (0);
+       return GNUNET_SYSERR;
+      }
       TALER_amount_ntoh (&value,
                          &new_dki[i]->properties.value);
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
@@ -2896,6 +2957,7 @@ refresh_session_cb (void *cls,
                             &value))
       {
         GNUNET_break (0);
+       cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
         return GNUNET_SYSERR;
       }
       if (GNUNET_OK !=
@@ -2904,6 +2966,7 @@ refresh_session_cb (void *cls,
                             &value))
       {
         GNUNET_break (0);
+       cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
         return GNUNET_SYSERR;
       }
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
@@ -2916,6 +2979,7 @@ refresh_session_cb (void *cls,
                             &value))
       {
         GNUNET_break (0);
+       cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
         return GNUNET_SYSERR;
       }
       if (GNUNET_OK !=
@@ -2924,6 +2988,7 @@ refresh_session_cb (void *cls,
                             &value))
       {
         GNUNET_break (0);
+       cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
         return GNUNET_SYSERR;
       }
     }
@@ -2933,6 +2998,11 @@ refresh_session_cb (void *cls,
   dso = get_denomination_summary (cc,
                                   dki,
                                   &dki->properties.denom_hash);
+  if (NULL == dso)
+  {
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
   if (GNUNET_SYSERR ==
       TALER_amount_subtract (&tmp,
                              &dso->denom_balance,
@@ -2950,6 +3020,7 @@ refresh_session_cb (void *cls,
     /* This should not be possible, unless the AUDITOR
        has a bug in tracking total balance. */
     GNUNET_break (0);
+    cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
     return GNUNET_SYSERR;
   }
 
@@ -2970,6 +3041,7 @@ refresh_session_cb (void *cls,
                           &rfee))
     {
       GNUNET_break (0);
+      cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
       return GNUNET_SYSERR;
     }
   }
@@ -3020,16 +3092,18 @@ deposit_cb (void *cls,
   struct DenominationSummary *ds;
   struct TALER_DepositRequestPS dr;
   struct TALER_Amount tmp;
+  enum GNUNET_DB_QueryStatus qs;
 
   GNUNET_assert (rowid >= pp.last_deposit_serial_id); /* should be 
monotonically increasing */
   pp.last_deposit_serial_id = rowid + 1;
 
-  if (GNUNET_OK !=
-      get_denomination_info (denom_pub,
-                             &dki,
-                             NULL))
+  qs = get_denomination_info (denom_pub,
+                             &dki,
+                             NULL);
+  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
   {
-    GNUNET_break (0);
+    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+    cc->qs = qs;
     return GNUNET_SYSERR;
   }
 
@@ -3042,6 +3116,7 @@ deposit_cb (void *cls,
                        &dr.h_wire))
   {
     GNUNET_break (0);
+    cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
     return GNUNET_SYSERR;
   }
   dr.timestamp = GNUNET_TIME_absolute_hton (timestamp);
@@ -3072,12 +3147,18 @@ deposit_cb (void *cls,
   ds = get_denomination_summary (cc,
                                  dki,
                                  &dki->properties.denom_hash);
+  if (NULL == ds)
+  {
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
   if (GNUNET_SYSERR ==
       TALER_amount_subtract (&tmp,
                              &ds->denom_balance,
                              amount_with_fee))
   {
     report_emergency (dki);
+    cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
     return GNUNET_SYSERR;
   }
   ds->denom_balance = tmp;
@@ -3089,6 +3170,7 @@ deposit_cb (void *cls,
     /* This should not be possible, unless the AUDITOR
        has a bug in tracking total balance. */
     GNUNET_break (0);
+    cc->qs = GNUNET_DB_STATUS_HARD_ERROR; 
     return GNUNET_SYSERR;
   }
 
@@ -3109,6 +3191,7 @@ deposit_cb (void *cls,
                           &dfee))
     {
       GNUNET_break (0);
+      cc->qs = GNUNET_DB_STATUS_HARD_ERROR; 
       return GNUNET_SYSERR;
     }
   }
@@ -3151,16 +3234,17 @@ refund_cb (void *cls,
   struct TALER_RefundRequestPS rr;
   struct TALER_Amount amount_without_fee;
   struct TALER_Amount refund_fee;
+  enum GNUNET_DB_QueryStatus qs;
 
   GNUNET_assert (rowid >= pp.last_refund_serial_id); /* should be 
monotonically increasing */
   pp.last_refund_serial_id = rowid + 1;
 
-  if (GNUNET_OK !=
-      get_denomination_info (denom_pub,
-                             &dki,
-                             NULL))
+  qs = get_denomination_info (denom_pub,
+                             &dki,
+                             NULL);
+  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
   {
-    GNUNET_break (0);
+    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     return GNUNET_SYSERR;
   }
 
@@ -3209,12 +3293,18 @@ refund_cb (void *cls,
   ds = get_denomination_summary (cc,
                                  dki,
                                  &dki->properties.denom_hash);
+  if (NULL == ds)
+  {
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
   if (GNUNET_OK !=
       TALER_amount_add (&ds->denom_balance,
                         &ds->denom_balance,
                         &amount_without_fee))
   {
     GNUNET_break (0);
+    cc->qs = GNUNET_DB_STATUS_HARD_ERROR; 
     return GNUNET_SYSERR;
   }
   if (GNUNET_OK !=
@@ -3223,6 +3313,7 @@ refund_cb (void *cls,
                         &amount_without_fee))
   {
     GNUNET_break (0);
+    cc->qs = GNUNET_DB_STATUS_HARD_ERROR; 
     return GNUNET_SYSERR;
   }
   if (GNUNET_OK !=
@@ -3231,6 +3322,7 @@ refund_cb (void *cls,
                         &amount_without_fee))
   {
     GNUNET_break (0);
+    cc->qs = GNUNET_DB_STATUS_HARD_ERROR; 
     return GNUNET_SYSERR;
   }
   if (GNUNET_OK !=
@@ -3239,6 +3331,7 @@ refund_cb (void *cls,
                         &amount_without_fee))
   {
     GNUNET_break (0);
+    cc->qs = GNUNET_DB_STATUS_HARD_ERROR; 
     return GNUNET_SYSERR;
   }
 
@@ -3254,6 +3347,7 @@ refund_cb (void *cls,
                         &refund_fee))
   {
     GNUNET_break (0);
+    cc->qs = GNUNET_DB_STATUS_HARD_ERROR; 
     return GNUNET_SYSERR;
   }
 
@@ -3265,34 +3359,35 @@ refund_cb (void *cls,
  * Analyze the exchange's processing of coins.
  *
  * @param cls closure
- * @return #GNUNET_OK on success, #GNUNET_SYSERR on hard errors
+ * @return transaction status code
  */
-static int
+static enum GNUNET_DB_QueryStatus
 analyze_coins (void *cls)
 {
   struct CoinContext cc;
-  int dret;
+  enum GNUNET_DB_QueryStatus qs;
+  enum GNUNET_DB_QueryStatus qsx;
 
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Analyzing coins\n");
   /* setup 'cc' */
-  cc.ret = GNUNET_OK;
+  cc.qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
   cc.denom_summaries = GNUNET_CONTAINER_multihashmap_create (256,
-                                                           GNUNET_NO);
-  dret = adb->get_balance_summary (adb->cls,
-                                   asession,
-                                   &master_pub,
-                                   &cc.total_denom_balance,
-                                   &cc.deposit_fee_balance,
-                                   &cc.melt_fee_balance,
-                                   &cc.refund_fee_balance,
-                                   &cc.risk);
-  if (GNUNET_SYSERR == dret)
-  {
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
-  }
-  if (GNUNET_NO == dret)
+                                                            GNUNET_NO);
+  qsx = adb->get_balance_summary (adb->cls,
+                                 asession,
+                                 &master_pub,
+                                 &cc.total_denom_balance,
+                                 &cc.deposit_fee_balance,
+                                 &cc.melt_fee_balance,
+                                 &cc.refund_fee_balance,
+                                 &cc.risk);
+  if (0 > qsx)
+  {
+    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qsx);
+    return qsx;
+  }
+  if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qsx)
   {
     GNUNET_assert (GNUNET_OK ==
                    TALER_amount_get_zero (currency,
@@ -3312,92 +3407,93 @@ analyze_coins (void *cls)
   }
 
   /* process withdrawals */
-  if (GNUNET_SYSERR ==
-      edb->select_reserves_out_above_serial_id (edb->cls,
-                                                esession,
-                                                pp.last_withdraw_serial_id,
-                                                &withdraw_cb,
-                                                &cc))
+  if (0 >
+      (qs = edb->select_reserves_out_above_serial_id (edb->cls,
+                                                     esession,
+                                                     
pp.last_withdraw_serial_id,
+                                                     &withdraw_cb,
+                                                     &cc)) )
   {
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
+    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+    return qs;
   }
 
   /* process refunds */
-  if (GNUNET_SYSERR ==
-      edb->select_refunds_above_serial_id (edb->cls,
-                                           esession,
-                                           pp.last_refund_serial_id,
-                                           &refund_cb,
-                                           &cc))
+  if (0 >
+      (qs = edb->select_refunds_above_serial_id (edb->cls,
+                                                esession,
+                                                pp.last_refund_serial_id,
+                                                &refund_cb,
+                                                &cc)))
   {
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
+    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+    return qs;
   }
 
   /* process refreshs */
-  if (GNUNET_SYSERR ==
-      edb->select_refreshs_above_serial_id (edb->cls,
-                                            esession,
-                                            pp.last_melt_serial_id,
-                                            &refresh_session_cb,
-                                            &cc))
+  if (0 > 
+      (qs = edb->select_refreshs_above_serial_id (edb->cls,
+                                                 esession,
+                                                 pp.last_melt_serial_id,
+                                                 &refresh_session_cb,
+                                                 &cc)))
   {
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
+    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+    return qs;
   }
 
   /* process deposits */
-  if (GNUNET_SYSERR ==
-      edb->select_deposits_above_serial_id (edb->cls,
-                                            esession,
-                                            pp.last_deposit_serial_id,
-                                            &deposit_cb,
-                                            &cc))
+  if (0 >
+      (qs = edb->select_deposits_above_serial_id (edb->cls,
+                                                 esession,
+                                                 pp.last_deposit_serial_id,
+                                                 &deposit_cb,
+                                                 &cc)))
   {
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
+    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+    return qs;
   }
 
   /* sync 'cc' back to disk */
+  cc.qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
   GNUNET_CONTAINER_multihashmap_iterate (cc.denom_summaries,
                                          &sync_denomination,
                                          &cc);
   GNUNET_CONTAINER_multihashmap_destroy (cc.denom_summaries);
-  if (GNUNET_OK != cc.ret)
-  {
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
-  }
-  if (GNUNET_YES == dret)
-      dret = adb->update_balance_summary (adb->cls,
-                                          asession,
-                                          &master_pub,
-                                          &cc.total_denom_balance,
-                                          &cc.deposit_fee_balance,
-                                          &cc.melt_fee_balance,
-                                          &cc.refund_fee_balance,
-                                          &cc.risk);
+  if (0 > cc.qs)
+  {
+    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == cc.qs);
+    return cc.qs;
+  }
+  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qsx)
+    qs = adb->update_balance_summary (adb->cls,
+                                     asession,
+                                     &master_pub,
+                                     &cc.total_denom_balance,
+                                     &cc.deposit_fee_balance,
+                                     &cc.melt_fee_balance,
+                                     &cc.refund_fee_balance,
+                                     &cc.risk);
   else
-    dret = adb->insert_balance_summary (adb->cls,
-                                        asession,
-                                        &master_pub,
-                                        &cc.total_denom_balance,
-                                        &cc.deposit_fee_balance,
-                                        &cc.melt_fee_balance,
-                                        &cc.refund_fee_balance,
-                                        &cc.risk);
-  if (GNUNET_OK != dret)
-  {
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
+    qs = adb->insert_balance_summary (adb->cls,
+                                     asession,
+                                     &master_pub,
+                                     &cc.total_denom_balance,
+                                     &cc.deposit_fee_balance,
+                                     &cc.melt_fee_balance,
+                                     &cc.refund_fee_balance,
+                                     &cc.risk);
+  if (0 >= qs)
+  {
+    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+    return qs;
   }
   report_denomination_balance (&cc.total_denom_balance,
                                &cc.risk,
                                &cc.deposit_fee_balance,
                                &cc.melt_fee_balance,
                                &cc.refund_fee_balance);
-  return GNUNET_OK;
+  return qs;
 }
 
 
@@ -3408,9 +3504,9 @@ analyze_coins (void *cls)
  * its own transaction scope and must thus be internally consistent.
  *
  * @param cls closure
- * @param int #GNUNET_OK on success, #GNUNET_SYSERR on hard errors
+ * @return transaction status code
  */
-typedef int
+typedef enum GNUNET_DB_QueryStatus
 (*Analysis)(void *cls);
 
 
@@ -3420,26 +3516,25 @@ typedef int
  *
  * @param analysis analysis to run
  * @param analysis_cls closure for @a analysis
- * @return #GNUNET_OK if @a analysis succeessfully committed,
- *         #GNUNET_SYSERR on hard errors
+ * @return transaction status code
  */
-static int
+static enum GNUNET_DB_QueryStatus
 incremental_processing (Analysis analysis,
                         void *analysis_cls)
 {
-  int ret;
-  int have_pp;
+  enum GNUNET_DB_QueryStatus qs;
+  enum GNUNET_DB_QueryStatus qsx;
 
-  have_pp = adb->get_auditor_progress (adb->cls,
-                                       asession,
-                                       &master_pub,
-                                       &pp);
-  if (GNUNET_SYSERR == have_pp)
+  qsx = adb->get_auditor_progress (adb->cls,
+                                  asession,
+                                  &master_pub,
+                                  &pp);
+  if (0 > qsx)
   {
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
+    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qsx);
+    return qsx;
   }
-  if (GNUNET_NO == have_pp)
+  if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qsx)
   {
     GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE,
                 _("First analysis using this auditor, starting audit from 
scratch\n"));
@@ -3456,27 +3551,33 @@ incremental_processing (Analysis analysis,
                 (unsigned long long) pp.last_refund_serial_id,
                 (unsigned long long) pp.last_wire_out_serial_id);
   }
-  ret = analysis (analysis_cls);
-  if (GNUNET_OK != ret)
+  qs = analysis (analysis_cls);
+  if (0 > qs)
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Analysis phase failed, not recording progress\n");
-    return GNUNET_SYSERR;
-  }
-  if (GNUNET_YES == have_pp)
-    ret = adb->update_auditor_progress (adb->cls,
-                                        asession,
-                                        &master_pub,
-                                        &pp);
+    if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                 "Serialization issue, not recording progress\n");
+    else
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                 "Hard database error, not recording progress\n");
+    return qs;
+  }
+  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qsx)
+    qs = adb->update_auditor_progress (adb->cls,
+                                      asession,
+                                      &master_pub,
+                                      &pp);
   else
-    ret = adb->insert_auditor_progress (adb->cls,
-                                        asession,
-                                        &master_pub,
-                                        &pp);
-  if (GNUNET_OK != ret)
+    qs = adb->insert_auditor_progress (adb->cls,
+                                      asession,
+                                      &master_pub,
+                                      &pp);
+  if (0 >= qs)
   {
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+               "Failed to update auditor DB, not recording progress\n");
+    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+    return qs;
   }
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
               _("Concluded audit step at 
%llu/%llu/%llu/%llu/%llu/%llu/%llu\n\n"),
@@ -3487,7 +3588,7 @@ incremental_processing (Analysis analysis,
               (unsigned long long) pp.last_melt_serial_id,
               (unsigned long long) pp.last_refund_serial_id,
               (unsigned long long) pp.last_wire_out_serial_id);
-  return GNUNET_OK;
+  return qs;
 }
 
 
@@ -3506,6 +3607,7 @@ transact (Analysis analysis,
           void *analysis_cls)
 {
   int ret;
+  enum GNUNET_DB_QueryStatus qs;
 
   ret = adb->start (adb->cls,
                     asession);
@@ -3521,26 +3623,28 @@ transact (Analysis analysis,
     GNUNET_break (0);
     return GNUNET_SYSERR;
   }
-  ret = incremental_processing (analysis,
+  qs = incremental_processing (analysis,
                                 analysis_cls);
-  if (GNUNET_OK == ret)
+  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
   {
-    ret = edb->commit (edb->cls,
+    qs = edb->commit (edb->cls,
                        esession);
-    if (GNUNET_OK != ret)
+    if (0 > qs)
     {
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+      GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                   "Exchange DB commit failed, rolling back transaction\n");
       adb->rollback (adb->cls,
                      asession);
     }
     else
     {
-      ret = adb->commit (adb->cls,
-                         asession);
-      if (GNUNET_OK != ret)
+      qs = adb->commit (adb->cls,
+                       asession);
+      if (0 > qs)
       {
-        GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                     "Auditor DB commit failed!\n");
       }
     }
@@ -3555,7 +3659,7 @@ transact (Analysis analysis,
                    esession);
   }
   clear_transaction_state_cache ();
-  return ret;
+  return qs;
 }
 
 
diff --git a/src/auditordb/plugin_auditordb_postgres.c 
b/src/auditordb/plugin_auditordb_postgres.c
index f144746..4862cf2 100644
--- a/src/auditordb/plugin_auditordb_postgres.c
+++ b/src/auditordb/plugin_auditordb_postgres.c
@@ -31,72 +31,6 @@
 
 
 /**
- * Log a query error.
- *
- * @param result PQ result object of the query that failed
- */
-#define QUERY_ERR(result)                          \
-  LOG (GNUNET_ERROR_TYPE_ERROR, "Query failed at %s:%u: %s (%s)\n", __FILE__, 
__LINE__, PQresultErrorMessage (result), PQresStatus (PQresultStatus (result)))
-
-
-/**
- * Log a really unexpected PQ error.
- *
- * @param result PQ result object of the PQ operation that failed
- */
-#define BREAK_DB_ERR(result) do { \
-    GNUNET_break (0); \
-    LOG (GNUNET_ERROR_TYPE_ERROR, "Database failure: %s (%s)\n", 
PQresultErrorMessage (result), PQresStatus (PQresultStatus (result))); \
-  } while (0)
-
-
-/**
- * Shorthand for exit jumps.  Logs the current line number
- * and jumps to the "EXITIF_exit" label.
- *
- * @param cond condition that must be TRUE to exit with an error
- */
-#define EXITIF(cond)                                              \
-  do {                                                            \
-    if (cond) { GNUNET_break (0); goto EXITIF_exit; }             \
-  } while (0)
-
-
-/**
- * Execute an SQL statement and log errors on failure. Must be
- * run in a function that has an "SQLEXEC_fail" label to jump
- * to in case the SQL statement failed.
- *
- * @param conn database connection
- * @param sql SQL statement to run
- */
-#define SQLEXEC_(conn, sql)                                             \
-  do {                                                                  \
-    PGresult *result = PQexec (conn, sql);                              \
-    if (PGRES_COMMAND_OK != PQresultStatus (result))                    \
-    {                                                                   \
-      BREAK_DB_ERR (result);                                            \
-      PQclear (result);                                                 \
-      goto SQLEXEC_fail;                                                \
-    }                                                                   \
-    PQclear (result);                                                   \
-  } while (0)
-
-
-/**
- * Run an SQL statement, ignoring errors and clearing the result.
- *
- * @param conn database connection
- * @param sql SQL statement to run
- */
-#define SQLEXEC_IGNORE_ERROR_(conn, sql)                                \
-  do {                                                                  \
-    PGresult *result = PQexec (conn, sql);                              \
-    PQclear (result);                                                   \
-  } while (0)
-
-
-/**
  * Handle for a database session (per-thread, for transactions).
  */
 struct TALER_AUDITORDB_Session
@@ -203,42 +137,35 @@ static int
 postgres_drop_tables (void *cls)
 {
   struct PostgresClosure *pc = cls;
+  struct GNUNET_PQ_ExecuteStatement es[] = {
+    GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS predicted_result;"),
+    GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS historic_ledger;"),
+    GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS historic_losses;"),
+    GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS 
historic_denomination_revenue;"),
+    GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS balance_summary;"),
+    GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS denomination_pending;"),
+    GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS auditor_reserve_balance;"),
+    GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS auditor_wire_fee_balance;"),
+    GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS auditor_reserves;"),
+    GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS auditor_progress;"),
+    GNUNET_PQ_EXECUTE_STATEMENT_END
+  };
   PGconn *conn;
+  int ret;
 
   conn = connect_to_postgres (pc);
   if (NULL == conn)
     return GNUNET_SYSERR;
   LOG (GNUNET_ERROR_TYPE_INFO,
        "Dropping ALL tables\n");
+  ret = GNUNET_PQ_exec_statements (conn,
+                                   es);
   /* TODO: we probably need a bit more fine-grained control
      over drops for the '-r' option of taler-auditor; also,
      for the testcase, we currently fail to drop the
      auditor_denominations table... */
-  SQLEXEC_ (conn,
-            "DROP TABLE IF EXISTS predicted_result;");
-  SQLEXEC_ (conn,
-            "DROP TABLE IF EXISTS historic_ledger;");
-  SQLEXEC_ (conn,
-            "DROP TABLE IF EXISTS historic_losses;");
-  SQLEXEC_ (conn,
-            "DROP TABLE IF EXISTS historic_denomination_revenue;");
-  SQLEXEC_ (conn,
-            "DROP TABLE IF EXISTS balance_summary;");
-  SQLEXEC_ (conn,
-            "DROP TABLE IF EXISTS denomination_pending;");
-  SQLEXEC_ (conn,
-            "DROP TABLE IF EXISTS auditor_reserve_balance;");
-  SQLEXEC_ (conn,
-            "DROP TABLE IF EXISTS auditor_wire_fee_balance;");
-  SQLEXEC_ (conn,
-            "DROP TABLE IF EXISTS auditor_reserves;");
-  SQLEXEC_ (conn,
-            "DROP TABLE IF EXISTS auditor_progress;");
-  PQfinish (conn);
-  return GNUNET_OK;
- SQLEXEC_fail:
   PQfinish (conn);
-  return GNUNET_SYSERR;
+  return ret;
 }
 
 
@@ -252,245 +179,222 @@ static int
 postgres_create_tables (void *cls)
 {
   struct PostgresClosure *pc = cls;
+  struct GNUNET_PQ_ExecuteStatement es[] = {
+    /* Table with all of the denomination keys that the auditor
+       is aware of. */
+    GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS auditor_denominations"
+                           "(denom_pub_hash BYTEA PRIMARY KEY CHECK 
(LENGTH(denom_pub_hash)=64)"
+                           ",master_pub BYTEA NOT NULL CHECK 
(LENGTH(master_pub)=32)"
+                           ",valid_from INT8 NOT NULL"
+                           ",expire_withdraw INT8 NOT NULL"
+                           ",expire_deposit INT8 NOT NULL"
+                           ",expire_legal INT8 NOT NULL"
+                           ",coin_val INT8 NOT NULL" /* value of this denom */
+                           ",coin_frac INT4 NOT NULL" /* fractional value of 
this denom */
+                           ",coin_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT 
NULL" /* assuming same currency for fees */
+                           ",fee_withdraw_val INT8 NOT NULL"
+                           ",fee_withdraw_frac INT4 NOT NULL"
+                           ",fee_withdraw_curr 
VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+                           ",fee_deposit_val INT8 NOT NULL"
+                           ",fee_deposit_frac INT4 NOT NULL"
+                           ",fee_deposit_curr 
VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+                           ",fee_refresh_val INT8 NOT NULL"
+                           ",fee_refresh_frac INT4 NOT NULL"
+                           ",fee_refresh_curr 
VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+                           ",fee_refund_val INT8 NOT NULL"
+                           ",fee_refund_frac INT4 NOT NULL"
+                           ",fee_refund_curr VARCHAR("TALER_CURRENCY_LEN_STR") 
NOT NULL"
+                           ")"),
+    /* Table indicating up to which transactions the auditor has
+       processed the exchange database.  Used for SELECTing the
+       statements to process.  We basically trace the exchange's
+       operations by the 6 primary tables: reserves_in,
+       reserves_out, deposits, refresh_sessions, refunds and prewire. The
+       other tables of the exchange DB just provide supporting
+       evidence which is checked alongside the audit of these
+       five tables.  The 6 indices below include the last serial
+       ID from the respective tables that we have processed. Thus,
+       we need to select those table entries that are strictly
+       larger (and process in monotonically increasing order). */
+    GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS auditor_progress"
+                           "(master_pub BYTEA PRIMARY KEY CHECK 
(LENGTH(master_pub)=32)"
+                           ",last_reserve_in_serial_id INT8 NOT NULL"
+                           ",last_reserve_out_serial_id INT8 NOT NULL"
+                           ",last_reserve_payback_serial_id INT8 NOT NULL"
+                           ",last_reserve_close_serial_id INT8 NOT NULL"
+                           ",last_withdraw_serial_id INT8 NOT NULL"
+                           ",last_deposit_serial_id INT8 NOT NULL"
+                           ",last_melt_serial_id INT8 NOT NULL"
+                           ",last_refund_serial_id INT8 NOT NULL"
+                           ",last_wire_out_serial_id INT8 NOT NULL"
+                           ")"),
+    /* Table with all of the customer reserves and their respective
+       balances that the auditor is aware of.
+       "last_reserve_out_serial_id" marks the last withdrawal from
+       "reserves_out" about this reserve that the auditor is aware of,
+       and "last_reserve_in_serial_id" is the last "reserve_in"
+       operation about this reserve that the auditor is aware of. */
+    GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS auditor_reserves"
+                           "(reserve_pub BYTEA NOT NULL 
CHECK(LENGTH(reserve_pub)=32)"
+                           ",master_pub BYTEA NOT NULL CHECK 
(LENGTH(master_pub)=32)"
+                           ",reserve_balance_val INT8 NOT NULL"
+                           ",reserve_balance_frac INT4 NOT NULL"
+                           ",reserve_balance_curr 
VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+                           ",withdraw_fee_balance_val INT8 NOT NULL"
+                           ",withdraw_fee_balance_frac INT4 NOT NULL"
+                           ",withdraw_fee_balance_curr 
VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+                           ",expiration_date INT8 NOT NULL"
+                           ",auditor_reserves_rowid BIGSERIAL"
+                           ")"),
+    GNUNET_PQ_make_try_execute ("CREATE INDEX auditor_reserves_by_reserve_pub "
+                               "ON auditor_reserves(reserve_pub)"),
+    /* Table with the sum of the balances of all customer reserves
+       (by exchange's master public key) */
+    GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS 
auditor_reserve_balance"
+                           "(master_pub BYTEA PRIMARY KEY CHECK 
(LENGTH(master_pub)=32)"
+                           ",reserve_balance_val INT8 NOT NULL"
+                           ",reserve_balance_frac INT4 NOT NULL"
+                           ",reserve_balance_curr 
VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+                           ",withdraw_fee_balance_val INT8 NOT NULL"
+                           ",withdraw_fee_balance_frac INT4 NOT NULL"
+                           ",withdraw_fee_balance_curr 
VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+                           ")"),    
+    /* Table with the sum of the balances of all wire fees
+       (by exchange's master public key) */
+    GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS 
auditor_wire_fee_balance"
+                           "(master_pub BYTEA PRIMARY KEY CHECK 
(LENGTH(master_pub)=32)"
+                           ",wire_fee_balance_val INT8 NOT NULL"
+                           ",wire_fee_balance_frac INT4 NOT NULL"
+                           ",wire_fee_balance_curr 
VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+                           ")"),
+    /* Table with all of the outstanding denomination coins that the
+       exchange is aware of.  "last_deposit_serial_id" marks the
+       deposit_serial_id from "deposits" about this denomination key
+       that the auditor is aware of; "last_melt_serial_id" marks the
+       last melt from "refresh_sessions" that the auditor is aware
+       of; "refund_serial_id" tells us the last entry in "refunds"
+       for this denom_pub that the auditor is aware of. */
+    GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS denomination_pending"
+                           "(denom_pub_hash BYTEA PRIMARY KEY"
+                           /* " REFERENCES auditor_denominations 
(denom_pub_hash) ON DELETE CASCADE" // Do we want this? */
+                           ",denom_balance_val INT8 NOT NULL"
+                           ",denom_balance_frac INT4 NOT NULL"
+                           ",denom_balance_curr 
VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+                           ",denom_risk_val INT8 NOT NULL"
+                           ",denom_risk_frac INT4 NOT NULL"
+                           ",denom_risk_curr VARCHAR("TALER_CURRENCY_LEN_STR") 
NOT NULL"
+                           ")"),
+    /* Table with the sum of the outstanding coins from
+       "denomination_pending" (denom_pubs must belong to the
+       respective's exchange's master public key); it represents the
+       balance_summary of the exchange at this point (modulo
+       unexpected historic_loss-style events where denomination keys are
+       compromised) */
+    GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS balance_summary"
+                           "(master_pub BYTEA PRIMARY KEY CHECK 
(LENGTH(master_pub)=32)"
+                           ",denom_balance_val INT8 NOT NULL"
+                           ",denom_balance_frac INT4 NOT NULL"
+                           ",denom_balance_curr 
VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+                           ",deposit_fee_balance_val INT8 NOT NULL"
+                           ",deposit_fee_balance_frac INT4 NOT NULL"
+                           ",deposit_fee_balance_curr 
VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+                           ",melt_fee_balance_val INT8 NOT NULL"
+                           ",melt_fee_balance_frac INT4 NOT NULL"
+                           ",melt_fee_balance_curr 
VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+                           ",refund_fee_balance_val INT8 NOT NULL"
+                           ",refund_fee_balance_frac INT4 NOT NULL"
+                           ",refund_fee_balance_curr 
VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+                           ",risk_val INT8 NOT NULL"
+                           ",risk_frac INT4 NOT NULL"
+                           ",risk_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT 
NULL"
+                           ")"),
+    /* Table with historic profits; basically, when a denom_pub has
+       expired and everything associated with it is garbage collected,
+       the final profits end up in here; note that the "denom_pub" here
+       is not a foreign key, we just keep it as a reference point.
+       "revenue_balance" is the sum of all of the profits we made on the
+       coin except for withdraw fees (which are in
+       historic_reserve_revenue); the deposit, melt and refund fees are given
+       individually; the delta to the revenue_balance is from coins that
+       were withdrawn but never deposited prior to expiration. */
+    GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS 
historic_denomination_revenue"
+                           "(master_pub BYTEA NOT NULL CHECK 
(LENGTH(master_pub)=32)"
+                           ",denom_pub_hash BYTEA PRIMARY KEY CHECK 
(LENGTH(denom_pub_hash)=64)"
+                           ",revenue_timestamp INT8 NOT NULL"
+                           ",revenue_balance_val INT8 NOT NULL"
+                           ",revenue_balance_frac INT4 NOT NULL"
+                           ",revenue_balance_curr 
VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+                           ")"),
+    /* Table with historic losses; basically, when we need to
+       invalidate a denom_pub because the denom_priv was
+       compromised, we incur a loss. These losses are totaled
+       up here. (NOTE: the 'bankrupcy' protocol is not yet
+       implemented, so right now this table is not used.)  */
+    GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS historic_losses"
+                           "(master_pub BYTEA NOT NULL CHECK 
(LENGTH(master_pub)=32)"
+                           ",denom_pub_hash BYTEA PRIMARY KEY CHECK 
(LENGTH(denom_pub_hash)=64)"
+                           ",loss_timestamp INT8 NOT NULL"
+                           ",loss_balance_val INT8 NOT NULL"
+                           ",loss_balance_frac INT4 NOT NULL"
+                           ",loss_balance_curr 
VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+                           ")"),
+    /* Table with historic profits from reserves; we eventually
+       GC "historic_reserve_revenue", and then store the totals
+       in here (by time intervals). */
+    GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS 
historic_reserve_summary"
+                           "(master_pub BYTEA NOT NULL CHECK 
(LENGTH(master_pub)=32)"
+                           ",start_date INT8 NOT NULL"
+                           ",end_date INT8 NOT NULL"
+                           ",reserve_profits_val INT8 NOT NULL"
+                           ",reserve_profits_frac INT4 NOT NULL"
+                           ",reserve_profits_curr 
VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+                           ")"),
+    GNUNET_PQ_make_try_execute ("CREATE INDEX 
historic_reserve_summary_by_master_pub_start_date "
+                               "ON 
historic_reserve_summary(master_pub,start_date)"),
+    /* Table with historic business ledger; basically, when the exchange
+       operator decides to use operating costs for anything but wire
+       transfers to merchants, it goes in here.  This happens when the
+       operator users transaction fees for business expenses. "purpose"
+       is free-form but should be a human-readable wire transfer
+       identifier.   This is NOT yet used and outside of the scope of
+       the core auditing logic. However, once we do take fees to use
+       operating costs, and if we still want "predicted_result" to match
+       the tables overall, we'll need a command-line tool to insert rows
+       into this table and update "predicted_result" accordingly.
+       (So this table for now just exists as a reminder of what we'll
+       need in the long term.) */
+    GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS historic_ledger"
+                           "(master_pub BYTEA NOT NULL CHECK 
(LENGTH(master_pub)=32)"
+                           ",purpose VARCHAR NOT NULL"
+                           ",timestamp INT8 NOT NULL"
+                           ",balance_val INT8 NOT NULL"
+                           ",balance_frac INT4 NOT NULL"
+                           ",balance_curr VARCHAR("TALER_CURRENCY_LEN_STR") 
NOT NULL"
+                           ")"),
+    GNUNET_PQ_make_try_execute ("CREATE INDEX 
history_ledger_by_master_pub_and_time "
+                               "ON historic_ledger(master_pub,timestamp)"),
+    /* Table with the sum of the ledger, historic_revenue,
+       historic_losses and the auditor_reserve_balance.  This is the
+       final amount that the exchange should have in its bank account
+       right now. */
+    GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS predicted_result"
+                           "(master_pub BYTEA PRIMARY KEY CHECK 
(LENGTH(master_pub)=32)"
+                           ",balance_val INT8 NOT NULL"
+                           ",balance_frac INT4 NOT NULL"
+                           ",balance_curr VARCHAR("TALER_CURRENCY_LEN_STR") 
NOT NULL"
+                           ")"),
+    GNUNET_PQ_EXECUTE_STATEMENT_END
+  };
   PGconn *conn;
+  int ret;
 
   conn = connect_to_postgres (pc);
   if (NULL == conn)
     return GNUNET_SYSERR;
-#define SQLEXEC(sql) SQLEXEC_(conn, sql);
-#define SQLEXEC_INDEX(sql) SQLEXEC_IGNORE_ERROR_(conn, sql);
-
-  /* Table with all of the denomination keys that the auditor
-     is aware of. */
-  SQLEXEC ("CREATE TABLE IF NOT EXISTS auditor_denominations"
-           "(denom_pub_hash BYTEA PRIMARY KEY CHECK 
(LENGTH(denom_pub_hash)=64)"
-           ",master_pub BYTEA NOT NULL CHECK (LENGTH(master_pub)=32)"
-           ",valid_from INT8 NOT NULL"
-           ",expire_withdraw INT8 NOT NULL"
-           ",expire_deposit INT8 NOT NULL"
-           ",expire_legal INT8 NOT NULL"
-           ",coin_val INT8 NOT NULL" /* value of this denom */
-           ",coin_frac INT4 NOT NULL" /* fractional value of this denom */
-           ",coin_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL" /* assuming 
same currency for fees */
-           ",fee_withdraw_val INT8 NOT NULL"
-           ",fee_withdraw_frac INT4 NOT NULL"
-           ",fee_withdraw_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
-           ",fee_deposit_val INT8 NOT NULL"
-           ",fee_deposit_frac INT4 NOT NULL"
-           ",fee_deposit_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
-           ",fee_refresh_val INT8 NOT NULL"
-           ",fee_refresh_frac INT4 NOT NULL"
-           ",fee_refresh_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
-           ",fee_refund_val INT8 NOT NULL"
-           ",fee_refund_frac INT4 NOT NULL"
-           ",fee_refund_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
-           ")");
-
-  /* Table indicating up to which transactions the auditor has
-     processed the exchange database.  Used for SELECTing the
-     statements to process.  We basically trace the exchange's
-     operations by the 6 primary tables: reserves_in,
-     reserves_out, deposits, refresh_sessions, refunds and prewire. The
-     other tables of the exchange DB just provide supporting
-     evidence which is checked alongside the audit of these
-     five tables.  The 6 indices below include the last serial
-     ID from the respective tables that we have processed. Thus,
-     we need to select those table entries that are strictly
-     larger (and process in monotonically increasing order). */
-  SQLEXEC ("CREATE TABLE IF NOT EXISTS auditor_progress"
-          "(master_pub BYTEA PRIMARY KEY CHECK (LENGTH(master_pub)=32)"
-          ",last_reserve_in_serial_id INT8 NOT NULL"
-           ",last_reserve_out_serial_id INT8 NOT NULL"
-           ",last_reserve_payback_serial_id INT8 NOT NULL"
-           ",last_reserve_close_serial_id INT8 NOT NULL"
-           ",last_withdraw_serial_id INT8 NOT NULL"
-          ",last_deposit_serial_id INT8 NOT NULL"
-           ",last_melt_serial_id INT8 NOT NULL"
-          ",last_refund_serial_id INT8 NOT NULL"
-          ",last_wire_out_serial_id INT8 NOT NULL"
-          ")");
-
-  /* Table with all of the customer reserves and their respective
-     balances that the auditor is aware of.
-     "last_reserve_out_serial_id" marks the last withdrawal from
-     "reserves_out" about this reserve that the auditor is aware of,
-     and "last_reserve_in_serial_id" is the last "reserve_in"
-     operation about this reserve that the auditor is aware of. */
-  SQLEXEC ("CREATE TABLE IF NOT EXISTS auditor_reserves"
-          "(reserve_pub BYTEA NOT NULL CHECK(LENGTH(reserve_pub)=32)"
-           ",master_pub BYTEA NOT NULL CHECK (LENGTH(master_pub)=32)"
-           ",reserve_balance_val INT8 NOT NULL"
-           ",reserve_balance_frac INT4 NOT NULL"
-           ",reserve_balance_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
-           ",withdraw_fee_balance_val INT8 NOT NULL"
-           ",withdraw_fee_balance_frac INT4 NOT NULL"
-           ",withdraw_fee_balance_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT 
NULL"
-           ",expiration_date INT8 NOT NULL"
-           ",auditor_reserves_rowid BIGSERIAL"
-          ")");
-
-  SQLEXEC_INDEX("CREATE INDEX auditor_reserves_by_reserve_pub "
-                "ON auditor_reserves(reserve_pub)");
-
-  /* Table with the sum of the balances of all customer reserves
-     (by exchange's master public key) */
-  SQLEXEC ("CREATE TABLE IF NOT EXISTS auditor_reserve_balance"
-          "(master_pub BYTEA PRIMARY KEY CHECK (LENGTH(master_pub)=32)"
-          ",reserve_balance_val INT8 NOT NULL"
-           ",reserve_balance_frac INT4 NOT NULL"
-           ",reserve_balance_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
-           ",withdraw_fee_balance_val INT8 NOT NULL"
-           ",withdraw_fee_balance_frac INT4 NOT NULL"
-           ",withdraw_fee_balance_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT 
NULL"
-          ")");
-
-  /* Table with the sum of the balances of all wire fees
-     (by exchange's master public key) */
-  SQLEXEC ("CREATE TABLE IF NOT EXISTS auditor_wire_fee_balance"
-          "(master_pub BYTEA PRIMARY KEY CHECK (LENGTH(master_pub)=32)"
-          ",wire_fee_balance_val INT8 NOT NULL"
-           ",wire_fee_balance_frac INT4 NOT NULL"
-           ",wire_fee_balance_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
-          ")");
-
-  /* Table with all of the outstanding denomination coins that the
-     exchange is aware of.  "last_deposit_serial_id" marks the
-     deposit_serial_id from "deposits" about this denomination key
-     that the auditor is aware of; "last_melt_serial_id" marks the
-     last melt from "refresh_sessions" that the auditor is aware
-     of; "refund_serial_id" tells us the last entry in "refunds"
-     for this denom_pub that the auditor is aware of. */
-  SQLEXEC ("CREATE TABLE IF NOT EXISTS denomination_pending"
-          "(denom_pub_hash BYTEA PRIMARY KEY"
-           /* " REFERENCES auditor_denominations (denom_pub_hash) ON DELETE 
CASCADE" // Do we want this? */
-           ",denom_balance_val INT8 NOT NULL"
-           ",denom_balance_frac INT4 NOT NULL"
-           ",denom_balance_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
-           ",denom_risk_val INT8 NOT NULL"
-           ",denom_risk_frac INT4 NOT NULL"
-           ",denom_risk_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
-          ")");
-
-  /* Table with the sum of the outstanding coins from
-     "denomination_pending" (denom_pubs must belong to the
-     respective's exchange's master public key); it represents the
-     balance_summary of the exchange at this point (modulo
-     unexpected historic_loss-style events where denomination keys are
-     compromised) */
-  SQLEXEC ("CREATE TABLE IF NOT EXISTS balance_summary"
-          "(master_pub BYTEA PRIMARY KEY CHECK (LENGTH(master_pub)=32)"
-          ",denom_balance_val INT8 NOT NULL"
-           ",denom_balance_frac INT4 NOT NULL"
-           ",denom_balance_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
-           ",deposit_fee_balance_val INT8 NOT NULL"
-           ",deposit_fee_balance_frac INT4 NOT NULL"
-           ",deposit_fee_balance_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT 
NULL"
-           ",melt_fee_balance_val INT8 NOT NULL"
-           ",melt_fee_balance_frac INT4 NOT NULL"
-           ",melt_fee_balance_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
-           ",refund_fee_balance_val INT8 NOT NULL"
-           ",refund_fee_balance_frac INT4 NOT NULL"
-           ",refund_fee_balance_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT 
NULL"
-          ",risk_val INT8 NOT NULL"
-           ",risk_frac INT4 NOT NULL"
-           ",risk_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
-          ")");
-
-
-  /* Table with historic profits; basically, when a denom_pub has
-     expired and everything associated with it is garbage collected,
-     the final profits end up in here; note that the "denom_pub" here
-     is not a foreign key, we just keep it as a reference point.
-     "revenue_balance" is the sum of all of the profits we made on the
-     coin except for withdraw fees (which are in
-     historic_reserve_revenue); the deposit, melt and refund fees are given
-     individually; the delta to the revenue_balance is from coins that
-     were withdrawn but never deposited prior to expiration. */
-  SQLEXEC ("CREATE TABLE IF NOT EXISTS historic_denomination_revenue"
-          "(master_pub BYTEA NOT NULL CHECK (LENGTH(master_pub)=32)"
-          ",denom_pub_hash BYTEA PRIMARY KEY CHECK (LENGTH(denom_pub_hash)=64)"
-          ",revenue_timestamp INT8 NOT NULL"
-          ",revenue_balance_val INT8 NOT NULL"
-           ",revenue_balance_frac INT4 NOT NULL"
-           ",revenue_balance_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
-           ")");
-
-
-  /* Table with historic losses; basically, when we need to
-     invalidate a denom_pub because the denom_priv was
-     compromised, we incur a loss. These losses are totaled
-     up here. (NOTE: the 'bankrupcy' protocol is not yet
-     implemented, so right now this table is not used.)  */
-  SQLEXEC ("CREATE TABLE IF NOT EXISTS historic_losses"
-          "(master_pub BYTEA NOT NULL CHECK (LENGTH(master_pub)=32)"
-          ",denom_pub_hash BYTEA PRIMARY KEY CHECK (LENGTH(denom_pub_hash)=64)"
-          ",loss_timestamp INT8 NOT NULL"
-          ",loss_balance_val INT8 NOT NULL"
-           ",loss_balance_frac INT4 NOT NULL"
-           ",loss_balance_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
-          ")");
-
-  /* Table with historic profits from reserves; we eventually
-     GC "historic_reserve_revenue", and then store the totals
-     in here (by time intervals). */
-  SQLEXEC ("CREATE TABLE IF NOT EXISTS historic_reserve_summary"
-          "(master_pub BYTEA NOT NULL CHECK (LENGTH(master_pub)=32)"
-          ",start_date INT8 NOT NULL"
-          ",end_date INT8 NOT NULL"
-          ",reserve_profits_val INT8 NOT NULL"
-           ",reserve_profits_frac INT4 NOT NULL"
-           ",reserve_profits_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
-          ")");
-
-  SQLEXEC_INDEX("CREATE INDEX 
historic_reserve_summary_by_master_pub_start_date "
-                "ON historic_reserve_summary(master_pub,start_date)");
-
-
-  /* Table with historic business ledger; basically, when the exchange
-     operator decides to use operating costs for anything but wire
-     transfers to merchants, it goes in here.  This happens when the
-     operator users transaction fees for business expenses. "purpose"
-     is free-form but should be a human-readable wire transfer
-     identifier.   This is NOT yet used and outside of the scope of
-     the core auditing logic. However, once we do take fees to use
-     operating costs, and if we still want "predicted_result" to match
-     the tables overall, we'll need a command-line tool to insert rows
-     into this table and update "predicted_result" accordingly.
-     (So this table for now just exists as a reminder of what we'll
-     need in the long term.) */
-  SQLEXEC ("CREATE TABLE IF NOT EXISTS historic_ledger"
-          "(master_pub BYTEA NOT NULL CHECK (LENGTH(master_pub)=32)"
-          ",purpose VARCHAR NOT NULL"
-          ",timestamp INT8 NOT NULL"
-          ",balance_val INT8 NOT NULL"
-           ",balance_frac INT4 NOT NULL"
-           ",balance_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
-          ")");
-
-  SQLEXEC_INDEX("CREATE INDEX history_ledger_by_master_pub_and_time "
-                "ON historic_ledger(master_pub,timestamp)");
-
-  /* Table with the sum of the ledger, historic_revenue,
-     historic_losses and the auditor_reserve_balance.  This is the
-     final amount that the exchange should have in its bank account
-     right now. */
-  SQLEXEC ("CREATE TABLE IF NOT EXISTS predicted_result"
-          "(master_pub BYTEA PRIMARY KEY CHECK (LENGTH(master_pub)=32)"
-          ",balance_val INT8 NOT NULL"
-           ",balance_frac INT4 NOT NULL"
-           ",balance_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
-          ")");
-
-
-#undef SQLEXEC
-#undef SQLEXEC_INDEX
-
-  PQfinish (conn);
-  return GNUNET_OK;
-
- SQLEXEC_fail:
+  ret = GNUNET_PQ_exec_statements (conn,
+                                   es);
   PQfinish (conn);
-  return GNUNET_SYSERR;
+  return ret;
 }
 
 
@@ -503,445 +407,408 @@ postgres_create_tables (void *cls)
 static int
 postgres_prepare (PGconn *db_conn)
 {
-  PGresult *result;
-
-#define PREPARE(name, sql, ...)                                 \
-  do {                                                          \
-    result = PQprepare (db_conn, name, sql, __VA_ARGS__);       \
-    if (PGRES_COMMAND_OK != PQresultStatus (result))            \
-    {                                                           \
-      BREAK_DB_ERR (result);                                    \
-      PQclear (result); result = NULL;                          \
-      return GNUNET_SYSERR;                                     \
-    }                                                           \
-    PQclear (result); result = NULL;                            \
-  } while (0);
-
-  /* Used in #postgres_insert_denomination_info() */
-  PREPARE ("auditor_denominations_insert",
-           "INSERT INTO auditor_denominations "
-           "(denom_pub_hash"
-           ",master_pub"
-           ",valid_from"
-           ",expire_withdraw"
-           ",expire_deposit"
-           ",expire_legal"
-           ",coin_val"
-           ",coin_frac"
-           ",coin_curr"
-           ",fee_withdraw_val"
-           ",fee_withdraw_frac"
-           ",fee_withdraw_curr"
-           ",fee_deposit_val"
-           ",fee_deposit_frac"
-           ",fee_deposit_curr"
-           ",fee_refresh_val"
-           ",fee_refresh_frac"
-           ",fee_refresh_curr"
-           ",fee_refund_val"
-           ",fee_refund_frac"
-           ",fee_refund_curr"
-           ") VALUES 
($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21);",
-           21, NULL);
-
-  /* Used in #postgres_insert_denomination_info() */
-  PREPARE ("auditor_denominations_select",
-           "SELECT"
-           " denom_pub_hash"
-           ",valid_from"
-           ",expire_withdraw"
-           ",expire_deposit"
-           ",expire_legal"
-           ",coin_val"
-           ",coin_frac"
-           ",coin_curr"
-           ",fee_withdraw_val"
-           ",fee_withdraw_frac"
-           ",fee_withdraw_curr"
-           ",fee_deposit_val"
-           ",fee_deposit_frac"
-           ",fee_deposit_curr"
-           ",fee_refresh_val"
-           ",fee_refresh_frac"
-           ",fee_refresh_curr"
-           ",fee_refund_val"
-           ",fee_refund_frac"
-           ",fee_refund_curr"
-           " FROM auditor_denominations"
-           " WHERE master_pub=$1;",
-           1, NULL);
-
-  /* Used in #postgres_insert_auditor_progress() */
-  PREPARE ("auditor_progress_insert",
-           "INSERT INTO auditor_progress "
-          "(master_pub"
-          ",last_reserve_in_serial_id"
-           ",last_reserve_out_serial_id"
-           ",last_reserve_payback_serial_id"
-           ",last_reserve_close_serial_id"
-          ",last_withdraw_serial_id"
-          ",last_deposit_serial_id"
-           ",last_melt_serial_id"
-          ",last_refund_serial_id"
-          ",last_wire_out_serial_id"
-           ") VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10);",
-           10, NULL);
-
-  /* Used in #postgres_update_auditor_progress() */
-  PREPARE ("auditor_progress_update",
-           "UPDATE auditor_progress SET "
-          " last_reserve_in_serial_id=$1"
-           ",last_reserve_out_serial_id=$2"
-           ",last_reserve_payback_serial_id=$3"
-           ",last_reserve_close_serial_id=$4"
-          ",last_withdraw_serial_id=$5"
-          ",last_deposit_serial_id=$6"
-           ",last_melt_serial_id=$7"
-          ",last_refund_serial_id=$8"
-          ",last_wire_out_serial_id=$9"
-           " WHERE master_pub=$10",
-           10, NULL);
-
-  /* Used in #postgres_get_auditor_progress() */
-  PREPARE ("auditor_progress_select",
-           "SELECT"
-          " last_reserve_in_serial_id"
-           ",last_reserve_out_serial_id"
-           ",last_reserve_payback_serial_id"
-           ",last_reserve_close_serial_id"
-          ",last_withdraw_serial_id"
-          ",last_deposit_serial_id"
-           ",last_melt_serial_id"
-          ",last_refund_serial_id"
-          ",last_wire_out_serial_id"
-           " FROM auditor_progress"
-           " WHERE master_pub=$1;",
-           1, NULL);
-
-  /* Used in #postgres_insert_reserve_info() */
-  PREPARE ("auditor_reserves_insert",
-           "INSERT INTO auditor_reserves "
-          "(reserve_pub"
-           ",master_pub"
-           ",reserve_balance_val"
-           ",reserve_balance_frac"
-           ",reserve_balance_curr"
-           ",withdraw_fee_balance_val"
-           ",withdraw_fee_balance_frac"
-           ",withdraw_fee_balance_curr"
-           ",expiration_date"
-           ") VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9);",
-           9, NULL);
-
-  /* Used in #postgres_update_reserve_info() */
-  PREPARE ("auditor_reserves_update",
-           "UPDATE auditor_reserves SET"
-           " reserve_balance_val=$1"
-           ",reserve_balance_frac=$2"
-           ",reserve_balance_curr=$3"
-           ",withdraw_fee_balance_val=$4"
-           ",withdraw_fee_balance_frac=$5"
-           ",withdraw_fee_balance_curr=$6"
-           ",expiration_date=$7"
-           " WHERE reserve_pub=$8 AND master_pub=$9;",
-           9, NULL);
-
-  /* Used in #postgres_get_reserve_info() */
-  PREPARE ("auditor_reserves_select",
-           "SELECT"
-           " reserve_balance_val"
-           ",reserve_balance_frac"
-           ",reserve_balance_curr"
-           ",withdraw_fee_balance_val"
-           ",withdraw_fee_balance_frac"
-           ",withdraw_fee_balance_curr"
-           ",expiration_date"
-           ",auditor_reserves_rowid"
-           " FROM auditor_reserves"
-           " WHERE reserve_pub=$1 AND master_pub=$2;",
-           2, NULL);
-
-  /* Used in #postgres_del_reserve_info() */
-  PREPARE ("auditor_reserves_delete",
-           "DELETE"
-           " FROM auditor_reserves"
-           " WHERE reserve_pub=$1 AND master_pub=$2;",
-           2, NULL);
-
-  /* Used in #postgres_insert_reserve_summary() */
-  PREPARE ("auditor_reserve_balance_insert",
-           "INSERT INTO auditor_reserve_balance"
-          "(master_pub"
-          ",reserve_balance_val"
-           ",reserve_balance_frac"
-           ",reserve_balance_curr"
-           ",withdraw_fee_balance_val"
-           ",withdraw_fee_balance_frac"
-           ",withdraw_fee_balance_curr"
-           ") VALUES ($1,$2,$3,$4,$5,$6,$7)",
-           7, NULL);
-
-  /* Used in #postgres_update_reserve_summary() */
-  PREPARE ("auditor_reserve_balance_update",
-           "UPDATE auditor_reserve_balance SET"
-          " reserve_balance_val=$1"
-           ",reserve_balance_frac=$2"
-           ",reserve_balance_curr=$3"
-           ",withdraw_fee_balance_val=$4"
-           ",withdraw_fee_balance_frac=$5"
-           ",withdraw_fee_balance_curr=$6"
-           " WHERE master_pub=$7;",
-           7, NULL);
-
-  /* Used in #postgres_get_reserve_summary() */
-  PREPARE ("auditor_reserve_balance_select",
-           "SELECT"
-          " reserve_balance_val"
-           ",reserve_balance_frac"
-           ",reserve_balance_curr"
-           ",withdraw_fee_balance_val"
-           ",withdraw_fee_balance_frac"
-           ",withdraw_fee_balance_curr"
-           " FROM auditor_reserve_balance"
-           " WHERE master_pub=$1;",
-           1, NULL);
-
-  /* Used in #postgres_insert_wire_fee_summary() */
-  PREPARE ("auditor_wire_fee_balance_insert",
-           "INSERT INTO auditor_wire_fee_balance"
-          "(master_pub"
-          ",wire_fee_balance_val"
-           ",wire_fee_balance_frac"
-           ",wire_fee_balance_curr"
-           ") VALUES ($1,$2,$3,$4)",
-           4, NULL);
-
-  /* Used in #postgres_update_wire_fee_summary() */
-  PREPARE ("auditor_wire_fee_balance_update",
-           "UPDATE auditor_wire_fee_balance SET"
-          " wire_fee_balance_val=$1"
-           ",wire_fee_balance_frac=$2"
-           ",wire_fee_balance_curr=$3"
-           " WHERE master_pub=$4;",
-           4, NULL);
-
-  /* Used in #postgres_get_wire_fee_summary() */
-  PREPARE ("auditor_wire_fee_balance_select",
-           "SELECT"
-          " wire_fee_balance_val"
-           ",wire_fee_balance_frac"
-           ",wire_fee_balance_curr"
-           " FROM auditor_wire_fee_balance"
-           " WHERE master_pub=$1;",
-           1, NULL);
-
-
-  /* Used in #postgres_insert_denomination_balance() */
-  PREPARE ("denomination_pending_insert",
-           "INSERT INTO denomination_pending "
-          "(denom_pub_hash"
-           ",denom_balance_val"
-           ",denom_balance_frac"
-           ",denom_balance_curr"
-           ",denom_risk_val"
-           ",denom_risk_frac"
-           ",denom_risk_curr"
-           ") VALUES ($1,$2,$3,$4,$5,$6,$7);",
-           7, NULL);
-
-  /* Used in #postgres_update_denomination_balance() */
-  PREPARE ("denomination_pending_update",
-           "UPDATE denomination_pending SET"
-           " denom_balance_val=$1"
-           ",denom_balance_frac=$2"
-           ",denom_balance_curr=$3"
-           ",denom_risk_val=$4"
-           ",denom_risk_frac=$5"
-           ",denom_risk_curr=$6"
-           " WHERE denom_pub_hash=$7",
-           7, NULL);
-
-  /* Used in #postgres_get_denomination_balance() */
-  PREPARE ("denomination_pending_select",
-           "SELECT"
-           " denom_balance_val"
-           ",denom_balance_frac"
-           ",denom_balance_curr"
-           ",denom_risk_val"
-           ",denom_risk_frac"
-           ",denom_risk_curr"
-           " FROM denomination_pending"
-           " WHERE denom_pub_hash=$1",
-           1, NULL);
-
-  /* Used in #postgres_insert_balance_summary() */
-  PREPARE ("balance_summary_insert",
-           "INSERT INTO balance_summary "
-          "(master_pub"
-          ",denom_balance_val"
-           ",denom_balance_frac"
-           ",denom_balance_curr"
-           ",deposit_fee_balance_val"
-           ",deposit_fee_balance_frac"
-           ",deposit_fee_balance_curr"
-           ",melt_fee_balance_val"
-           ",melt_fee_balance_frac"
-           ",melt_fee_balance_curr"
-           ",refund_fee_balance_val"
-           ",refund_fee_balance_frac"
-           ",refund_fee_balance_curr"
-          ",risk_val"
-           ",risk_frac"
-           ",risk_curr"
-           ") VALUES 
($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16);",
-           16, NULL);
-
-  /* Used in #postgres_update_balance_summary() */
-  PREPARE ("balance_summary_update",
-           "UPDATE balance_summary SET"
-          " denom_balance_val=$1"
-           ",denom_balance_frac=$2"
-           ",denom_balance_curr=$3"
-           ",deposit_fee_balance_val=$4"
-           ",deposit_fee_balance_frac=$5"
-           ",deposit_fee_balance_curr=$6"
-           ",melt_fee_balance_val=$7"
-           ",melt_fee_balance_frac=$8"
-           ",melt_fee_balance_curr=$9"
-           ",refund_fee_balance_val=$10"
-           ",refund_fee_balance_frac=$11"
-           ",refund_fee_balance_curr=$12"
-          ",risk_val=$13"
-           ",risk_frac=$14"
-           ",risk_curr=$15"
-           " WHERE master_pub=$16;",
-           16, NULL);
-
-  /* Used in #postgres_get_balance_summary() */
-  PREPARE ("balance_summary_select",
-           "SELECT"
-          " denom_balance_val"
-           ",denom_balance_frac"
-           ",denom_balance_curr"
-           ",deposit_fee_balance_val"
-           ",deposit_fee_balance_frac"
-           ",deposit_fee_balance_curr"
-           ",melt_fee_balance_val"
-           ",melt_fee_balance_frac"
-           ",melt_fee_balance_curr"
-           ",refund_fee_balance_val"
-           ",refund_fee_balance_frac"
-           ",refund_fee_balance_curr"
-          ",risk_val"
-           ",risk_frac"
-           ",risk_curr"
-           " FROM balance_summary"
-           " WHERE master_pub=$1;",
-           1, NULL);
-
-  /* Used in #postgres_insert_historic_denom_revenue() */
-  PREPARE ("historic_denomination_revenue_insert",
-           "INSERT INTO historic_denomination_revenue"
-          "(master_pub"
-          ",denom_pub_hash"
-          ",revenue_timestamp"
-          ",revenue_balance_val"
-           ",revenue_balance_frac"
-           ",revenue_balance_curr"
-           ") VALUES ($1,$2,$3,$4,$5,$6);",
-           6, NULL);
-
-  /* Used in #postgres_select_historic_denom_revenue() */
-  PREPARE ("historic_denomination_revenue_select",
-           "SELECT"
-          " denom_pub_hash"
-          ",revenue_timestamp"
-          ",revenue_balance_val"
-           ",revenue_balance_frac"
-           ",revenue_balance_curr"
-           " FROM historic_denomination_revenue"
-           " WHERE master_pub=$1;",
-           1, NULL);
-
-  /* Used in #postgres_insert_historic_losses() */
-  PREPARE ("historic_losses_insert",
-           "INSERT INTO historic_losses"
-          "(master_pub"
-          ",denom_pub_hash"
-          ",loss_timestamp"
-          ",loss_balance_val"
-           ",loss_balance_frac"
-           ",loss_balance_curr"
-           ") VALUES ($1,$2,$3,$4,$5,$6);",
-           6, NULL);
-
-  /* Used in #postgres_select_historic_losses() */
-  PREPARE ("historic_losses_select",
-           "SELECT"
-          " denom_pub_hash"
-          ",loss_timestamp"
-          ",loss_balance_val"
-           ",loss_balance_frac"
-           ",loss_balance_curr"
-           " FROM historic_losses"
-           " WHERE master_pub=$1;",
-           1, NULL);
-
-  /* Used in #postgres_insert_historic_reserve_revenue() */
-  PREPARE ("historic_reserve_summary_insert",
-           "INSERT INTO historic_reserve_summary"
-          "(master_pub"
-          ",start_date"
-          ",end_date"
-          ",reserve_profits_val"
-           ",reserve_profits_frac"
-           ",reserve_profits_curr"
-           ") VALUES ($1,$2,$3,$4,$5,$6);",
-           6, NULL);
-
-  /* Used in #postgres_select_historic_reserve_revenue() */
-  PREPARE ("historic_reserve_summary_select",
-           "SELECT"
-          " start_date"
-          ",end_date"
-          ",reserve_profits_val"
-           ",reserve_profits_frac"
-           ",reserve_profits_curr"
-           " FROM historic_reserve_summary"
-           " WHERE master_pub=$1;",
-           1, NULL);
-
-  /* Used in #postgres_insert_predicted_result() */
-  PREPARE ("predicted_result_insert",
-           "INSERT INTO predicted_result"
-          "(master_pub"
-          ",balance_val"
-           ",balance_frac"
-           ",balance_curr"
-           ") VALUES ($1,$2,$3,$4);",
-           4, NULL);
-
-  /* Used in #postgres_update_predicted_result() */
-  PREPARE ("predicted_result_update",
-           "UPDATE predicted_result SET"
-          " balance_val=$1"
-           ",balance_frac=$2"
-           ",balance_curr=$3"
-           " WHERE master_pub=$4;",
-           4, NULL);
-
-  /* Used in #postgres_get_predicted_balance() */
-  PREPARE ("predicted_result_select",
-           "SELECT"
-          " balance_val"
-           ",balance_frac"
-           ",balance_curr"
-           " FROM predicted_result"
-           " WHERE master_pub=$1;",
-           1, NULL);
+  struct GNUNET_PQ_PreparedStatement ps[] = {
+    /* used in #postgres_commit */
+    GNUNET_PQ_make_prepare ("do_commit",
+                            "COMMIT",
+                            0),
+    /* Used in #postgres_insert_denomination_info() */
+    GNUNET_PQ_make_prepare ("auditor_denominations_insert",
+                           "INSERT INTO auditor_denominations "
+                           "(denom_pub_hash"
+                           ",master_pub"
+                           ",valid_from"
+                           ",expire_withdraw"
+                           ",expire_deposit"
+                           ",expire_legal"
+                           ",coin_val"
+                           ",coin_frac"
+                           ",coin_curr"
+                           ",fee_withdraw_val"
+                           ",fee_withdraw_frac"
+                           ",fee_withdraw_curr"
+                           ",fee_deposit_val"
+                           ",fee_deposit_frac"
+                           ",fee_deposit_curr"
+                           ",fee_refresh_val"
+                           ",fee_refresh_frac"
+                           ",fee_refresh_curr"
+                           ",fee_refund_val"
+                           ",fee_refund_frac"
+                           ",fee_refund_curr"
+                           ") VALUES 
($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16,$17,$18,$19,$20,$21);",
+                           21),
+    /* Used in #postgres_insert_denomination_info() */
+    GNUNET_PQ_make_prepare ("auditor_denominations_select",
+                           "SELECT"
+                           " denom_pub_hash"
+                           ",valid_from"
+                           ",expire_withdraw"
+                           ",expire_deposit"
+                           ",expire_legal"
+                           ",coin_val"
+                           ",coin_frac"
+                           ",coin_curr"
+                           ",fee_withdraw_val"
+                           ",fee_withdraw_frac"
+                           ",fee_withdraw_curr"
+                           ",fee_deposit_val"
+                           ",fee_deposit_frac"
+                           ",fee_deposit_curr"
+                           ",fee_refresh_val"
+                           ",fee_refresh_frac"
+                           ",fee_refresh_curr"
+                           ",fee_refund_val"
+                           ",fee_refund_frac"
+                           ",fee_refund_curr"
+                           " FROM auditor_denominations"
+                           " WHERE master_pub=$1;",
+                           1),
+    /* Used in #postgres_insert_auditor_progress() */
+    GNUNET_PQ_make_prepare ("auditor_progress_insert",
+                           "INSERT INTO auditor_progress "
+                           "(master_pub"
+                           ",last_reserve_in_serial_id"
+                           ",last_reserve_out_serial_id"
+                           ",last_reserve_payback_serial_id"
+                           ",last_reserve_close_serial_id"
+                           ",last_withdraw_serial_id"
+                           ",last_deposit_serial_id"
+                           ",last_melt_serial_id"
+                           ",last_refund_serial_id"
+                           ",last_wire_out_serial_id"
+                           ") VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10);",
+                           10),
+    /* Used in #postgres_update_auditor_progress() */
+    GNUNET_PQ_make_prepare ("auditor_progress_update",
+                           "UPDATE auditor_progress SET "
+                           " last_reserve_in_serial_id=$1"
+                           ",last_reserve_out_serial_id=$2"
+                           ",last_reserve_payback_serial_id=$3"
+                           ",last_reserve_close_serial_id=$4"
+                           ",last_withdraw_serial_id=$5"
+                           ",last_deposit_serial_id=$6"
+                           ",last_melt_serial_id=$7"
+                           ",last_refund_serial_id=$8"
+                           ",last_wire_out_serial_id=$9"
+                           " WHERE master_pub=$10",
+                           10),
+    /* Used in #postgres_get_auditor_progress() */
+    GNUNET_PQ_make_prepare ("auditor_progress_select",
+                           "SELECT"
+                           " last_reserve_in_serial_id"
+                           ",last_reserve_out_serial_id"
+                           ",last_reserve_payback_serial_id"
+                           ",last_reserve_close_serial_id"
+                           ",last_withdraw_serial_id"
+                           ",last_deposit_serial_id"
+                           ",last_melt_serial_id"
+                           ",last_refund_serial_id"
+                           ",last_wire_out_serial_id"
+                           " FROM auditor_progress"
+                           " WHERE master_pub=$1;",
+                           1),
+    /* Used in #postgres_insert_reserve_info() */
+    GNUNET_PQ_make_prepare ("auditor_reserves_insert",
+                           "INSERT INTO auditor_reserves "
+                           "(reserve_pub"
+                           ",master_pub"
+                           ",reserve_balance_val"
+                           ",reserve_balance_frac"
+                           ",reserve_balance_curr"
+                           ",withdraw_fee_balance_val"
+                           ",withdraw_fee_balance_frac"
+                           ",withdraw_fee_balance_curr"
+                           ",expiration_date"
+                           ") VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9);",
+                           9),
+    /* Used in #postgres_update_reserve_info() */
+    GNUNET_PQ_make_prepare ("auditor_reserves_update",
+                           "UPDATE auditor_reserves SET"
+                           " reserve_balance_val=$1"
+                           ",reserve_balance_frac=$2"
+                           ",reserve_balance_curr=$3"
+                           ",withdraw_fee_balance_val=$4"
+                           ",withdraw_fee_balance_frac=$5"
+                           ",withdraw_fee_balance_curr=$6"
+                           ",expiration_date=$7"
+                           " WHERE reserve_pub=$8 AND master_pub=$9;",
+                           9),
+    /* Used in #postgres_get_reserve_info() */
+    GNUNET_PQ_make_prepare ("auditor_reserves_select",
+                           "SELECT"
+                           " reserve_balance_val"
+                           ",reserve_balance_frac"
+                           ",reserve_balance_curr"
+                           ",withdraw_fee_balance_val"
+                           ",withdraw_fee_balance_frac"
+                           ",withdraw_fee_balance_curr"
+                           ",expiration_date"
+                           ",auditor_reserves_rowid"
+                           " FROM auditor_reserves"
+                           " WHERE reserve_pub=$1 AND master_pub=$2;",
+                           2),
+    /* Used in #postgres_del_reserve_info() */
+    GNUNET_PQ_make_prepare ("auditor_reserves_delete",
+                           "DELETE"
+                           " FROM auditor_reserves"
+                           " WHERE reserve_pub=$1 AND master_pub=$2;",
+                           2),
+    /* Used in #postgres_insert_reserve_summary() */
+    GNUNET_PQ_make_prepare ("auditor_reserve_balance_insert",
+                           "INSERT INTO auditor_reserve_balance"
+                           "(master_pub"
+                           ",reserve_balance_val"
+                           ",reserve_balance_frac"
+                           ",reserve_balance_curr"
+                           ",withdraw_fee_balance_val"
+                           ",withdraw_fee_balance_frac"
+                           ",withdraw_fee_balance_curr"
+                           ") VALUES ($1,$2,$3,$4,$5,$6,$7)",
+                           7),
+    /* Used in #postgres_update_reserve_summary() */
+    GNUNET_PQ_make_prepare ("auditor_reserve_balance_update",
+                           "UPDATE auditor_reserve_balance SET"
+                           " reserve_balance_val=$1"
+                           ",reserve_balance_frac=$2"
+                           ",reserve_balance_curr=$3"
+                           ",withdraw_fee_balance_val=$4"
+                           ",withdraw_fee_balance_frac=$5"
+                           ",withdraw_fee_balance_curr=$6"
+                           " WHERE master_pub=$7;",
+                           7),
+    /* Used in #postgres_get_reserve_summary() */
+    GNUNET_PQ_make_prepare ("auditor_reserve_balance_select",
+                           "SELECT"
+                           " reserve_balance_val"
+                           ",reserve_balance_frac"
+                           ",reserve_balance_curr"
+                           ",withdraw_fee_balance_val"
+                           ",withdraw_fee_balance_frac"
+                           ",withdraw_fee_balance_curr"
+                           " FROM auditor_reserve_balance"
+                           " WHERE master_pub=$1;",
+                           1),
+    /* Used in #postgres_insert_wire_fee_summary() */
+    GNUNET_PQ_make_prepare ("auditor_wire_fee_balance_insert",
+                           "INSERT INTO auditor_wire_fee_balance"
+                           "(master_pub"
+                           ",wire_fee_balance_val"
+                           ",wire_fee_balance_frac"
+                           ",wire_fee_balance_curr"
+                           ") VALUES ($1,$2,$3,$4)",
+                           4),
+    /* Used in #postgres_update_wire_fee_summary() */
+    GNUNET_PQ_make_prepare ("auditor_wire_fee_balance_update",
+                           "UPDATE auditor_wire_fee_balance SET"
+                           " wire_fee_balance_val=$1"
+                           ",wire_fee_balance_frac=$2"
+                           ",wire_fee_balance_curr=$3"
+                           " WHERE master_pub=$4;",
+                           4),
+    /* Used in #postgres_get_wire_fee_summary() */
+    GNUNET_PQ_make_prepare ("auditor_wire_fee_balance_select",
+                           "SELECT"
+                           " wire_fee_balance_val"
+                           ",wire_fee_balance_frac"
+                           ",wire_fee_balance_curr"
+                           " FROM auditor_wire_fee_balance"
+                           " WHERE master_pub=$1;",
+                           1),
+    /* Used in #postgres_insert_denomination_balance() */
+    GNUNET_PQ_make_prepare ("denomination_pending_insert",
+                           "INSERT INTO denomination_pending "
+                           "(denom_pub_hash"
+                           ",denom_balance_val"
+                           ",denom_balance_frac"
+                           ",denom_balance_curr"
+                           ",denom_risk_val"
+                           ",denom_risk_frac"
+                           ",denom_risk_curr"
+                           ") VALUES ($1,$2,$3,$4,$5,$6,$7);",
+                           7),    
+    /* Used in #postgres_update_denomination_balance() */
+    GNUNET_PQ_make_prepare ("denomination_pending_update",
+                           "UPDATE denomination_pending SET"
+                           " denom_balance_val=$1"
+                           ",denom_balance_frac=$2"
+                           ",denom_balance_curr=$3"
+                           ",denom_risk_val=$4"
+                           ",denom_risk_frac=$5"
+                           ",denom_risk_curr=$6"
+                           " WHERE denom_pub_hash=$7",
+                           7),    
+    /* Used in #postgres_get_denomination_balance() */
+    GNUNET_PQ_make_prepare ("denomination_pending_select",
+                           "SELECT"
+                           " denom_balance_val"
+                           ",denom_balance_frac"
+                           ",denom_balance_curr"
+                           ",denom_risk_val"
+                           ",denom_risk_frac"
+                           ",denom_risk_curr"
+                           " FROM denomination_pending"
+                           " WHERE denom_pub_hash=$1",
+                           1),
+    /* Used in #postgres_insert_balance_summary() */
+    GNUNET_PQ_make_prepare ("balance_summary_insert",
+                           "INSERT INTO balance_summary "
+                           "(master_pub"
+                           ",denom_balance_val"
+                           ",denom_balance_frac"
+                           ",denom_balance_curr"
+                           ",deposit_fee_balance_val"
+                           ",deposit_fee_balance_frac"
+                           ",deposit_fee_balance_curr"
+                           ",melt_fee_balance_val"
+                           ",melt_fee_balance_frac"
+                           ",melt_fee_balance_curr"
+                           ",refund_fee_balance_val"
+                           ",refund_fee_balance_frac"
+                           ",refund_fee_balance_curr"
+                           ",risk_val"
+                           ",risk_frac"
+                           ",risk_curr"
+                           ") VALUES 
($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13,$14,$15,$16);",
+                           16),
+    /* Used in #postgres_update_balance_summary() */
+    GNUNET_PQ_make_prepare ("balance_summary_update",
+                           "UPDATE balance_summary SET"
+                           " denom_balance_val=$1"
+                           ",denom_balance_frac=$2"
+                           ",denom_balance_curr=$3"
+                           ",deposit_fee_balance_val=$4"
+                           ",deposit_fee_balance_frac=$5"
+                           ",deposit_fee_balance_curr=$6"
+                           ",melt_fee_balance_val=$7"
+                           ",melt_fee_balance_frac=$8"
+                           ",melt_fee_balance_curr=$9"
+                           ",refund_fee_balance_val=$10"
+                           ",refund_fee_balance_frac=$11"
+                           ",refund_fee_balance_curr=$12"
+                           ",risk_val=$13"
+                           ",risk_frac=$14"
+                           ",risk_curr=$15"
+                           " WHERE master_pub=$16;",
+                           16),
+    /* Used in #postgres_get_balance_summary() */
+    GNUNET_PQ_make_prepare ("balance_summary_select",
+                           "SELECT"
+                           " denom_balance_val"
+                           ",denom_balance_frac"
+                           ",denom_balance_curr"
+                           ",deposit_fee_balance_val"
+                           ",deposit_fee_balance_frac"
+                           ",deposit_fee_balance_curr"
+                           ",melt_fee_balance_val"
+                           ",melt_fee_balance_frac"
+                           ",melt_fee_balance_curr"
+                           ",refund_fee_balance_val"
+                           ",refund_fee_balance_frac"
+                           ",refund_fee_balance_curr"
+                           ",risk_val"
+                           ",risk_frac"
+                           ",risk_curr"
+                           " FROM balance_summary"
+                           " WHERE master_pub=$1;",
+                           1),
+    /* Used in #postgres_insert_historic_denom_revenue() */
+    GNUNET_PQ_make_prepare ("historic_denomination_revenue_insert",
+                           "INSERT INTO historic_denomination_revenue"
+                           "(master_pub"
+                           ",denom_pub_hash"
+                           ",revenue_timestamp"
+                           ",revenue_balance_val"
+                           ",revenue_balance_frac"
+                           ",revenue_balance_curr"
+                           ") VALUES ($1,$2,$3,$4,$5,$6);",
+                           6),
+    /* Used in #postgres_select_historic_denom_revenue() */
+    GNUNET_PQ_make_prepare ("historic_denomination_revenue_select",
+                           "SELECT"
+                           " denom_pub_hash"
+                           ",revenue_timestamp"
+                           ",revenue_balance_val"
+                           ",revenue_balance_frac"
+                           ",revenue_balance_curr"
+                           " FROM historic_denomination_revenue"
+                           " WHERE master_pub=$1;",
+                           1),
+    /* Used in #postgres_insert_historic_losses() */
+    GNUNET_PQ_make_prepare ("historic_losses_insert",
+                           "INSERT INTO historic_losses"
+                           "(master_pub"
+                           ",denom_pub_hash"
+                           ",loss_timestamp"
+                           ",loss_balance_val"
+                           ",loss_balance_frac"
+                           ",loss_balance_curr"
+                           ") VALUES ($1,$2,$3,$4,$5,$6);",
+                           6),
+    /* Used in #postgres_select_historic_losses() */
+    GNUNET_PQ_make_prepare ("historic_losses_select",
+                           "SELECT"
+                           " denom_pub_hash"
+                           ",loss_timestamp"
+                           ",loss_balance_val"
+                           ",loss_balance_frac"
+                           ",loss_balance_curr"
+                           " FROM historic_losses"
+                           " WHERE master_pub=$1;",
+                           1),
+    /* Used in #postgres_insert_historic_reserve_revenue() */
+    GNUNET_PQ_make_prepare ("historic_reserve_summary_insert",
+                           "INSERT INTO historic_reserve_summary"
+                           "(master_pub"
+                           ",start_date"
+                           ",end_date"
+                           ",reserve_profits_val"
+                           ",reserve_profits_frac"
+                           ",reserve_profits_curr"
+                           ") VALUES ($1,$2,$3,$4,$5,$6);",
+                           6),    
+    /* Used in #postgres_select_historic_reserve_revenue() */
+    GNUNET_PQ_make_prepare ("historic_reserve_summary_select",
+                           "SELECT"
+                           " start_date"
+                           ",end_date"
+                           ",reserve_profits_val"
+                           ",reserve_profits_frac"
+                           ",reserve_profits_curr"
+                           " FROM historic_reserve_summary"
+                           " WHERE master_pub=$1;",
+                           1),
+    /* Used in #postgres_insert_predicted_result() */
+    GNUNET_PQ_make_prepare ("predicted_result_insert",
+                           "INSERT INTO predicted_result"
+                           "(master_pub"
+                           ",balance_val"
+                           ",balance_frac"
+                           ",balance_curr"
+                           ") VALUES ($1,$2,$3,$4);",
+                           4),
+    /* Used in #postgres_update_predicted_result() */
+    GNUNET_PQ_make_prepare ("predicted_result_update",
+                           "UPDATE predicted_result SET"
+                           " balance_val=$1"
+                           ",balance_frac=$2"
+                           ",balance_curr=$3"
+                           " WHERE master_pub=$4;",
+                           4),
+    /* Used in #postgres_get_predicted_balance() */
+    GNUNET_PQ_make_prepare ("predicted_result_select",
+                           "SELECT"
+                           " balance_val"
+                           ",balance_frac"
+                           ",balance_curr"
+                           " FROM predicted_result"
+                           " WHERE master_pub=$1;",
+                           1),
+    GNUNET_PQ_PREPARED_STATEMENT_END
+  };
 
-  return GNUNET_OK;
-#undef PREPARE
+  return GNUNET_PQ_prepare_statements (db_conn,
+                                       ps);
 }
 
 
@@ -954,8 +821,11 @@ static void
 db_conn_destroy (void *cls)
 {
   struct TALER_AUDITORDB_Session *session = cls;
-  PGconn *db_conn = session->conn;
+  PGconn *db_conn;
 
+  if (NULL == session)
+    return;
+  db_conn = session->conn;
   if (NULL != db_conn)
     PQfinish (db_conn);
   GNUNET_free (session);
@@ -977,7 +847,23 @@ postgres_get_session (void *cls)
   struct TALER_AUDITORDB_Session *session;
 
   if (NULL != (session = pthread_getspecific (pc->db_conn_threadlocal)))
-    return session;
+  {
+    if (CONNECTION_BAD == PQstatus (session->conn))
+    {
+      /**
+       * Reset the thread-local database-handle.  Disconnects from the
+       * DB.  Needed after the database server restarts as we need to
+       * properly reconnect. */
+      GNUNET_assert (0 == pthread_setspecific (pc->db_conn_threadlocal,
+                                             NULL));
+      PQfinish (session->conn);
+      GNUNET_free (session);
+    }
+    else
+    {
+      return session;
+    }
+  }
   db_conn = connect_to_postgres (pc);
   if (NULL == db_conn)
     return NULL;
@@ -1026,7 +912,6 @@ postgres_start (void *cls,
     PQclear (result);
     return GNUNET_SYSERR;
   }
-
   PQclear (result);
   return GNUNET_OK;
 }
@@ -1058,49 +943,19 @@ postgres_rollback (void *cls,
  *
  * @param cls the `struct PostgresClosure` with the plugin-specific state
  * @param session the database connection
- * @return #GNUNET_OK on success
+ * @return transaction status code
  */
-static int
+enum GNUNET_DB_QueryStatus
 postgres_commit (void *cls,
                  struct TALER_AUDITORDB_Session *session)
 {
-  PGresult *result;
-
-  result = PQexec (session->conn,
-                   "COMMIT");
-  if (PGRES_COMMAND_OK !=
-      PQresultStatus (result))
-  {
-    const char *sqlstate;
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_end
+  };
 
-    sqlstate = PQresultErrorField (result,
-                                   PG_DIAG_SQLSTATE);
-    if (NULL == sqlstate)
-    {
-      /* very unexpected... */
-      GNUNET_break (0);
-      PQclear (result);
-      return GNUNET_SYSERR;
-    }
-    /* 40P01: deadlock, 40001: serialization failure */
-    if ( (0 == strcmp (sqlstate,
-                       "40P01")) ||
-         (0 == strcmp (sqlstate,
-                       "40001")) )
-    {
-      /* These two can be retried and have a fair chance of working
-         the next time */
-      PQclear (result);
-      return GNUNET_NO;
-    }
-    LOG (GNUNET_ERROR_TYPE_ERROR,
-         "Database commit failure: %s\n",
-         sqlstate);
-    PQclear (result);
-    return GNUNET_SYSERR;
-  }
-  PQclear (result);
-  return GNUNET_OK;
+  return GNUNET_PQ_eval_prepared_non_select (session->conn,
+                                             "do_commit",
+                                             params);
 }
 
 
@@ -1122,7 +977,7 @@ postgres_gc (void *cls)
     GNUNET_PQ_query_param_end
   };
   PGconn *conn;
-  PGresult *result;
+  enum GNUNET_DB_QueryStatus qs;
 
   now = GNUNET_TIME_absolute_get ();
   conn = connect_to_postgres (pc);
@@ -1135,17 +990,15 @@ postgres_gc (void *cls)
     return GNUNET_SYSERR;
   }
   /* FIXME: this is obviously not going to be this easy... */
-  result = GNUNET_PQ_exec_prepared (conn,
-                                    "gc_auditor",
-                                    params_time);
-  if (PGRES_COMMAND_OK != PQresultStatus (result))
+  qs = GNUNET_PQ_eval_prepared_non_select (conn,
+                                          "gc_auditor",
+                                          params_time);
+  if (0 > qs)
   {
-    BREAK_DB_ERR (result);
-    PQclear (result);
+    GNUNET_break (0);
     PQfinish (conn);
     return GNUNET_SYSERR;
   }
-  PQclear (result);
   PQfinish (conn);
   return GNUNET_OK;
 }
@@ -1159,16 +1012,13 @@ postgres_gc (void *cls)
  * @param cls the @e cls of this struct with the plugin-specific state
  * @param session connection to use
  * @param issue issuing information with value, fees and other info about the 
denomination
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+ * @return operation status result
  */
-static int
+static enum GNUNET_DB_QueryStatus
 postgres_insert_denomination_info (void *cls,
                                    struct TALER_AUDITORDB_Session *session,
                                    const struct 
TALER_DenominationKeyValidityPS *issue)
 {
-  PGresult *result;
-  int ret;
-
   struct GNUNET_PQ_QueryParam params[] = {
     GNUNET_PQ_query_param_auto_from_type (&issue->denom_hash),
     GNUNET_PQ_query_param_auto_from_type (&issue->master),
@@ -1197,70 +1047,61 @@ postgres_insert_denomination_info (void *cls,
   GNUNET_assert (GNUNET_YES ==
                  TALER_amount_cmp_currency_nbo (&issue->value,
                                                &issue->fee_refund));
-
-  result = GNUNET_PQ_exec_prepared (session->conn,
-                                   "auditor_denominations_insert",
-                                   params);
-  if (PGRES_COMMAND_OK != PQresultStatus (result))
-  {
-    ret = GNUNET_SYSERR;
-    BREAK_DB_ERR (result);
-  }
-  else
-  {
-    ret = GNUNET_OK;
-  }
-  PQclear (result);
-  return ret;
+  return GNUNET_PQ_eval_prepared_non_select (session->conn,
+                                            "auditor_denominations_insert",
+                                            params);
 }
 
 
 /**
- * Get information about denomination keys of a particular exchange.
+ * Closure for #denomination_info_cb().
+ */
+struct DenominationInfoContext
+{
+
+  /**
+   * Master public key that is being used.
+   */
+  const struct TALER_MasterPublicKeyP *master_pub;
+  
+  /**
+   * Function to call for each denomination.
+   */
+  TALER_AUDITORDB_DenominationInfoDataCallback cb;
+
+  /**
+   * Closure for @e cb
+   */
+  void *cb_cls;
+  
+  /**
+   * Query status to return.
+   */
+  enum GNUNET_DB_QueryStatus qs;
+};
+
+
+/**
+ * Helper function for #postgres_select_denomination_info().
+ * To be called with the results of a SELECT statement
+ * that has returned @a num_results results.
  *
- * @param cls the @e cls of this struct with the plugin-specific state
- * @param session connection to use
- * @param master_pub master public key of the exchange
- * @param cb function to call with the results
- * @param cb_cls closure for @a cb
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+ * @param cls closure of type `struct DenominationInfoContext *`
+ * @param result the postgres result
+ * @param num_result the number of results in @a result
  */
-static int
-postgres_select_denomination_info (void *cls,
-                                   struct TALER_AUDITORDB_Session *session,
-                                   const struct TALER_MasterPublicKeyP 
*master_pub,
-                                   
TALER_AUDITORDB_DenominationInfoDataCallback cb,
-                                   void *cb_cls)
+static void
+denomination_info_cb (void *cls,
+                     PGresult *result,
+                     unsigned int num_results)
 {
-  struct GNUNET_PQ_QueryParam params[] = {
-    GNUNET_PQ_query_param_auto_from_type (master_pub),
-    GNUNET_PQ_query_param_end
-  };
-  PGresult *result;
-  result = GNUNET_PQ_exec_prepared (session->conn,
-                                    "auditor_denominations_select",
-                                    params);
-  if (PGRES_TUPLES_OK !=
-      PQresultStatus (result))
-  {
-    BREAK_DB_ERR (result);
-    PQclear (result);
-    return GNUNET_SYSERR;
-  }
+  struct DenominationInfoContext *dic = cls;
 
-  int ret = GNUNET_OK;
-  int nrows = PQntuples (result);
-  if (0 == nrows)
-  {
-    LOG (GNUNET_ERROR_TYPE_DEBUG,
-         "postgres_select_denomination_info() returned 0 matching rows\n");
-    PQclear (result);
-    return GNUNET_NO;
-  }
-  for (int i = 0; i < nrows; i++)
+  for (unsigned int i = 0; i < num_results; i++)
   {
-    struct TALER_DenominationKeyValidityPS issue = { .master = *master_pub };
-
+    struct TALER_DenominationKeyValidityPS issue = {
+      .master = *dic->master_pub
+    };
     struct GNUNET_PQ_ResultSpec rs[] = {
       GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash", 
&issue.denom_hash),
       GNUNET_PQ_result_spec_auto_from_type ("valid_from", &issue.start),
@@ -1274,26 +1115,62 @@ postgres_select_denomination_info (void *cls,
       TALER_PQ_result_spec_amount_nbo ("fee_refund", &issue.fee_refund),
       GNUNET_PQ_result_spec_end
     };
+
     if (GNUNET_OK !=
-        GNUNET_PQ_extract_result (result, rs, 0))
+        GNUNET_PQ_extract_result (result,
+                                 rs,
+                                 i))
     {
       GNUNET_break (0);
-      PQclear (result);
-      return GNUNET_SYSERR;
-    }
-    ret = cb (cb_cls,
-              &issue);
-    switch (ret)
-    {
-    case GNUNET_OK:
-      break;
-
-    default:
-      i = nrows;
+      dic->qs = GNUNET_DB_STATUS_HARD_ERROR;
+      return;
     }
+    dic->qs = i + 1;
+    if (GNUNET_OK !=
+       dic->cb (dic->cb_cls,
+                &issue))
+      return;
   }
-  PQclear (result);
-  return ret;
+}
+
+
+/**
+ * Get information about denomination keys of a particular exchange.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param session connection to use
+ * @param master_pub master public key of the exchange
+ * @param cb function to call with the results
+ * @param cb_cls closure for @a cb
+ * @return transaction status code
+ */
+static enum GNUNET_DB_QueryStatus
+postgres_select_denomination_info (void *cls,
+                                   struct TALER_AUDITORDB_Session *session,
+                                   const struct TALER_MasterPublicKeyP 
*master_pub,
+                                   
TALER_AUDITORDB_DenominationInfoDataCallback cb,
+                                   void *cb_cls)
+{
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_auto_from_type (master_pub),
+    GNUNET_PQ_query_param_end
+  };
+  struct DenominationInfoContext dic = {
+    .master_pub = master_pub,
+    .cb = cb,
+    .cb_cls = cb_cls
+  };
+  enum GNUNET_DB_QueryStatus qs;
+  
+  qs = GNUNET_PQ_eval_prepared_multi_select (session->conn,
+                                            "auditor_denominations_select",
+                                            params,
+                                            &denomination_info_cb,
+                                            &dic);
+  if (qs > 0)
+    return dic.qs;
+  GNUNET_break (GNUNET_DB_STATUS_HARD_ERROR != qs);
+  return qs;
 }
 
 
@@ -1305,15 +1182,14 @@ postgres_select_denomination_info (void *cls,
  * @param session connection to use
  * @param master_pub master key of the exchange
  * @param pp where is the auditor in processing
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+ * @return transaction status code
  */
-int
+static enum GNUNET_DB_QueryStatus
 postgres_insert_auditor_progress (void *cls,
                                   struct TALER_AUDITORDB_Session *session,
                                   const struct TALER_MasterPublicKeyP 
*master_pub,
                                   const struct TALER_AUDITORDB_ProgressPoint 
*pp)
 {
-  PGresult *result;
   struct GNUNET_PQ_QueryParam params[] = {
     GNUNET_PQ_query_param_auto_from_type (master_pub),
     GNUNET_PQ_query_param_uint64 (&pp->last_reserve_in_serial_id),
@@ -1327,22 +1203,10 @@ postgres_insert_auditor_progress (void *cls,
     GNUNET_PQ_query_param_uint64 (&pp->last_wire_out_serial_id),
     GNUNET_PQ_query_param_end
   };
-  int ret;
 
-  result = GNUNET_PQ_exec_prepared (session->conn,
-                                   "auditor_progress_insert",
-                                   params);
-  if (PGRES_COMMAND_OK != PQresultStatus (result))
-  {
-    ret = GNUNET_SYSERR;
-    BREAK_DB_ERR (result);
-  }
-  else
-  {
-    ret = GNUNET_OK;
-  }
-  PQclear (result);
-  return ret;
+  return GNUNET_PQ_eval_prepared_non_select (session->conn,
+                                            "auditor_progress_insert",
+                                            params);
 }
 
 
@@ -1354,15 +1218,14 @@ postgres_insert_auditor_progress (void *cls,
  * @param session connection to use
  * @param master_pub master key of the exchange
  * @param pp where is the auditor in processing
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+ * @return transaction status code
  */
-int
+static enum GNUNET_DB_QueryStatus
 postgres_update_auditor_progress (void *cls,
                                   struct TALER_AUDITORDB_Session *session,
                                   const struct TALER_MasterPublicKeyP 
*master_pub,
                                   const struct TALER_AUDITORDB_ProgressPoint 
*pp)
 {
-  PGresult *result;
   struct GNUNET_PQ_QueryParam params[] = {
     GNUNET_PQ_query_param_uint64 (&pp->last_reserve_in_serial_id),
     GNUNET_PQ_query_param_uint64 (&pp->last_reserve_out_serial_id),
@@ -1376,22 +1239,10 @@ postgres_update_auditor_progress (void *cls,
     GNUNET_PQ_query_param_auto_from_type (master_pub),
     GNUNET_PQ_query_param_end
   };
-  int ret;
 
-  result = GNUNET_PQ_exec_prepared (session->conn,
-                                   "auditor_progress_update",
-                                   params);
-  if (PGRES_COMMAND_OK != PQresultStatus (result))
-  {
-    ret = GNUNET_SYSERR;
-    BREAK_DB_ERR (result);
-  }
-  else
-  {
-    ret = GNUNET_OK;
-  }
-  PQclear (result);
-  return ret;
+  return GNUNET_PQ_eval_prepared_non_select (session->conn,
+                                            "auditor_progress_update",
+                                            params);
 }
 
 
@@ -1402,10 +1253,9 @@ postgres_update_auditor_progress (void *cls,
  * @param session connection to use
  * @param master_pub master key of the exchange
  * @param[out] pp set to where the auditor is in processing
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure;
- *         #GNUNET_NO if we have no records for the @a master_pub
+ * @return transaction status code
  */
-int
+static enum GNUNET_DB_QueryStatus
 postgres_get_auditor_progress (void *cls,
                                struct TALER_AUDITORDB_Session *session,
                                const struct TALER_MasterPublicKeyP *master_pub,
@@ -1415,7 +1265,6 @@ postgres_get_auditor_progress (void *cls,
     GNUNET_PQ_query_param_auto_from_type (master_pub),
     GNUNET_PQ_query_param_end
   };
-  PGresult *result;
   struct GNUNET_PQ_ResultSpec rs[] = {
     GNUNET_PQ_result_spec_uint64 ("last_reserve_in_serial_id",
                                   &pp->last_reserve_in_serial_id),
@@ -1438,38 +1287,10 @@ postgres_get_auditor_progress (void *cls,
     GNUNET_PQ_result_spec_end
   };
 
-  result = GNUNET_PQ_exec_prepared (session->conn,
-                                    "auditor_progress_select",
-                                    params);
-  if (PGRES_TUPLES_OK !=
-      PQresultStatus (result))
-  {
-    BREAK_DB_ERR (result);
-    PQclear (result);
-    return GNUNET_SYSERR;
-  }
-
-  int nrows = PQntuples (result);
-  if (0 == nrows)
-  {
-    LOG (GNUNET_ERROR_TYPE_DEBUG,
-         "postgres_get_auditor_progress() returned 0 matching rows\n");
-    PQclear (result);
-    return GNUNET_NO;
-  }
-  GNUNET_assert (1 == nrows);
-
-  if (GNUNET_OK !=
-      GNUNET_PQ_extract_result (result,
-                                rs,
-                                0))
-  {
-    GNUNET_break (0);
-    PQclear (result);
-    return GNUNET_SYSERR;
-  }
-  PQclear (result);
-  return GNUNET_OK;
+  return GNUNET_PQ_eval_prepared_singleton_select (session->conn,
+                                                  "auditor_progress_select",
+                                                  params,
+                                                  rs);
 }
 
 
@@ -1485,9 +1306,9 @@ postgres_get_auditor_progress (void *cls,
  * @param withdraw_fee_balance amount the exchange gained in withdraw fees
  *                             due to withdrawals from this reserve
  * @param expiration_date expiration date of the reserve
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+ * @return transaction status code
  */
-static int
+static enum GNUNET_DB_QueryStatus
 postgres_insert_reserve_info (void *cls,
                               struct TALER_AUDITORDB_Session *session,
                               const struct TALER_ReservePublicKeyP 
*reserve_pub,
@@ -1496,8 +1317,6 @@ postgres_insert_reserve_info (void *cls,
                               const struct TALER_Amount *withdraw_fee_balance,
                               struct GNUNET_TIME_Absolute expiration_date)
 {
-  PGresult *result;
-  int ret;
   struct GNUNET_PQ_QueryParam params[] = {
     GNUNET_PQ_query_param_auto_from_type (reserve_pub),
     GNUNET_PQ_query_param_auto_from_type (master_pub),
@@ -1511,20 +1330,9 @@ postgres_insert_reserve_info (void *cls,
                  TALER_amount_cmp_currency (reserve_balance,
                                             withdraw_fee_balance));
 
-  result = GNUNET_PQ_exec_prepared (session->conn,
-                                   "auditor_reserves_insert",
-                                   params);
-  if (PGRES_COMMAND_OK != PQresultStatus (result))
-  {
-    ret = GNUNET_SYSERR;
-    BREAK_DB_ERR (result);
-  }
-  else
-  {
-    ret = GNUNET_OK;
-  }
-  PQclear (result);
-  return ret;
+  return GNUNET_PQ_eval_prepared_non_select (session->conn,
+                                            "auditor_reserves_insert",
+                                            params);
 }
 
 
@@ -1540,9 +1348,9 @@ postgres_insert_reserve_info (void *cls,
  * @param withdraw_fee_balance amount the exchange gained in withdraw fees
  *                             due to withdrawals from this reserve
  * @param expiration_date expiration date of the reserve
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+ * @return transaction status code
  */
-static int
+static enum GNUNET_DB_QueryStatus
 postgres_update_reserve_info (void *cls,
                               struct TALER_AUDITORDB_Session *session,
                               const struct TALER_ReservePublicKeyP 
*reserve_pub,
@@ -1551,8 +1359,6 @@ postgres_update_reserve_info (void *cls,
                               const struct TALER_Amount *withdraw_fee_balance,
                               struct GNUNET_TIME_Absolute expiration_date)
 {
-  PGresult *result;
-  int ret;
   struct GNUNET_PQ_QueryParam params[] = {
     TALER_PQ_query_param_amount (reserve_balance),
     TALER_PQ_query_param_amount (withdraw_fee_balance),
@@ -1566,20 +1372,9 @@ postgres_update_reserve_info (void *cls,
                  TALER_amount_cmp_currency (reserve_balance,
                                             withdraw_fee_balance));
 
-  result = GNUNET_PQ_exec_prepared (session->conn,
-                                   "auditor_reserves_update",
-                                   params);
-  if (PGRES_COMMAND_OK != PQresultStatus (result))
-  {
-    ret = GNUNET_SYSERR;
-    BREAK_DB_ERR (result);
-  }
-  else
-  {
-    ret = GNUNET_OK;
-  }
-  PQclear (result);
-  return ret;
+  return GNUNET_PQ_eval_prepared_non_select (session->conn,
+                                            "auditor_reserves_update",
+                                            params);
 }
 
 
@@ -1590,10 +1385,9 @@ postgres_update_reserve_info (void *cls,
  * @param session connection to use
  * @param reserve_pub public key of the reserve
  * @param master_pub master public key of the exchange
- * @return #GNUNET_OK on success; #GNUNET_NO if there is no known
- *         record about this reserve; #GNUNET_SYSERR on failure
+ * @return transaction status code
  */
-static int
+static enum GNUNET_DB_QueryStatus
 postgres_del_reserve_info (void *cls,
                            struct TALER_AUDITORDB_Session *session,
                            const struct TALER_ReservePublicKeyP *reserve_pub,
@@ -1604,23 +1398,10 @@ postgres_del_reserve_info (void *cls,
     GNUNET_PQ_query_param_auto_from_type (master_pub),
     GNUNET_PQ_query_param_end
   };
-  PGresult *result;
-  int ret;
 
-  result = GNUNET_PQ_exec_prepared (session->conn,
-                                    "auditor_reserves_delete",
-                                    params);
-  ret = PQresultStatus (result);
-  if (PGRES_COMMAND_OK != ret)
-  {
-    BREAK_DB_ERR (result);
-    PQclear (result);
-    return GNUNET_SYSERR;
-  }
-  if (0 == strcmp ("0",
-                   PQcmdTuples (result)))
-    return GNUNET_NO;
-  return GNUNET_OK;
+  return GNUNET_PQ_eval_prepared_non_select (session->conn,
+                                            "auditor_reserves_delete",
+                                            params);
 }
 
 
@@ -1636,10 +1417,9 @@ postgres_del_reserve_info (void *cls,
  * @param[out] withdraw_fee_balance amount the exchange gained in withdraw fees
  *                             due to withdrawals from this reserve
  * @param[out] expiration_date expiration date of the reserve
- * @return #GNUNET_OK on success; #GNUNET_NO if there is no known
- *         record about this reserve; #GNUNET_SYSERR on failure
+ * @return transaction status code
  */
-static int
+static enum GNUNET_DB_QueryStatus
 postgres_get_reserve_info (void *cls,
                            struct TALER_AUDITORDB_Session *session,
                            const struct TALER_ReservePublicKeyP *reserve_pub,
@@ -1651,31 +1431,10 @@ postgres_get_reserve_info (void *cls,
 {
   struct GNUNET_PQ_QueryParam params[] = {
     GNUNET_PQ_query_param_auto_from_type (reserve_pub),
+
     GNUNET_PQ_query_param_auto_from_type (master_pub),
     GNUNET_PQ_query_param_end
   };
-  PGresult *result;
-  result = GNUNET_PQ_exec_prepared (session->conn,
-                                    "auditor_reserves_select",
-                                    params);
-  if (PGRES_TUPLES_OK !=
-      PQresultStatus (result))
-  {
-    BREAK_DB_ERR (result);
-    PQclear (result);
-    return GNUNET_SYSERR;
-  }
-
-  int nrows = PQntuples (result);
-  if (0 == nrows)
-  {
-    LOG (GNUNET_ERROR_TYPE_DEBUG,
-         "postgres_get_reserve_info() returned 0 matching rows\n");
-    PQclear (result);
-    return GNUNET_NO;
-  }
-  GNUNET_assert (1 == nrows);
-
   struct GNUNET_PQ_ResultSpec rs[] = {
     TALER_PQ_result_spec_amount ("reserve_balance", reserve_balance),
     TALER_PQ_result_spec_amount ("withdraw_fee_balance", withdraw_fee_balance),
@@ -1683,15 +1442,11 @@ postgres_get_reserve_info (void *cls,
     GNUNET_PQ_result_spec_uint64 ("auditor_reserves_rowid", rowid),
     GNUNET_PQ_result_spec_end
   };
-  if (GNUNET_OK !=
-      GNUNET_PQ_extract_result (result, rs, 0))
-  {
-    GNUNET_break (0);
-    PQclear (result);
-    return GNUNET_SYSERR;
-  }
-  PQclear (result);
-  return GNUNET_OK;
+
+  return GNUNET_PQ_eval_prepared_singleton_select (session->conn,
+                                                  "auditor_reserves_select",
+                                                  params,
+                                                  rs);
 }
 
 
@@ -1705,17 +1460,15 @@ postgres_get_reserve_info (void *cls,
  * @param reserve_balance amount stored in the reserve
  * @param withdraw_fee_balance amount the exchange gained in withdraw fees
  *                             due to withdrawals from this reserve
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+ * @return transaction status code
  */
-static int
+static enum GNUNET_DB_QueryStatus
 postgres_insert_reserve_summary (void *cls,
                                  struct TALER_AUDITORDB_Session *session,
                                  const struct TALER_MasterPublicKeyP 
*master_pub,
                                  const struct TALER_Amount *reserve_balance,
                                  const struct TALER_Amount 
*withdraw_fee_balance)
 {
-  PGresult *result;
-  int ret;
   struct GNUNET_PQ_QueryParam params[] = {
     GNUNET_PQ_query_param_auto_from_type (master_pub),
     TALER_PQ_query_param_amount (reserve_balance),
@@ -1727,20 +1480,9 @@ postgres_insert_reserve_summary (void *cls,
                  TALER_amount_cmp_currency (reserve_balance,
                                             withdraw_fee_balance));
 
-  result = GNUNET_PQ_exec_prepared (session->conn,
-                                   "auditor_reserve_balance_insert",
-                                   params);
-  if (PGRES_COMMAND_OK != PQresultStatus (result))
-  {
-    ret = GNUNET_SYSERR;
-    BREAK_DB_ERR (result);
-  }
-  else
-  {
-    ret = GNUNET_OK;
-  }
-  PQclear (result);
-  return ret;
+  return GNUNET_PQ_eval_prepared_non_select (session->conn,
+                                            "auditor_reserve_balance_insert",
+                                            params);
 }
 
 
@@ -1754,17 +1496,15 @@ postgres_insert_reserve_summary (void *cls,
  * @param reserve_balance amount stored in the reserve
  * @param withdraw_fee_balance amount the exchange gained in withdraw fees
  *                             due to withdrawals from this reserve
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+ * @return transaction status code
  */
-static int
+static enum GNUNET_DB_QueryStatus
 postgres_update_reserve_summary (void *cls,
                                  struct TALER_AUDITORDB_Session *session,
                                  const struct TALER_MasterPublicKeyP 
*master_pub,
                                  const struct TALER_Amount *reserve_balance,
                                  const struct TALER_Amount 
*withdraw_fee_balance)
 {
-  PGresult *result;
-  int ret;
   struct GNUNET_PQ_QueryParam params[] = {
     TALER_PQ_query_param_amount (reserve_balance),
     TALER_PQ_query_param_amount (withdraw_fee_balance),
@@ -1772,20 +1512,9 @@ postgres_update_reserve_summary (void *cls,
     GNUNET_PQ_query_param_end
   };
 
-  result = GNUNET_PQ_exec_prepared (session->conn,
-                                   "auditor_reserve_balance_update",
-                                   params);
-  if (PGRES_COMMAND_OK != PQresultStatus (result))
-  {
-    ret = GNUNET_SYSERR;
-    BREAK_DB_ERR (result);
-  }
-  else
-  {
-    ret = GNUNET_OK;
-  }
-  PQclear (result);
-  return ret;
+  return GNUNET_PQ_eval_prepared_non_select (session->conn,
+                                            "auditor_reserve_balance_update",
+                                            params);
 }
 
 
@@ -1798,10 +1527,9 @@ postgres_update_reserve_summary (void *cls,
  * @param[out] reserve_balance amount stored in the reserve
  * @param[out] withdraw_fee_balance amount the exchange gained in withdraw fees
  *                             due to withdrawals from this reserve
- * @return #GNUNET_OK on success; #GNUNET_NO if there is no known
- *         record about this exchange; #GNUNET_SYSERR on failure
+ * @return transaction status code
  */
-static int
+static enum GNUNET_DB_QueryStatus
 postgres_get_reserve_summary (void *cls,
                               struct TALER_AUDITORDB_Session *session,
                               const struct TALER_MasterPublicKeyP *master_pub,
@@ -1812,47 +1540,18 @@ postgres_get_reserve_summary (void *cls,
     GNUNET_PQ_query_param_auto_from_type (master_pub),
     GNUNET_PQ_query_param_end
   };
-  PGresult *result;
-  result = GNUNET_PQ_exec_prepared (session->conn,
-                                    "auditor_reserve_balance_select",
-                                    params);
-  if (PGRES_TUPLES_OK !=
-      PQresultStatus (result))
-  {
-    BREAK_DB_ERR (result);
-    PQclear (result);
-    return GNUNET_SYSERR;
-  }
-
-  int nrows = PQntuples (result);
-  if (0 == nrows)
-  {
-    LOG (GNUNET_ERROR_TYPE_DEBUG,
-         "postgres_get_reserve_summary() returned 0 matching rows\n");
-    PQclear (result);
-    return GNUNET_NO;
-  }
-  GNUNET_assert (1 == nrows);
-
   struct GNUNET_PQ_ResultSpec rs[] = {
     TALER_PQ_result_spec_amount ("reserve_balance", reserve_balance),
     TALER_PQ_result_spec_amount ("withdraw_fee_balance", withdraw_fee_balance),
 
     GNUNET_PQ_result_spec_end
   };
-  if (GNUNET_OK !=
-      GNUNET_PQ_extract_result (result, rs, 0))
-  {
-    GNUNET_break (0);
-    PQclear (result);
-    return GNUNET_SYSERR;
-  }
-  PQclear (result);
-  return GNUNET_OK;
-}
-
-
 
+  return GNUNET_PQ_eval_prepared_singleton_select (session->conn,
+                                                  
"auditor_reserve_balance_select",
+                                                  params,
+                                                  rs);
+}
 
 
 /**
@@ -1863,36 +1562,23 @@ postgres_get_reserve_summary (void *cls,
  * @param session connection to use
  * @param master_pub master public key of the exchange
  * @param wire_fee_balance amount the exchange gained in wire fees
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+ * @return transaction status code
  */
-static int
+static enum GNUNET_DB_QueryStatus
 postgres_insert_wire_fee_summary (void *cls,
                                   struct TALER_AUDITORDB_Session *session,
                                   const struct TALER_MasterPublicKeyP 
*master_pub,
                                   const struct TALER_Amount *wire_fee_balance)
 {
-  PGresult *result;
-  int ret;
   struct GNUNET_PQ_QueryParam params[] = {
     GNUNET_PQ_query_param_auto_from_type (master_pub),
-     TALER_PQ_query_param_amount (wire_fee_balance),
+    TALER_PQ_query_param_amount (wire_fee_balance),
     GNUNET_PQ_query_param_end
   };
 
-  result = GNUNET_PQ_exec_prepared (session->conn,
-                                    "auditor_wire_fee_balance_insert",
-                                    params);
-  if (PGRES_COMMAND_OK != PQresultStatus (result))
-  {
-    ret = GNUNET_SYSERR;
-    BREAK_DB_ERR (result);
-  }
-  else
-  {
-    ret = GNUNET_OK;
-  }
-  PQclear (result);
-  return ret;
+  return GNUNET_PQ_eval_prepared_non_select (session->conn,
+                                            "auditor_wire_fee_balance_insert",
+                                            params);
 }
 
 
@@ -1904,36 +1590,23 @@ postgres_insert_wire_fee_summary (void *cls,
  * @param session connection to use
  * @param master_pub master public key of the exchange
  * @param wire_fee_balance amount the exchange gained in wire fees
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+ * @return transaction status code
  */
-static int
+static enum GNUNET_DB_QueryStatus
 postgres_update_wire_fee_summary (void *cls,
                                   struct TALER_AUDITORDB_Session *session,
                                   const struct TALER_MasterPublicKeyP 
*master_pub,
                                   const struct TALER_Amount *wire_fee_balance)
 {
-  PGresult *result;
-  int ret;
   struct GNUNET_PQ_QueryParam params[] = {
     TALER_PQ_query_param_amount (wire_fee_balance),
     GNUNET_PQ_query_param_auto_from_type (master_pub),
     GNUNET_PQ_query_param_end
   };
 
-  result = GNUNET_PQ_exec_prepared (session->conn,
-                                   "auditor_wire_fee_balance_update",
-                                   params);
-  if (PGRES_COMMAND_OK != PQresultStatus (result))
-  {
-    ret = GNUNET_SYSERR;
-    BREAK_DB_ERR (result);
-  }
-  else
-  {
-    ret = GNUNET_OK;
-  }
-  PQclear (result);
-  return ret;
+  return GNUNET_PQ_eval_prepared_non_select (session->conn,
+                                            "auditor_wire_fee_balance_update",
+                                            params);
 }
 
 
@@ -1944,10 +1617,9 @@ postgres_update_wire_fee_summary (void *cls,
  * @param session connection to use
  * @param master_pub master public key of the exchange
  * @param[out] wire_fee_balance set amount the exchange gained in wire fees
- * @return #GNUNET_OK on success; #GNUNET_NO if there is no known
- *         record about this exchange; #GNUNET_SYSERR on failure
+ * @return transaction status code
  */
-static int
+static enum GNUNET_DB_QueryStatus
 postgres_get_wire_fee_summary (void *cls,
                                struct TALER_AUDITORDB_Session *session,
                                const struct TALER_MasterPublicKeyP *master_pub,
@@ -1957,42 +1629,16 @@ postgres_get_wire_fee_summary (void *cls,
     GNUNET_PQ_query_param_auto_from_type (master_pub),
     GNUNET_PQ_query_param_end
   };
-  PGresult *result;
-  result = GNUNET_PQ_exec_prepared (session->conn,
-                                    "auditor_wire_fee_balance_select",
-                                    params);
-  if (PGRES_TUPLES_OK !=
-      PQresultStatus (result))
-  {
-    BREAK_DB_ERR (result);
-    PQclear (result);
-    return GNUNET_SYSERR;
-  }
-
-  int nrows = PQntuples (result);
-  if (0 == nrows)
-  {
-    LOG (GNUNET_ERROR_TYPE_DEBUG,
-         "postgres_get_wire_fee_summary() returned 0 matching rows\n");
-    PQclear (result);
-    return GNUNET_NO;
-  }
-  GNUNET_assert (1 == nrows);
-
   struct GNUNET_PQ_ResultSpec rs[] = {
     TALER_PQ_result_spec_amount ("wire_fee_balance", wire_fee_balance),
 
     GNUNET_PQ_result_spec_end
   };
-  if (GNUNET_OK !=
-      GNUNET_PQ_extract_result (result, rs, 0))
-  {
-    GNUNET_break (0);
-    PQclear (result);
-    return GNUNET_SYSERR;
-  }
-  PQclear (result);
-  return GNUNET_OK;
+
+  return GNUNET_PQ_eval_prepared_singleton_select (session->conn,
+                                                  
"auditor_wire_fee_balance_select",
+                                                  params,
+                                                  rs);
 }
 
 
@@ -2005,17 +1651,15 @@ postgres_get_wire_fee_summary (void *cls,
  * @param denom_pub_hash hash of the denomination public key
  * @param denom_balance value of coins outstanding with this denomination key
  * @param denom_risk value of coins issued with this denomination key
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+ * @return transaction status code
  */
-static int
+static enum GNUNET_DB_QueryStatus
 postgres_insert_denomination_balance (void *cls,
                                       struct TALER_AUDITORDB_Session *session,
                                       const struct GNUNET_HashCode 
*denom_pub_hash,
                                       const struct TALER_Amount *denom_balance,
                                       const struct TALER_Amount *denom_risk)
 {
-  PGresult *result;
-  int ret;
   struct GNUNET_PQ_QueryParam params[] = {
     GNUNET_PQ_query_param_auto_from_type (denom_pub_hash),
     TALER_PQ_query_param_amount (denom_balance),
@@ -2023,20 +1667,9 @@ postgres_insert_denomination_balance (void *cls,
     GNUNET_PQ_query_param_end
   };
 
-  result = GNUNET_PQ_exec_prepared (session->conn,
-                                   "denomination_pending_insert",
-                                   params);
-  if (PGRES_COMMAND_OK != PQresultStatus (result))
-  {
-    ret = GNUNET_SYSERR;
-    BREAK_DB_ERR (result);
-  }
-  else
-  {
-    ret = GNUNET_OK;
-  }
-  PQclear (result);
-  return ret;
+  return GNUNET_PQ_eval_prepared_non_select (session->conn,
+                                            "denomination_pending_insert",
+                                            params);
 }
 
 
@@ -2049,17 +1682,15 @@ postgres_insert_denomination_balance (void *cls,
  * @param denom_pub_hash hash of the denomination public key
  * @param denom_balance value of coins outstanding with this denomination key
  * @param denom_risk value of coins issued with this denomination key
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+ * @return transaction status code
  */
-static int
+static enum GNUNET_DB_QueryStatus
 postgres_update_denomination_balance (void *cls,
                                       struct TALER_AUDITORDB_Session *session,
                                       const struct GNUNET_HashCode 
*denom_pub_hash,
                                       const struct TALER_Amount *denom_balance,
                                       const struct TALER_Amount *denom_risk)
 {
-  PGresult *result;
-  int ret;
   struct GNUNET_PQ_QueryParam params[] = {
     TALER_PQ_query_param_amount (denom_balance),
     TALER_PQ_query_param_amount (denom_risk),
@@ -2067,20 +1698,9 @@ postgres_update_denomination_balance (void *cls,
     GNUNET_PQ_query_param_end
   };
 
-  result = GNUNET_PQ_exec_prepared (session->conn,
-                                   "denomination_pending_update",
-                                   params);
-  if (PGRES_COMMAND_OK != PQresultStatus (result))
-  {
-    ret = GNUNET_SYSERR;
-    BREAK_DB_ERR (result);
-  }
-  else
-  {
-    ret = GNUNET_OK;
-  }
-  PQclear (result);
-  return ret;
+  return GNUNET_PQ_eval_prepared_non_select (session->conn,
+                                            "denomination_pending_update",
+                                            params);
 }
 
 
@@ -2092,9 +1712,9 @@ postgres_update_denomination_balance (void *cls,
  * @param denom_pub_hash hash of the denomination public key
  * @param[out] denom_balance value of coins outstanding with this denomination 
key
  * @param[out] denom_risk value of coins issued with this denomination key
- * @return #GNUNET_OK on success; #GNUNET_NO if no record found, 
#GNUNET_SYSERR on failure
+ * @return transaction status code
  */
-static int
+static enum GNUNET_DB_QueryStatus
 postgres_get_denomination_balance (void *cls,
                                    struct TALER_AUDITORDB_Session *session,
                                    const struct GNUNET_HashCode 
*denom_pub_hash,
@@ -2105,43 +1725,16 @@ postgres_get_denomination_balance (void *cls,
     GNUNET_PQ_query_param_auto_from_type (denom_pub_hash),
     GNUNET_PQ_query_param_end
   };
-  PGresult *result;
-
-  result = GNUNET_PQ_exec_prepared (session->conn,
-                                    "denomination_pending_select",
-                                    params);
-  if (PGRES_TUPLES_OK !=
-      PQresultStatus (result))
-  {
-    BREAK_DB_ERR (result);
-    PQclear (result);
-    return GNUNET_SYSERR;
-  }
-
-  int nrows = PQntuples (result);
-  if (0 == nrows)
-  {
-    LOG (GNUNET_ERROR_TYPE_DEBUG,
-         "postgres_get_denomination_balance() returned 0 matching rows\n");
-    PQclear (result);
-    return GNUNET_NO;
-  }
-  GNUNET_assert (1 == nrows);
-
   struct GNUNET_PQ_ResultSpec rs[] = {
     TALER_PQ_result_spec_amount ("denom_balance", denom_balance),
     TALER_PQ_result_spec_amount ("denom_risk", denom_risk),
     GNUNET_PQ_result_spec_end
   };
-  if (GNUNET_OK !=
-      GNUNET_PQ_extract_result (result, rs, 0))
-  {
-    GNUNET_break (0);
-    PQclear (result);
-    return GNUNET_SYSERR;
-  }
-  PQclear (result);
-  return GNUNET_OK;
+  
+  return GNUNET_PQ_eval_prepared_singleton_select (session->conn,
+                                                  
"denomination_pending_select",
+                                                  params,
+                                                  rs);
 }
 
 
@@ -2157,9 +1750,9 @@ postgres_get_denomination_balance (void *cls,
  * @param melt_fee_balance total melt fees collected for this DK
  * @param refund_fee_balance total refund fees collected for this DK
  * @param risk maximum risk exposure of the exchange
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+ * @return transaction status code
  */
-static int
+static enum GNUNET_DB_QueryStatus
 postgres_insert_balance_summary (void *cls,
                                  struct TALER_AUDITORDB_Session *session,
                                  const struct TALER_MasterPublicKeyP 
*master_pub,
@@ -2169,8 +1762,6 @@ postgres_insert_balance_summary (void *cls,
                                  const struct TALER_Amount *refund_fee_balance,
                                  const struct TALER_Amount *risk)
 {
-  PGresult *result;
-  int ret;
   struct GNUNET_PQ_QueryParam params[] = {
     GNUNET_PQ_query_param_auto_from_type (master_pub),
     TALER_PQ_query_param_amount (denom_balance),
@@ -2193,20 +1784,9 @@ postgres_insert_balance_summary (void *cls,
                  TALER_amount_cmp_currency (denom_balance,
                                             refund_fee_balance));
 
-  result = GNUNET_PQ_exec_prepared (session->conn,
-                                   "balance_summary_insert",
-                                   params);
-  if (PGRES_COMMAND_OK != PQresultStatus (result))
-  {
-    ret = GNUNET_SYSERR;
-    BREAK_DB_ERR (result);
-  }
-  else
-  {
-    ret = GNUNET_OK;
-  }
-  PQclear (result);
-  return ret;
+  return GNUNET_PQ_eval_prepared_non_select (session->conn,
+                                            "balance_summary_insert",
+                                            params);
 }
 
 
@@ -2222,9 +1802,9 @@ postgres_insert_balance_summary (void *cls,
  * @param melt_fee_balance total melt fees collected for this DK
  * @param refund_fee_balance total refund fees collected for this DK
  * @param risk maximum risk exposure of the exchange
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+ * @return transaction status code
  */
-static int
+static enum GNUNET_DB_QueryStatus
 postgres_update_balance_summary (void *cls,
                                  struct TALER_AUDITORDB_Session *session,
                                  const struct TALER_MasterPublicKeyP 
*master_pub,
@@ -2234,8 +1814,6 @@ postgres_update_balance_summary (void *cls,
                                  const struct TALER_Amount *refund_fee_balance,
                                  const struct TALER_Amount *risk)
 {
-  PGresult *result;
-  int ret;
   struct GNUNET_PQ_QueryParam params[] = {
     TALER_PQ_query_param_amount (denom_balance),
     TALER_PQ_query_param_amount (deposit_fee_balance),
@@ -2246,20 +1824,9 @@ postgres_update_balance_summary (void *cls,
     GNUNET_PQ_query_param_end
   };
 
-  result = GNUNET_PQ_exec_prepared (session->conn,
-                                   "balance_summary_update",
-                                   params);
-  if (PGRES_COMMAND_OK != PQresultStatus (result))
-  {
-    ret = GNUNET_SYSERR;
-    BREAK_DB_ERR (result);
-  }
-  else
-  {
-    ret = GNUNET_OK;
-  }
-  PQclear (result);
-  return ret;
+  return GNUNET_PQ_eval_prepared_non_select (session->conn,
+                                            "balance_summary_update",
+                                            params);
 }
 
 
@@ -2274,10 +1841,9 @@ postgres_update_balance_summary (void *cls,
  * @param[out] melt_fee_balance total melt fees collected for this DK
  * @param[out] refund_fee_balance total refund fees collected for this DK
  * @param[out] risk maximum risk exposure of the exchange
- * @return #GNUNET_OK on success; #GNUNET_NO if there is no entry
- *           for this @a master_pub; #GNUNET_SYSERR on failure
+ * @return transaction status code
  */
-static int
+static enum GNUNET_DB_QueryStatus
 postgres_get_balance_summary (void *cls,
                               struct TALER_AUDITORDB_Session *session,
                               const struct TALER_MasterPublicKeyP *master_pub,
@@ -2291,29 +1857,6 @@ postgres_get_balance_summary (void *cls,
     GNUNET_PQ_query_param_auto_from_type (master_pub),
     GNUNET_PQ_query_param_end
   };
-  PGresult *result;
-
-  result = GNUNET_PQ_exec_prepared (session->conn,
-                                    "balance_summary_select",
-                                    params);
-  if (PGRES_TUPLES_OK !=
-      PQresultStatus (result))
-  {
-    BREAK_DB_ERR (result);
-    PQclear (result);
-    return GNUNET_SYSERR;
-  }
-
-  int nrows = PQntuples (result);
-  if (0 == nrows)
-  {
-    LOG (GNUNET_ERROR_TYPE_DEBUG,
-         "postgres_get_balance_summary() returned 0 matching rows\n");
-    PQclear (result);
-    return GNUNET_NO;
-  }
-  GNUNET_assert (1 == nrows);
-
   struct GNUNET_PQ_ResultSpec rs[] = {
     TALER_PQ_result_spec_amount ("denom_balance", denom_balance),
     TALER_PQ_result_spec_amount ("deposit_fee_balance", deposit_fee_balance),
@@ -2322,15 +1865,11 @@ postgres_get_balance_summary (void *cls,
     TALER_PQ_result_spec_amount ("risk", risk),
     GNUNET_PQ_result_spec_end
   };
-  if (GNUNET_OK !=
-      GNUNET_PQ_extract_result (result, rs, 0))
-  {
-    GNUNET_break (0);
-    PQclear (result);
-    return GNUNET_SYSERR;
-  }
-  PQclear (result);
-  return GNUNET_OK;
+
+  return GNUNET_PQ_eval_prepared_singleton_select (session->conn,
+                                                  "balance_summary_select",
+                                                  params,
+                                                  rs);
 }
 
 
@@ -2346,9 +1885,9 @@ postgres_get_balance_summary (void *cls,
  * @param revenue_balance what was the total profit made from
  *                        deposit fees, melting fees, refresh fees
  *                        and coins that were never returned?
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+ * @return transaction status code
  */
-static int
+static enum GNUNET_DB_QueryStatus
 postgres_insert_historic_denom_revenue (void *cls,
                                         struct TALER_AUDITORDB_Session 
*session,
                                         const struct TALER_MasterPublicKeyP 
*master_pub,
@@ -2356,8 +1895,6 @@ postgres_insert_historic_denom_revenue (void *cls,
                                         struct GNUNET_TIME_Absolute 
revenue_timestamp,
                                         const struct TALER_Amount 
*revenue_balance)
 {
-  PGresult *result;
-  int ret;
   struct GNUNET_PQ_QueryParam params[] = {
     GNUNET_PQ_query_param_auto_from_type (master_pub),
     GNUNET_PQ_query_param_auto_from_type (denom_pub_hash),
@@ -2366,68 +1903,51 @@ postgres_insert_historic_denom_revenue (void *cls,
     GNUNET_PQ_query_param_end
   };
 
-  result = GNUNET_PQ_exec_prepared (session->conn,
-                                   "historic_denomination_revenue_insert",
-                                   params);
-  if (PGRES_COMMAND_OK != PQresultStatus (result))
-  {
-    ret = GNUNET_SYSERR;
-    BREAK_DB_ERR (result);
-  }
-  else
-  {
-    ret = GNUNET_OK;
-  }
-  PQclear (result);
-  return ret;
+  return GNUNET_PQ_eval_prepared_non_select (session->conn,
+                                            
"historic_denomination_revenue_insert",
+                                            params);
 }
 
 
 /**
- * Obtain all of the historic denomination key revenue
- * of the given @a master_pub.
- *
- * @param cls the @e cls of this struct with the plugin-specific state
- * @param session connection to use
- * @param master_pub master key of the exchange
- * @param cb function to call with the results
- * @param cb_cls closure for @a cb
- * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
+ * Closure for #historic_denom_revenue_cb().
  */
-static int
-postgres_select_historic_denom_revenue (void *cls,
-                                        struct TALER_AUDITORDB_Session 
*session,
-                                        const struct TALER_MasterPublicKeyP 
*master_pub,
-                                        
TALER_AUDITORDB_HistoricDenominationRevenueDataCallback cb,
-                                        void *cb_cls)
+struct HistoricDenomRevenueContext
 {
-  struct GNUNET_PQ_QueryParam params[] = {
-    GNUNET_PQ_query_param_auto_from_type (master_pub),
-    GNUNET_PQ_query_param_end
-  };
-  PGresult *result;
+  /**
+   * Function to call for each result.
+   */
+  TALER_AUDITORDB_HistoricDenominationRevenueDataCallback cb;
 
-  result = GNUNET_PQ_exec_prepared (session->conn,
-                                    "historic_denomination_revenue_select",
-                                    params);
-  if (PGRES_TUPLES_OK !=
-      PQresultStatus (result))
-  {
-    BREAK_DB_ERR (result);
-    PQclear (result);
-    return GNUNET_SYSERR;
-  }
+  /**
+   * Closure for @e cb.
+   */
+  void *cb_cls;
 
-  int ret = GNUNET_OK;
-  int nrows = PQntuples (result);
-  if (0 == nrows)
-  {
-    LOG (GNUNET_ERROR_TYPE_DEBUG,
-         "postgres_select_historic_denom_revenue() returned 0 matching 
rows\n");
-    PQclear (result);
-    return GNUNET_NO;
-  }
-  for (int i = 0; i < nrows; i++)
+  /**
+   * Number of results processed.
+   */
+  enum GNUNET_DB_QueryStatus qs;
+};
+
+
+/**
+ * Helper function for #postgres_select_historic_denom_revenue().
+ * To be called with the results of a SELECT statement
+ * that has returned @a num_results results.
+ *
+ * @param cls closure of type `struct HistoricRevenueContext *`
+ * @param result the postgres result
+ * @param num_result the number of results in @a result
+ */
+static void
+historic_denom_revenue_cb (void *cls,
+                          PGresult *result,
+                          unsigned int num_results)
+{
+  struct HistoricDenomRevenueContext *hrc = cls;
+
+  for (unsigned int i = 0; i < num_results; i++)
   {
     struct GNUNET_HashCode denom_pub_hash;
     struct GNUNET_TIME_Absolute revenue_timestamp;
@@ -2440,28 +1960,62 @@ postgres_select_historic_denom_revenue (void *cls,
     };
 
     if (GNUNET_OK !=
-        GNUNET_PQ_extract_result (result, rs, 0))
+        GNUNET_PQ_extract_result (result,
+                                 rs,
+                                 i))
     {
       GNUNET_break (0);
-      PQclear (result);
-      return GNUNET_SYSERR;
+      hrc->qs = GNUNET_DB_STATUS_HARD_ERROR;
+      return;
     }
 
-    ret = cb (cb_cls,
-              &denom_pub_hash,
-              revenue_timestamp,
-              &revenue_balance);
-    switch (ret)
-    {
-    case GNUNET_OK:
+    hrc->qs = i + 1;
+    if (GNUNET_OK !=
+       hrc->cb (hrc->cb_cls,
+                &denom_pub_hash,
+                revenue_timestamp,
+                &revenue_balance))
       break;
-
-    default:
-      i = nrows;
-    }
   }
-  PQclear (result);
-  return ret;
+}
+
+
+/**
+ * Obtain all of the historic denomination key revenue
+ * of the given @a master_pub.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param session connection to use
+ * @param master_pub master key of the exchange
+ * @param cb function to call with the results
+ * @param cb_cls closure for @a cb
+ * @return transaction status code
+ */
+static enum GNUNET_DB_QueryStatus
+postgres_select_historic_denom_revenue (void *cls,
+                                        struct TALER_AUDITORDB_Session 
*session,
+                                        const struct TALER_MasterPublicKeyP 
*master_pub,
+                                        
TALER_AUDITORDB_HistoricDenominationRevenueDataCallback cb,
+                                        void *cb_cls)
+{
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_auto_from_type (master_pub),
+    GNUNET_PQ_query_param_end
+  };
+  struct HistoricDenomRevenueContext hrc = {
+    .cb = cb,
+    .cb_cls = cb_cls
+  };
+  enum GNUNET_DB_QueryStatus qs;
+  
+  qs = GNUNET_PQ_eval_prepared_multi_select (session->conn,
+                                            
"historic_denomination_revenue_select",
+                                            params,
+                                            &historic_denom_revenue_cb,
+                                            &hrc);
+  if (qs <= 0)
+    return qs;
+  return hrc.qs;
 }
 
 
@@ -2478,9 +2032,9 @@ postgres_select_historic_denom_revenue (void *cls,
  * @param denom_pub_hash hash of the denomination key
  * @param loss_timestamp when did this profit get realized
  * @param loss_balance what was the total loss
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+ * @return transaction status code
  */
-static int
+static enum GNUNET_DB_QueryStatus
 postgres_insert_historic_losses (void *cls,
                                  struct TALER_AUDITORDB_Session *session,
                                  const struct TALER_MasterPublicKeyP 
*master_pub,
@@ -2488,8 +2042,6 @@ postgres_insert_historic_losses (void *cls,
                                  struct GNUNET_TIME_Absolute loss_timestamp,
                                  const struct TALER_Amount *loss_balance)
 {
-  PGresult *result;
-  int ret;
   struct GNUNET_PQ_QueryParam params[] = {
     GNUNET_PQ_query_param_auto_from_type (master_pub),
     GNUNET_PQ_query_param_auto_from_type (denom_pub_hash),
@@ -2498,20 +2050,79 @@ postgres_insert_historic_losses (void *cls,
     GNUNET_PQ_query_param_end
   };
 
-  result = GNUNET_PQ_exec_prepared (session->conn,
-                                   "historic_losses_insert",
-                                   params);
-  if (PGRES_COMMAND_OK != PQresultStatus (result))
-  {
-    ret = GNUNET_SYSERR;
-    BREAK_DB_ERR (result);
-  }
-  else
+  return GNUNET_PQ_eval_prepared_non_select (session->conn,
+                                            "historic_losses_insert",
+                                            params);
+}
+
+
+/**
+ * Closure for #losses_cb.
+ */
+struct LossContext
+{
+  /**
+   * Function to call for each result.
+   */
+  TALER_AUDITORDB_HistoricLossesDataCallback cb;
+
+  /** 
+   * Closure for @e cb.
+   */
+  void *cb_cls;
+
+  /**
+   * Status code to return.
+   */
+  enum GNUNET_DB_QueryStatus qs;
+};
+
+
+/**
+ * Helper function for #postgres_select_historic_denom_revenue().
+ * To be called with the results of a SELECT statement
+ * that has returned @a num_results results.
+ *
+ * @param cls closure of type `struct HistoricRevenueContext *`
+ * @param result the postgres result
+ * @param num_result the number of results in @a result
+ */
+static void
+losses_cb (void *cls,
+          PGresult *result,
+          unsigned int num_results)
+{
+  struct LossContext *lctx = cls;
+  
+  for (unsigned int i = 0; i < num_results; i++)
   {
-    ret = GNUNET_OK;
+    struct GNUNET_HashCode denom_pub_hash;
+    struct GNUNET_TIME_Absolute loss_timestamp;
+    struct TALER_Amount loss_balance;
+    struct GNUNET_PQ_ResultSpec rs[] = {
+      GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash", &denom_pub_hash),
+      GNUNET_PQ_result_spec_auto_from_type ("loss_timestamp", &loss_timestamp),
+      TALER_PQ_result_spec_amount ("loss_balance", &loss_balance),
+      GNUNET_PQ_result_spec_end
+    };
+    
+    if (GNUNET_OK !=
+        GNUNET_PQ_extract_result (result,
+                                 rs,
+                                 i))
+    {
+      GNUNET_break (0);
+      lctx->qs = GNUNET_DB_STATUS_HARD_ERROR;
+      return;
+    }
+    lctx->qs = i + 1;
+    if (GNUNET_OK !=
+       lctx->cb (lctx->cb_cls,
+                 &denom_pub_hash,
+                 loss_timestamp,
+                 &loss_balance))
+      break;
   }
-  PQclear (result);
-  return ret;
 }
 
 
@@ -2524,9 +2135,9 @@ postgres_insert_historic_losses (void *cls,
  * @param master_pub master key of the exchange
  * @param cb function to call with the results
  * @param cb_cls closure for @a cb
- * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
+ * @return transaction status code
  */
-static int
+static enum GNUNET_DB_QueryStatus
 postgres_select_historic_losses (void *cls,
                                  struct TALER_AUDITORDB_Session *session,
                                  const struct TALER_MasterPublicKeyP 
*master_pub,
@@ -2537,65 +2148,20 @@ postgres_select_historic_losses (void *cls,
     GNUNET_PQ_query_param_auto_from_type (master_pub),
     GNUNET_PQ_query_param_end
   };
-  PGresult *result;
-
-  result = GNUNET_PQ_exec_prepared (session->conn,
-                                    "historic_losses_select",
-                                    params);
-  if (PGRES_TUPLES_OK !=
-      PQresultStatus (result))
-  {
-    BREAK_DB_ERR (result);
-    PQclear (result);
-    return GNUNET_SYSERR;
-  }
-
-  int ret = GNUNET_OK;
-  int nrows = PQntuples (result);
-  if (0 == nrows)
-  {
-    LOG (GNUNET_ERROR_TYPE_DEBUG,
-         "postgres_select_historic_losses() returned 0 matching rows\n");
-    PQclear (result);
-    return GNUNET_NO;
-  }
-  for (int i = 0; i < nrows; i++)
-  {
-    struct GNUNET_HashCode denom_pub_hash;
-    struct GNUNET_TIME_Absolute loss_timestamp;
-    struct TALER_Amount loss_balance;
-
-    struct GNUNET_PQ_ResultSpec rs[] = {
-      GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash", &denom_pub_hash),
-
-      GNUNET_PQ_result_spec_auto_from_type ("loss_timestamp", &loss_timestamp),
-
-      TALER_PQ_result_spec_amount ("loss_balance", &loss_balance),
-
-      GNUNET_PQ_result_spec_end
-    };
-    if (GNUNET_OK !=
-        GNUNET_PQ_extract_result (result, rs, 0))
-    {
-      GNUNET_break (0);
-      PQclear (result);
-      return GNUNET_SYSERR;
-    }
-    ret = cb (cb_cls,
-              &denom_pub_hash,
-              loss_timestamp,
-              &loss_balance);
-    switch (ret)
-    {
-    case GNUNET_OK:
-      break;
-
-    default:
-      i = nrows;
-    }
-  }
-  PQclear (result);
-  return ret;
+  struct LossContext lctx = {
+    .cb = cb,
+    .cb_cls = cb_cls
+  };
+  enum GNUNET_DB_QueryStatus qs;
+
+  qs = GNUNET_PQ_eval_prepared_multi_select (session->conn,
+                                            "historic_losses_select",
+                                            params,
+                                            &losses_cb,
+                                            &lctx);
+  if (qs <= 0)
+    return qs;
+  return lctx.qs;
 }
 
 
@@ -2608,9 +2174,9 @@ postgres_select_historic_losses (void *cls,
  * @param start_time beginning of aggregated time interval
  * @param end_time end of aggregated time interval
  * @param reserve_profits total profits made
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+ * @return transaction status code
  */
-static int
+static enum GNUNET_DB_QueryStatus
 postgres_insert_historic_reserve_revenue (void *cls,
                                           struct TALER_AUDITORDB_Session 
*session,
                                           const struct TALER_MasterPublicKeyP 
*master_pub,
@@ -2618,8 +2184,6 @@ postgres_insert_historic_reserve_revenue (void *cls,
                                           struct GNUNET_TIME_Absolute end_time,
                                           const struct TALER_Amount 
*reserve_profits)
 {
-  PGresult *result;
-  int ret;
   struct GNUNET_PQ_QueryParam params[] = {
     GNUNET_PQ_query_param_auto_from_type (master_pub),
     GNUNET_PQ_query_param_auto_from_type (&start_time),
@@ -2628,100 +2192,117 @@ postgres_insert_historic_reserve_revenue (void *cls,
     GNUNET_PQ_query_param_end
   };
 
-  result = GNUNET_PQ_exec_prepared (session->conn,
-                                   "historic_reserve_summary_insert",
-                                   params);
-  if (PGRES_COMMAND_OK != PQresultStatus (result))
-  {
-    ret = GNUNET_SYSERR;
-    BREAK_DB_ERR (result);
-  }
-  else
-  {
-    ret = GNUNET_OK;
-  }
-  PQclear (result);
-  return ret;
+  return GNUNET_PQ_eval_prepared_non_select (session->conn,
+                                            "historic_reserve_summary_insert",
+                                            params);
 }
 
 
 /**
- * Return information about an exchange's historic revenue from reserves.
- *
- * @param cls the @e cls of this struct with the plugin-specific state
- * @param session connection to use
- * @param master_pub master key of the exchange
- * @param cb function to call with results
- * @param cb_cls closure for @a cb
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+ * Closure for #historic_reserve_revenue_cb().
  */
-static int
-postgres_select_historic_reserve_revenue (void *cls,
-                                          struct TALER_AUDITORDB_Session 
*session,
-                                          const struct TALER_MasterPublicKeyP 
*master_pub,
-                                          
TALER_AUDITORDB_HistoricReserveRevenueDataCallback cb,
-                                          void *cb_cls)
+struct HistoricReserveRevenueContext
 {
-  struct GNUNET_PQ_QueryParam params[] = {
-    GNUNET_PQ_query_param_auto_from_type (master_pub),
-    GNUNET_PQ_query_param_end
-  };
-  PGresult *result;
+  /**
+   * Function to call for each result.
+   */
+  TALER_AUDITORDB_HistoricReserveRevenueDataCallback cb;
 
-  result = GNUNET_PQ_exec_prepared (session->conn,
-                                    "historic_reserve_summary_select",
-                                    params);
-  if (PGRES_TUPLES_OK !=
-      PQresultStatus (result))
-  {
-    BREAK_DB_ERR (result);
-    PQclear (result);
-    return GNUNET_SYSERR;
-  }
+  /**
+   * Closure for @e cb.
+   */
+  void *cb_cls;
 
-  int ret = GNUNET_OK;
-  int nrows = PQntuples (result);
-  if (0 == nrows)
-  {
-    LOG (GNUNET_ERROR_TYPE_DEBUG,
-         "postgres_select_historic_reserve_revenue() returned 0 matching 
rows\n");
-    PQclear (result);
-    return GNUNET_NO;
-  }
-  for (int i = 0; i < nrows; i++)
+  /**
+   * Number of results processed.
+   */
+  enum GNUNET_DB_QueryStatus qs;
+};
+
+
+/**
+ * Helper function for #postgres_select_historic_reserve_revenue().
+ * To be called with the results of a SELECT statement
+ * that has returned @a num_results results.
+ *
+ * @param cls closure of type `struct HistoricRevenueContext *`
+ * @param result the postgres result
+ * @param num_result the number of results in @a result
+ */
+static void
+historic_reserve_revenue_cb (void *cls,
+                            PGresult *result,
+                            unsigned int num_results)
+{
+  struct HistoricReserveRevenueContext *hrc = cls;
+
+  for (unsigned int i = 0; i < num_results; i++)
   {
     struct GNUNET_TIME_Absolute start_date;
     struct GNUNET_TIME_Absolute end_date;
     struct TALER_Amount reserve_profits;
-
     struct GNUNET_PQ_ResultSpec rs[] = {
       GNUNET_PQ_result_spec_auto_from_type ("start_date", &start_date),
       GNUNET_PQ_result_spec_auto_from_type ("end_date", &end_date),
       TALER_PQ_result_spec_amount ("reserve_profits", &reserve_profits),
       GNUNET_PQ_result_spec_end
     };
+    
     if (GNUNET_OK !=
-        GNUNET_PQ_extract_result (result, rs, 0))
+        GNUNET_PQ_extract_result (result,
+                                 rs,
+                                 i))
     {
       GNUNET_break (0);
-      PQclear (result);
-      return GNUNET_SYSERR;
+      hrc->qs = GNUNET_DB_STATUS_HARD_ERROR;
+      return;
     }
-    ret = cb (cb_cls,
-              start_date,
-              end_date,
-              &reserve_profits);
-    switch (ret)
-    {
-    case GNUNET_OK:
+    hrc->qs = i + 1;
+    if (GNUNET_OK !=
+       hrc->cb (hrc->cb_cls,
+                start_date,
+                end_date,
+                &reserve_profits))
       break;
-
-    default:
-      i = nrows;
-    }
   }
-  PQclear (result);
-  return ret;
+}
+
+
+/**
+ * Return information about an exchange's historic revenue from reserves.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state
+ * @param session connection to use
+ * @param master_pub master key of the exchange
+ * @param cb function to call with results
+ * @param cb_cls closure for @a cb
+ * @return transaction status code
+ */
+static enum GNUNET_DB_QueryStatus
+postgres_select_historic_reserve_revenue (void *cls,
+                                          struct TALER_AUDITORDB_Session 
*session,
+                                          const struct TALER_MasterPublicKeyP 
*master_pub,
+                                          
TALER_AUDITORDB_HistoricReserveRevenueDataCallback cb,
+                                          void *cb_cls)
+{
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_auto_from_type (master_pub),
+    GNUNET_PQ_query_param_end
+  };
+  enum GNUNET_DB_QueryStatus qs;
+  struct HistoricReserveRevenueContext hrc = {
+    .cb = cb,
+    .cb_cls = cb_cls
+  };
+  
+  qs = GNUNET_PQ_eval_prepared_multi_select (session->conn,
+                                            "historic_reserve_summary_select",
+                                            params,
+                                            &historic_reserve_revenue_cb,
+                                            &hrc);
+  if (0 >= qs)
+    return qs;
+  return hrc.qs;
 }
 
 
@@ -2733,36 +2314,23 @@ postgres_select_historic_reserve_revenue (void *cls,
  * @param session connection to use
  * @param master_pub master key of the exchange
  * @param balance what the bank account balance of the exchange should show
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+ * @return transaction status code
  */
-static int
+static enum GNUNET_DB_QueryStatus
 postgres_insert_predicted_result (void *cls,
                                   struct TALER_AUDITORDB_Session *session,
                                   const struct TALER_MasterPublicKeyP 
*master_pub,
                                   const struct TALER_Amount *balance)
 {
-  PGresult *result;
-  int ret;
   struct GNUNET_PQ_QueryParam params[] = {
     GNUNET_PQ_query_param_auto_from_type (master_pub),
     TALER_PQ_query_param_amount (balance),
     GNUNET_PQ_query_param_end
   };
 
-  result = GNUNET_PQ_exec_prepared (session->conn,
-                                   "predicted_result_insert",
-                                   params);
-  if (PGRES_COMMAND_OK != PQresultStatus (result))
-  {
-    ret = GNUNET_SYSERR;
-    BREAK_DB_ERR (result);
-  }
-  else
-  {
-    ret = GNUNET_OK;
-  }
-  PQclear (result);
-  return ret;
+  return GNUNET_PQ_eval_prepared_non_select (session->conn,
+                                            "predicted_result_insert",
+                                            params);
 }
 
 
@@ -2774,37 +2342,23 @@ postgres_insert_predicted_result (void *cls,
  * @param session connection to use
  * @param master_pub master key of the exchange
  * @param balance what the bank account balance of the exchange should show
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+ * @return transaction status code
  */
-static int
+static enum GNUNET_DB_QueryStatus
 postgres_update_predicted_result (void *cls,
                                   struct TALER_AUDITORDB_Session *session,
                                   const struct TALER_MasterPublicKeyP 
*master_pub,
                                   const struct TALER_Amount *balance)
 {
-  PGresult *result;
-  int ret;
-
   struct GNUNET_PQ_QueryParam params[] = {
     TALER_PQ_query_param_amount (balance),
     GNUNET_PQ_query_param_auto_from_type (master_pub),
     GNUNET_PQ_query_param_end
   };
 
-  result = GNUNET_PQ_exec_prepared (session->conn,
-                                   "predicted_result_update",
-                                   params);
-  if (PGRES_COMMAND_OK != PQresultStatus (result))
-  {
-    ret = GNUNET_SYSERR;
-    BREAK_DB_ERR (result);
-  }
-  else
-  {
-    ret = GNUNET_OK;
-  }
-  PQclear (result);
-  return ret;
+  return GNUNET_PQ_eval_prepared_non_select (session->conn,
+                                            "predicted_result_update",
+                                            params);
 }
 
 
@@ -2815,10 +2369,9 @@ postgres_update_predicted_result (void *cls,
  * @param session connection to use
  * @param master_pub master key of the exchange
  * @param[out] balance expected bank account balance of the exchange
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure;
- *         #GNUNET_NO if we have no records for the @a master_pub
+ * @return transaction status code
  */
-static int
+static enum GNUNET_DB_QueryStatus
 postgres_get_predicted_balance (void *cls,
                                 struct TALER_AUDITORDB_Session *session,
                                 const struct TALER_MasterPublicKeyP 
*master_pub,
@@ -2828,43 +2381,16 @@ postgres_get_predicted_balance (void *cls,
     GNUNET_PQ_query_param_auto_from_type (master_pub),
     GNUNET_PQ_query_param_end
   };
-  PGresult *result;
-
-  result = GNUNET_PQ_exec_prepared (session->conn,
-                                    "predicted_result_select",
-                                    params);
-  if (PGRES_TUPLES_OK !=
-      PQresultStatus (result))
-  {
-    BREAK_DB_ERR (result);
-    PQclear (result);
-    return GNUNET_SYSERR;
-  }
-
-  int nrows = PQntuples (result);
-  if (0 == nrows)
-  {
-    LOG (GNUNET_ERROR_TYPE_DEBUG,
-         "postgres_get_predicted_balance() returned 0 matching rows\n");
-    PQclear (result);
-    return GNUNET_NO;
-  }
-  GNUNET_assert (1 == nrows);
-
   struct GNUNET_PQ_ResultSpec rs[] = {
     TALER_PQ_result_spec_amount ("balance", balance),
 
     GNUNET_PQ_result_spec_end
   };
-  if (GNUNET_OK !=
-      GNUNET_PQ_extract_result (result, rs, 0))
-  {
-    GNUNET_break (0);
-    PQclear (result);
-    return GNUNET_SYSERR;
-  }
-  PQclear (result);
-  return GNUNET_OK;
+
+  return GNUNET_PQ_eval_prepared_singleton_select (session->conn,
+                                                  "predicted_result_select",
+                                                  params,
+                                                  rs);
 }
 
 
diff --git a/src/auditordb/test_auditordb.c b/src/auditordb/test_auditordb.c
index bc23f11..04c6d23 100644
--- a/src/auditordb/test_auditordb.c
+++ b/src/auditordb/test_auditordb.c
@@ -19,6 +19,7 @@
  * @author Gabor X Toth
  */
 #include "platform.h"
+#include <gnunet/gnunet_db_lib.h>
 #include "taler_auditordb_lib.h"
 #include "taler_auditordb_plugin.h"
 
@@ -69,7 +70,9 @@ select_denomination_info_result (void *cls,
 {
   const struct TALER_DenominationKeyValidityPS *issue1 = cls;
 
-  if (0 != memcmp (issue1, issue2, sizeof (*issue2)))
+  if (0 != memcmp (issue1,
+                  issue2,
+                  sizeof (*issue2)))
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                 "select_denomination_info_result: issue does not match\n");
@@ -193,7 +196,7 @@ run (void *cls)
   TALER_amount_hton (&issue.fee_refresh, &fee_refresh);
   TALER_amount_hton (&issue.fee_refund, &fee_refund);
 
-  FAILIF (GNUNET_OK !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->insert_denomination_info (plugin->cls,
                                             session,
                                             &issue));
@@ -201,11 +204,11 @@ run (void *cls)
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
               "Test: select_denomination_info\n");
 
-  FAILIF (GNUNET_OK !=
+  FAILIF (0 >=
           plugin->select_denomination_info (plugin->cls,
                                             session,
                                             &master_pub,
-                                            select_denomination_info_result,
+                                            &select_denomination_info_result,
                                             &issue));
 
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
@@ -228,7 +231,7 @@ run (void *cls)
     .last_wire_out_serial_id = 0
   };
 
-  FAILIF (GNUNET_OK !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->insert_auditor_progress (plugin->cls,
                                            session,
                                            &master_pub,
@@ -243,7 +246,7 @@ run (void *cls)
   pp.last_refund_serial_id++;
   pp.last_wire_out_serial_id++;
 
-  FAILIF (GNUNET_OK !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->update_auditor_progress (plugin->cls,
                                            session,
                                            &master_pub,
@@ -252,7 +255,7 @@ run (void *cls)
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
               "Test: get_auditor_progress\n");
 
-  FAILIF (GNUNET_OK !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->get_auditor_progress (plugin->cls,
                                         session,
                                         &master_pub,
@@ -277,7 +280,7 @@ run (void *cls)
                  TALER_string_to_amount (CURRENCY ":23.456789",
                                          &withdraw_fee_balance));
 
-  FAILIF (GNUNET_OK !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->insert_reserve_info (plugin->cls,
                                        session,
                                        &reserve_pub,
@@ -289,7 +292,7 @@ run (void *cls)
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
               "Test: update_reserve_info\n");
 
-  FAILIF (GNUNET_OK !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->update_reserve_info (plugin->cls,
                                        session,
                                        &reserve_pub,
@@ -301,7 +304,7 @@ run (void *cls)
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
               "Test: get_reserve_info\n");
 
-  FAILIF (GNUNET_OK !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->get_reserve_info (plugin->cls,
                                     session,
                                     &reserve_pub,
@@ -318,7 +321,7 @@ run (void *cls)
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
               "Test: insert_reserve_summary\n");
 
-  FAILIF (GNUNET_OK !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->insert_reserve_summary (plugin->cls,
                                           session,
                                           &master_pub,
@@ -328,7 +331,7 @@ run (void *cls)
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
               "Test: update_reserve_summary\n");
 
-  FAILIF (GNUNET_OK !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->update_reserve_summary (plugin->cls,
                                           session,
                                           &master_pub,
@@ -341,22 +344,33 @@ run (void *cls)
   ZR_BLK (&reserve_balance2);
   ZR_BLK (&withdraw_fee_balance2);
 
-  FAILIF (GNUNET_OK !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->get_reserve_summary (plugin->cls,
                                        session,
                                        &master_pub,
                                        &reserve_balance2,
                                        &withdraw_fee_balance2));
 
-  FAILIF (0 != memcmp (&reserve_balance2, &reserve_balance, sizeof 
(reserve_balance))
-          || 0 != memcmp (&withdraw_fee_balance2, &withdraw_fee_balance, 
sizeof (withdraw_fee_balance)));
-
+  FAILIF ( (0 != memcmp (&reserve_balance2,
+                        &reserve_balance,
+                        sizeof (reserve_balance)) ||
+           (0 != memcmp (&withdraw_fee_balance2,
+                         &withdraw_fee_balance,
+                         sizeof (withdraw_fee_balance))) ) );
+          
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
               "Test: insert_denomination_balance\n");
 
-  struct TALER_Amount denom_balance, deposit_fee_balance, melt_fee_balance, 
refund_fee_balance;
-  struct TALER_Amount denom_balance2, deposit_fee_balance2, melt_fee_balance2, 
refund_fee_balance2;
-  struct TALER_Amount rbalance, rbalance2;
+  struct TALER_Amount denom_balance;
+  struct TALER_Amount deposit_fee_balance;
+  struct TALER_Amount melt_fee_balance;
+  struct TALER_Amount refund_fee_balance;
+  struct TALER_Amount denom_balance2;
+  struct TALER_Amount deposit_fee_balance2;
+  struct TALER_Amount melt_fee_balance2;
+  struct TALER_Amount refund_fee_balance2;
+  struct TALER_Amount rbalance;
+  struct TALER_Amount rbalance2;
 
   GNUNET_assert (GNUNET_OK ==
                  TALER_string_to_amount (CURRENCY ":12.345678",
@@ -374,7 +388,7 @@ run (void *cls)
                  TALER_string_to_amount (CURRENCY ":13.57986",
                                          &rbalance));
 
-  FAILIF (GNUNET_OK !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->insert_denomination_balance (plugin->cls,
                                                session,
                                                &denom_pub_hash,
@@ -389,7 +403,7 @@ run (void *cls)
   pp.last_melt_serial_id++;
   pp.last_refund_serial_id++;
 
-  FAILIF (GNUNET_OK !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->update_denomination_balance (plugin->cls,
                                                session,
                                                &denom_pub_hash,
@@ -398,7 +412,7 @@ run (void *cls)
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
               "Test: get_denomination_balance\n");
 
-  FAILIF (GNUNET_OK !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->get_denomination_balance (plugin->cls,
                                             session,
                                             &denom_pub_hash,
@@ -412,7 +426,7 @@ run (void *cls)
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
               "Test: insert_balance_summary\n");
 
-  FAILIF (GNUNET_OK !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->insert_balance_summary (plugin->cls,
                                                session,
                                                &master_pub,
@@ -425,7 +439,7 @@ run (void *cls)
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
               "Test: update_balance_summary\n");
 
-  FAILIF (GNUNET_OK !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->update_balance_summary (plugin->cls,
                                                session,
                                                &master_pub,
@@ -444,7 +458,7 @@ run (void *cls)
   ZR_BLK (&refund_fee_balance2);
   ZR_BLK (&rbalance2);
 
-  FAILIF (GNUNET_OK !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->get_balance_summary (plugin->cls,
                                             session,
                                             &master_pub,
@@ -454,17 +468,27 @@ run (void *cls)
                                             &refund_fee_balance2,
                                             &rbalance2));
 
-  FAILIF (0 != memcmp (&denom_balance2, &denom_balance, sizeof (denom_balance))
-          || 0 != memcmp (&deposit_fee_balance2, &deposit_fee_balance, sizeof 
(deposit_fee_balance))
-          || 0 != memcmp (&melt_fee_balance2, &melt_fee_balance, sizeof 
(melt_fee_balance))
-          || 0 != memcmp (&refund_fee_balance2, &refund_fee_balance, sizeof 
(refund_fee_balance)));
-  FAILIF (0 != memcmp (&rbalance2, &rbalance, sizeof (rbalance)));
+  FAILIF ( (0 != memcmp (&denom_balance2,
+                        &denom_balance,
+                        sizeof (denom_balance)) ) ||
+          (0 != memcmp (&deposit_fee_balance2,
+                        &deposit_fee_balance,
+                        sizeof (deposit_fee_balance)) ) ||
+          (0 != memcmp (&melt_fee_balance2,
+                        &melt_fee_balance,
+                        sizeof (melt_fee_balance)) ) ||
+          (0 != memcmp (&refund_fee_balance2,
+                        &refund_fee_balance,
+                        sizeof (refund_fee_balance))) );
+  FAILIF (0 != memcmp (&rbalance2,
+                      &rbalance,
+                      sizeof (rbalance)));
 
 
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
               "Test: insert_historic_denom_revenue\n");
 
-  FAILIF (GNUNET_OK !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->insert_historic_denom_revenue (plugin->cls,
                                                  session,
                                                  &master_pub,
@@ -472,7 +496,7 @@ run (void *cls)
                                                  past,
                                                  &rbalance));
 
-  FAILIF (GNUNET_OK !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->insert_historic_denom_revenue (plugin->cls,
                                                  session,
                                                  &master_pub,
@@ -510,17 +534,17 @@ run (void *cls)
     return GNUNET_OK;
   }
 
-  FAILIF (GNUNET_OK !=
+  FAILIF (0 >=
           plugin->select_historic_denom_revenue (plugin->cls,
                                                  session,
                                                  &master_pub,
-                                                 
select_historic_denom_revenue_result,
+                                                 
&select_historic_denom_revenue_result,
                                                  NULL));
 
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
               "Test: insert_historic_losses\n");
 
-  FAILIF (GNUNET_OK !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->insert_historic_losses (plugin->cls,
                                           session,
                                           &master_pub,
@@ -528,7 +552,7 @@ run (void *cls)
                                           past,
                                           &rbalance));
 
-  FAILIF (GNUNET_OK !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->insert_historic_losses (plugin->cls,
                                           session,
                                           &master_pub,
@@ -567,7 +591,7 @@ run (void *cls)
     return GNUNET_OK;
   }
 
-  FAILIF (GNUNET_OK !=
+  FAILIF (0 >=
           plugin->select_historic_losses (plugin->cls,
                                           session,
                                           &master_pub,
@@ -582,7 +606,7 @@ run (void *cls)
                  TALER_string_to_amount (CURRENCY ":56.789012",
                                          &reserve_profits));
 
-  FAILIF (GNUNET_OK !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->insert_historic_reserve_revenue (plugin->cls,
                                                    session,
                                                    &master_pub,
@@ -590,7 +614,7 @@ run (void *cls)
                                                    future,
                                                    &reserve_profits));
 
-  FAILIF (GNUNET_OK !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->insert_historic_reserve_revenue (plugin->cls,
                                                    session,
                                                    &master_pub,
@@ -627,7 +651,7 @@ run (void *cls)
     return GNUNET_OK;
   }
 
-  FAILIF (GNUNET_OK !=
+  FAILIF (0 >=
           plugin->select_historic_reserve_revenue (plugin->cls,
                                                    session,
                                                    &master_pub,
@@ -637,7 +661,7 @@ run (void *cls)
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
               "Test: insert_predicted_result\n");
 
-  FAILIF (GNUNET_OK !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->insert_predicted_result (plugin->cls,
                                            session,
                                            &master_pub,
@@ -650,18 +674,18 @@ run (void *cls)
                  TALER_string_to_amount (CURRENCY ":78.901234",
                                          &rbalance));
 
-  FAILIF (GNUNET_OK !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->update_predicted_result (plugin->cls,
                                            session,
                                            &master_pub,
                                            &rbalance));
 
-  FAILIF (GNUNET_OK !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->insert_wire_fee_summary (plugin->cls,
                                            session,
                                            &master_pub,
                                            &rbalance));
-  FAILIF (GNUNET_OK !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->update_wire_fee_summary (plugin->cls,
                                            session,
                                            &master_pub,
@@ -669,7 +693,7 @@ run (void *cls)
   {
     struct TALER_Amount rprof;
 
-    FAILIF (GNUNET_OK !=
+    FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
             plugin->get_wire_fee_summary (plugin->cls,
                                           session,
                                           &master_pub,
@@ -678,7 +702,7 @@ run (void *cls)
             TALER_amount_cmp (&rprof,
                               &reserve_profits));
   }
-  FAILIF (GNUNET_OK !=
+  FAILIF (0 >
           plugin->commit (plugin->cls,
                           session));
 
@@ -690,13 +714,13 @@ run (void *cls)
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
               "Test: get_predicted_balance\n");
 
-  FAILIF (GNUNET_OK !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->get_predicted_balance (plugin->cls,
                                          session,
                                          &master_pub,
                                          &rbalance2));
 
-  FAILIF (GNUNET_OK !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->del_reserve_info (plugin->cls,
                                     session,
                                     &reserve_pub,
diff --git a/src/bank-lib/Makefile.am b/src/bank-lib/Makefile.am
index 48c7e9c..d499753 100644
--- a/src/bank-lib/Makefile.am
+++ b/src/bank-lib/Makefile.am
@@ -11,7 +11,7 @@ lib_LTLIBRARIES = \
   libtalerfakebank.la
 
 libtalerbank_la_LDFLAGS = \
-  -version-info 0:0:0 \
+  -version-info 1:0:0 \
   -no-undefined
 
 libtalerbank_la_SOURCES = \
diff --git a/src/bank-lib/bank_api_common.c b/src/bank-lib/bank_api_common.c
index e68ba41..738d2a5 100644
--- a/src/bank-lib/bank_api_common.c
+++ b/src/bank-lib/bank_api_common.c
@@ -80,13 +80,12 @@ TALER_BANK_make_auth_header_ (const struct 
TALER_BANK_AuthenticationData *auth)
     authh = append (authh,
                     "X-Taler-Bank-Password",
                     auth->details.basic.password);
-    break;
+    return authh;    
   }
-  return authh;
+  return NULL;
 }
 
 
-
 /**
  * Obtain the URL to use for an API request.
  *
diff --git a/src/bank-lib/fakebank.c b/src/bank-lib/fakebank.c
index ceda4d3..2689241 100644
--- a/src/bank-lib/fakebank.c
+++ b/src/bank-lib/fakebank.c
@@ -65,7 +65,7 @@ struct Transaction
   /**
    * Subject of the transfer.
    */
-  struct TALER_WireTransferIdentifierRawP wtid;
+  char *subject;
 
   /**
    * Base URL of the exchange.
@@ -119,7 +119,7 @@ struct TALER_FAKEBANK_Handle
 /**
  * Check that the @a want_amount was transferred from
  * the @a want_debit to the @a want_credit account.  If
- * so, set the @a wtid to the transfer identifier.
+ * so, set the @a subject to the transfer identifier.
  * If not, return #GNUNET_SYSERR.
  *
  * @param h bank instance
@@ -128,7 +128,7 @@ struct TALER_FAKEBANK_Handle
  * @param want_credit account that should have been credited
  * @param exchange_base_url expected base URL of the exchange
  *        i.e. "https://example.com/";; may include a port
- * @param[out] wtid set to the wire transfer identifier
+ * @param[out] subject set to the wire transfer identifier
  * @return #GNUNET_OK on success
  */
 int
@@ -137,11 +137,9 @@ TALER_FAKEBANK_check (struct TALER_FAKEBANK_Handle *h,
                       uint64_t want_debit,
                       uint64_t want_credit,
                       const char *exchange_base_url,
-                      struct TALER_WireTransferIdentifierRawP *wtid)
+                      char **subject)
 {
-  struct Transaction *t;
-
-  for (t = h->transactions_head; NULL != t; t = t->next)
+  for (struct Transaction *t = h->transactions_head; NULL != t; t = t->next)
   {
     if ( (want_debit == t->debit_account) &&
          (want_credit == t->credit_account) &&
@@ -153,7 +151,7 @@ TALER_FAKEBANK_check (struct TALER_FAKEBANK_Handle *h,
       GNUNET_CONTAINER_DLL_remove (h->transactions_head,
                                    h->transactions_tail,
                                    t);
-      *wtid = t->wtid;
+      *subject = t->subject;
       GNUNET_free (t->exchange_base_url);
       GNUNET_free (t);
       return GNUNET_OK;
@@ -161,7 +159,7 @@ TALER_FAKEBANK_check (struct TALER_FAKEBANK_Handle *h,
   }
   fprintf (stderr,
            "Did not find matching transaction!\nI have:\n");
-  for (t = h->transactions_head; NULL != t; t = t->next)
+  for (struct Transaction *t = h->transactions_head; NULL != t; t = t->next)
   {
     char *s;
 
@@ -179,6 +177,49 @@ TALER_FAKEBANK_check (struct TALER_FAKEBANK_Handle *h,
 
 
 /**
+ * Tell the fakebank to create another wire transfer.
+ *
+ * @param h fake bank handle
+ * @param debit_account account to debit
+ * @param credit_account account to credit
+ * @param amount amount to transfer
+ * @param subject wire transfer subject to use
+ * @param exchange_base_url exchange URL
+ * @return serial_id of the transfer
+ */
+uint64_t
+TALER_FAKEBANK_make_transfer (struct TALER_FAKEBANK_Handle *h,
+                              uint64_t debit_account,
+                              uint64_t credit_account,
+                              const struct TALER_Amount *amount,
+                              const char *subject,
+                              const char *exchange_base_url)
+{
+  struct Transaction *t;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Making transfer from %llu to %llu over %s and subject %s\n",
+              (unsigned long long) debit_account,
+              (unsigned long long) credit_account,
+              TALER_amount2s (amount),
+              subject);
+  t = GNUNET_new (struct Transaction);
+  t->debit_account = debit_account;
+  t->credit_account = credit_account;
+  t->amount = *amount;
+  t->exchange_base_url = GNUNET_strdup (exchange_base_url);
+  t->serial_id = ++h->serial_counter;
+  t->date = GNUNET_TIME_absolute_get ();
+  t->subject = GNUNET_strdup (subject);
+  GNUNET_TIME_round_abs (&t->date);
+  GNUNET_CONTAINER_DLL_insert_tail (h->transactions_head,
+                                    h->transactions_tail,
+                                    t);
+  return t->serial_id;
+}
+
+
+/**
  * Check that no wire transfers were ordered (or at least none
  * that have not been taken care of via #TALER_FAKEBANK_check()).
  * If any transactions are onrecord, return #GNUNET_SYSERR.
@@ -228,6 +269,7 @@ TALER_FAKEBANK_stop (struct TALER_FAKEBANK_Handle *h)
     GNUNET_CONTAINER_DLL_remove (h->transactions_head,
                                  h->transactions_tail,
                                  t);
+    GNUNET_free (t->subject);
     GNUNET_free (t->exchange_base_url);
     GNUNET_free (t);
   }
@@ -291,9 +333,9 @@ handle_admin_add_incoming (struct TALER_FAKEBANK_Handle *h,
 {
   enum GNUNET_JSON_PostResult pr;
   json_t *json;
-  struct Transaction *t;
   struct MHD_Response *resp;
   int ret;
+  uint64_t serial_id;
 
   pr = GNUNET_JSON_post_parser (REQUEST_BUFFER_MAX,
                                 con_cls,
@@ -316,14 +358,17 @@ handle_admin_add_incoming (struct TALER_FAKEBANK_Handle 
*h,
   case GNUNET_JSON_PR_SUCCESS:
     break;
   }
-  t = GNUNET_new (struct Transaction);
   {
+    const char *wtid;
+    uint64_t debit_account;
+    uint64_t credit_account;
     const char *base_url;
+    struct TALER_Amount amount;
     struct GNUNET_JSON_Specification spec[] = {
-      GNUNET_JSON_spec_fixed_auto ("wtid", &t->wtid),
-      GNUNET_JSON_spec_uint64 ("debit_account", &t->debit_account),
-      GNUNET_JSON_spec_uint64 ("credit_account", &t->credit_account),
-      TALER_JSON_spec_amount ("amount", &t->amount),
+      GNUNET_JSON_spec_string ("wtid", &wtid),
+      GNUNET_JSON_spec_uint64 ("debit_account", &debit_account),
+      GNUNET_JSON_spec_uint64 ("credit_account", &credit_account),
+      TALER_JSON_spec_amount ("amount", &amount),
       GNUNET_JSON_spec_string ("exchange_url", &base_url),
       GNUNET_JSON_spec_end ()
     };
@@ -336,19 +381,18 @@ handle_admin_add_incoming (struct TALER_FAKEBANK_Handle 
*h,
       json_decref (json);
       return MHD_NO;
     }
-    t->exchange_base_url = GNUNET_strdup (base_url);
-    t->serial_id = ++h->serial_counter;
-    t->date = GNUNET_TIME_absolute_get ();
-    GNUNET_TIME_round_abs (&t->date);
-    GNUNET_CONTAINER_DLL_insert_tail (h->transactions_head,
-                                      h->transactions_tail,
-                                      t);
+    serial_id = TALER_FAKEBANK_make_transfer (h,
+                                              debit_account,
+                                              credit_account,
+                                              &amount,
+                                              wtid,
+                                              base_url);
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "Receiving incoming wire transfer: %llu->%llu from %s\n",
+                (unsigned long long) debit_account,
+                (unsigned long long) credit_account,
+                base_url);
   }
-  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              "Receiving incoming wire transfer: %llu->%llu from %s\n",
-              (unsigned long long) t->debit_account,
-              (unsigned long long) t->credit_account,
-              t->exchange_base_url);
   json_decref (json);
 
   /* Finally build response object */
@@ -358,7 +402,7 @@ handle_admin_add_incoming (struct TALER_FAKEBANK_Handle *h,
 
     json = json_pack ("{s:I}",
                       "serial_id",
-                      (json_int_t) t->serial_id);
+                      (json_int_t) serial_id);
     json_str = json_dumps (json,
                            JSON_INDENT(2));
     json_decref (json);
@@ -463,6 +507,12 @@ handle_history (struct TALER_FAKEBANK_Handle *h,
     GNUNET_break (0);
     return MHD_NO;
   }
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Client asked for up to %lld results of type %s for account %llu 
starting at %s\n",
+              count,
+              dir,
+              (unsigned long long) account_number,
+              start);
   if (NULL == dir)
     direction = TALER_BANK_DIRECTION_BOTH;
   else if (0 == strcasecmp (dir,
@@ -477,7 +527,7 @@ handle_history (struct TALER_FAKEBANK_Handle *h,
     else
       pos = h->transactions_tail;
   }
-  else
+  else if (NULL != h->transactions_head)
   {
     for (pos = h->transactions_head;
          NULL != pos;
@@ -495,6 +545,12 @@ handle_history (struct TALER_FAKEBANK_Handle *h,
     if (count < 0)
       pos = pos->prev;
   }
+  else
+  {
+    /* list is empty */
+    pos = NULL;
+  }
+
   history = json_array ();
   while ( (NULL != pos) &&
           (0 != count) )
@@ -502,6 +558,12 @@ handle_history (struct TALER_FAKEBANK_Handle *h,
     json_t *trans;
     char *subject;
 
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "Found transaction over %s from %llu to %llu\n",
+                TALER_amount2s (&pos->amount),
+                (unsigned long long) pos->debit_account,
+                (unsigned long long) pos->credit_account);
+
     if (! ( ( (account_number == pos->debit_account) &&
               (0 != (direction & TALER_BANK_DIRECTION_DEBIT)) ) ||
             ( (account_number == pos->credit_account) &&
@@ -514,17 +576,10 @@ handle_history (struct TALER_FAKEBANK_Handle *h,
       continue;
     }
 
-    {
-      char *ws;
-
-      ws = GNUNET_STRINGS_data_to_string_alloc (&pos->wtid,
-                                                sizeof (pos->wtid));
-      GNUNET_asprintf (&subject,
-                       "%s %s",
-                       ws,
-                       pos->exchange_base_url);
-      GNUNET_free (ws);
-    }
+    GNUNET_asprintf (&subject,
+                     "%s %s",
+                     pos->subject,
+                     pos->exchange_base_url);
     trans = json_pack ("{s:I, s:o, s:o, s:s, s:I, s:s}",
                        "row_id", (json_int_t) pos->serial_id,
                        "date", GNUNET_JSON_from_time_abs (pos->date),
@@ -553,6 +608,8 @@ handle_history (struct TALER_FAKEBANK_Handle *h,
     struct MHD_Response *resp;
 
     json_decref (history);
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "Returning empty transaction history\n");
     resp = MHD_create_response_from_buffer (0,
                                             "",
                                             MHD_RESPMEM_PERSISTENT);
diff --git a/src/bank-lib/test_bank_interpreter.c 
b/src/bank-lib/test_bank_interpreter.c
index e966424..f5aee8e 100644
--- a/src/bank-lib/test_bank_interpreter.c
+++ b/src/bank-lib/test_bank_interpreter.c
@@ -572,7 +572,9 @@ history_cb (void *cls,
                   "Expected history of length %llu, got %llu\n",
                   (unsigned long long) total,
                   (unsigned long long) cmd->details.history.results_obtained);
-      print_expected (h, total, UINT_MAX);
+      print_expected (h,
+                      total,
+                      UINT_MAX);
       free_history (h,
                     total);
       fail (is);
@@ -621,7 +623,6 @@ interpreter_run (void *cls)
   struct InterpreterState *is = cls;
   struct TBI_Command *cmd = &is->commands[is->ip];
   const struct TBI_Command *ref;
-  struct TALER_WireTransferIdentifierRawP wtid;
   struct TALER_Amount amount;
   const struct GNUNET_SCHEDULER_TaskContext *tc;
   struct TALER_BANK_AuthenticationData auth;
@@ -719,25 +720,34 @@ interpreter_run (void *cls)
     GNUNET_assert (GNUNET_OK ==
                    TALER_string_to_amount 
(ref->details.admin_add_incoming.amount,
                                            &amount));
-    if (GNUNET_OK !=
-        TALER_FAKEBANK_check (is->fakebank,
-                              &amount,
-                              ref->details.admin_add_incoming.debit_account_no,
-                              
ref->details.admin_add_incoming.credit_account_no,
-                              
ref->details.admin_add_incoming.exchange_base_url,
-                              &wtid))
     {
-      GNUNET_break (0);
-      fail (is);
-      return;
-    }
-    if (0 != memcmp (&wtid,
-                     &ref->details.admin_add_incoming.wtid,
-                     sizeof (wtid)))
-    {
-      GNUNET_break (0);
-      fail (is);
-      return;
+      char *subject;
+      char *expect;
+
+      if (GNUNET_OK !=
+          TALER_FAKEBANK_check (is->fakebank,
+                                &amount,
+                                
ref->details.admin_add_incoming.debit_account_no,
+                                
ref->details.admin_add_incoming.credit_account_no,
+                                
ref->details.admin_add_incoming.exchange_base_url,
+                                &subject))
+      {
+        GNUNET_break (0);
+        fail (is);
+        return;
+      }
+      expect = GNUNET_STRINGS_data_to_string_alloc 
(&ref->details.admin_add_incoming.wtid,
+                                                    sizeof 
(ref->details.admin_add_incoming.wtid));
+      if (0 != strcmp (subject, expect))
+      {
+        GNUNET_free (expect);
+        GNUNET_free (subject);
+        GNUNET_break (0);
+        fail (is);
+        return;
+      }
+      GNUNET_free (subject);
+      GNUNET_free (expect);
     }
     is->ip++;
     is->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
diff --git a/src/benchmark/taler-exchange-benchmark.c 
b/src/benchmark/taler-exchange-benchmark.c
index 70cc014..10a5eed 100644
--- a/src/benchmark/taler-exchange-benchmark.c
+++ b/src/benchmark/taler-exchange-benchmark.c
@@ -1234,10 +1234,12 @@ build_refresh ()
  *
  * @param cls closure
  * @param _keys information about keys of the exchange
+ * @param vc compatibility information
  */
 static void
 cert_cb (void *cls,
-         const struct TALER_EXCHANGE_Keys *_keys)
+         const struct TALER_EXCHANGE_Keys *_keys,
+        enum TALER_EXCHANGE_VersionCompatibility vc)
 {
   /* check that keys is OK */
   if (NULL == _keys)
diff --git a/src/exchange-lib/Makefile.am b/src/exchange-lib/Makefile.am
index 68cb71e..d891d1d 100644
--- a/src/exchange-lib/Makefile.am
+++ b/src/exchange-lib/Makefile.am
@@ -10,7 +10,7 @@ lib_LTLIBRARIES = \
   libtalerexchange.la
 
 libtalerexchange_la_LDFLAGS = \
-  -version-info 1:0:0 \
+  -version-info 2:0:0 \
   -no-undefined
 
 libtalerexchange_la_SOURCES = \
diff --git a/src/exchange-lib/exchange_api_handle.c 
b/src/exchange-lib/exchange_api_handle.c
index 590d4de..a03f7d4 100644
--- a/src/exchange-lib/exchange_api_handle.c
+++ b/src/exchange-lib/exchange_api_handle.c
@@ -29,6 +29,17 @@
 #include "taler_signatures.h"
 #include "exchange_api_handle.h"
 
+/**
+ * Which revision of the Taler protocol is implemented
+ * by this library?  Used to determine compatibility.
+ */
+#define TALER_PROTOCOL_CURRENT 0
+
+/**
+ * How many revisions back are we compatible to?
+ */
+#define TALER_PROTOCOL_AGE 0
+
 
 /**
  * Log error related to CURL operations.
@@ -485,11 +496,13 @@ parse_json_auditor (struct 
TALER_EXCHANGE_AuditorInformation *auditor,
  *
  * @param[in] resp_obj JSON object to parse
  * @param[out] key_data where to store the results we decoded
+ * @param[out] where to store version compatibility data
  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error (malformed JSON)
  */
 static int
 decode_keys_json (const json_t *resp_obj,
-                  struct TALER_EXCHANGE_Keys *key_data)
+                  struct TALER_EXCHANGE_Keys *key_data,
+                 enum TALER_EXCHANGE_VersionCompatibility *vc)
 {
   struct GNUNET_TIME_Absolute list_issue_date;
   struct TALER_ExchangeSignatureP sig;
@@ -497,7 +510,10 @@ decode_keys_json (const json_t *resp_obj,
   struct GNUNET_HashContext *hash_context;
   struct TALER_ExchangePublicKeyP pub;
 
-  memset (key_data, 0, sizeof (struct TALER_EXCHANGE_Keys));
+  /* FIXME: #4840: handle incremental / cherry-picked /keys! */
+  memset (key_data,
+         0,
+         sizeof (struct TALER_EXCHANGE_Keys));
   if (JSON_OBJECT != json_typeof (resp_obj))
     return GNUNET_SYSERR;
 
@@ -505,6 +521,9 @@ decode_keys_json (const json_t *resp_obj,
   /* parse the master public key and issue date of the response */
   {
     const char *ver;
+    unsigned int age;
+    unsigned int revision;
+    unsigned int current;
     struct GNUNET_JSON_Specification spec[] = {
       GNUNET_JSON_spec_string ("version",
                                &ver),
@@ -523,6 +542,29 @@ decode_keys_json (const json_t *resp_obj,
             GNUNET_JSON_parse (resp_obj,
                                spec,
                                NULL, NULL));
+    if (3 != sscanf (ver,
+                    "%u:%u:%u",
+                    &current,
+                    &revision,
+                    &age))
+    {
+      GNUNET_break_op (0);
+      GNUNET_CRYPTO_hash_context_abort (hash_context);
+      return GNUNET_SYSERR;
+    }
+    *vc = TALER_EXCHANGE_VC_MATCH;
+    if (TALER_PROTOCOL_CURRENT < current)
+    {
+      *vc |= TALER_EXCHANGE_VC_NEWER;
+      if (TALER_PROTOCOL_CURRENT < current - age)
+       *vc |= TALER_EXCHANGE_VC_INCOMPATIBLE;
+    }
+    if (TALER_PROTOCOL_CURRENT > current)
+    {
+      *vc |= TALER_EXCHANGE_VC_OLDER;
+      if (TALER_PROTOCOL_CURRENT - TALER_PROTOCOL_AGE > current)
+       *vc |= TALER_EXCHANGE_VC_INCOMPATIBLE;
+    }
     key_data->version = GNUNET_strdup (ver);
   }
 
@@ -602,7 +644,8 @@ decode_keys_json (const json_t *resp_obj,
       key_data->num_auditors = len;
     }
   }
-
+  key_data->last_issue_date = list_issue_date;
+  
   /* Validate signature... */
   ks.purpose.size = htonl (sizeof (ks));
   ks.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_KEY_SET);
@@ -703,12 +746,14 @@ keys_completed_cb (void *cls,
   struct TALER_EXCHANGE_Handle *exchange = kr->exchange;
   struct TALER_EXCHANGE_Keys kd;
   struct TALER_EXCHANGE_Keys kd_old;
+  enum TALER_EXCHANGE_VersionCompatibility vc;
 
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Received keys from URL `%s' with status %ld.\n",
               kr->url,
               response_code);
   kd_old = exchange->key_data;
+  vc = TALER_EXCHANGE_VC_PROTOCOL_ERROR;
   switch (response_code)
   {
   case 0:
@@ -721,7 +766,8 @@ keys_completed_cb (void *cls,
     }
     if (GNUNET_OK !=
         decode_keys_json (resp_obj,
-                          &kd))
+                          &kd,
+                         &vc))
     {
       response_code = 0;
       break;
@@ -744,7 +790,8 @@ keys_completed_cb (void *cls,
     exchange->state = MHS_FAILED;
     /* notify application that we failed */
     exchange->cert_cb (exchange->cert_cb_cls,
-                       NULL);
+                       NULL,
+                      vc);
     if (NULL != exchange->key_data_raw)
       {
         json_decref (exchange->key_data_raw);
@@ -760,7 +807,8 @@ keys_completed_cb (void *cls,
   exchange->state = MHS_CERT;
   /* notify application about the key information */
   exchange->cert_cb (exchange->cert_cb_cls,
-                     &exchange->key_data);
+                     &exchange->key_data,
+                    vc);
   free_key_data (&kd_old);
 }
 
@@ -987,7 +1035,22 @@ request_keys (struct TALER_EXCHANGE_Handle *exchange)
   GNUNET_assert (NULL == exchange->kr);
   kr = GNUNET_new (struct KeysRequest);
   kr->exchange = exchange;
-  kr->url = MAH_path_to_url (exchange, "/keys");
+  if (GNUNET_YES == MAH_handle_is_ready (exchange))
+  {
+    char *arg;
+
+    GNUNET_asprintf (&arg,
+                    "/keys?last_issue_date=%llu",
+                    (unsigned long long) 
exchange->key_data.last_issue_date.abs_value_us);
+    kr->url = MAH_path_to_url (exchange,
+                              arg);
+    GNUNET_free (arg);
+  }
+  else
+  {
+    kr->url = MAH_path_to_url (exchange,
+                              "/keys");
+  }
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Requesting keys with URL `%s'.\n",
               kr->url);
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 53bfbd1..b5b6804 100644
--- a/src/exchange-lib/test_exchange_api.c
+++ b/src/exchange-lib/test_exchange_api.c
@@ -636,9 +636,9 @@ struct Command
       const char *exchange_base_url;
 
       /**
-       * Set (!) to the wire transfer identifier observed.
+       * Set (!) to the wire transfer subject observed.
        */
-      struct TALER_WireTransferIdentifierRawP wtid;
+      char *subject;
 
     } check_bank_transfer;
 
@@ -1805,18 +1805,24 @@ deposit_wtid_cb (void *cls,
     if (NULL != cmd->details.deposit_wtid.bank_transfer_ref)
     {
       const struct Command *ref;
+      char *ws;
+
+      ws = GNUNET_STRINGS_data_to_string_alloc (wtid,
+                                                sizeof (*wtid));
+
 
       ref = find_command (is,
                           cmd->details.deposit_wtid.bank_transfer_ref);
       GNUNET_assert (NULL != ref);
-      if (0 != memcmp (wtid,
-                       &ref->details.check_bank_transfer.wtid,
-                       sizeof (struct TALER_WireTransferIdentifierRawP)))
+      if (0 != strcmp (ws,
+                       ref->details.check_bank_transfer.subject))
       {
         GNUNET_break (0);
+        GNUNET_free (ws);
         fail (is);
         return;
       }
+      GNUNET_free (ws);
     }
     break;
   case MHD_HTTP_ACCEPTED:
@@ -2496,7 +2502,11 @@ interpreter_run (void *cls)
         cmd->details.wire_deposits.wtid = ref->details.deposit_wtid.wtid;
         break;
       case OC_CHECK_BANK_TRANSFER:
-        cmd->details.wire_deposits.wtid = 
ref->details.check_bank_transfer.wtid;
+        GNUNET_assert (GNUNET_OK ==
+                       GNUNET_STRINGS_string_to_data 
(ref->details.check_bank_transfer.subject,
+                                                      strlen 
(ref->details.check_bank_transfer.subject),
+                                                      
&cmd->details.wire_deposits.wtid,
+                                                      sizeof 
(cmd->details.wire_deposits.wtid)));
         break;
       default:
         GNUNET_break (0);
@@ -2597,7 +2607,7 @@ interpreter_run (void *cls)
                                 cmd->details.check_bank_transfer.account_debit,
                                 
cmd->details.check_bank_transfer.account_credit,
                                 
cmd->details.check_bank_transfer.exchange_base_url,
-                                &cmd->details.check_bank_transfer.wtid))
+                                &cmd->details.check_bank_transfer.subject))
       {
         GNUNET_break (0);
         fail (is);
@@ -2951,6 +2961,8 @@ do_shutdown (void *cls)
       }
       break;
     case OC_CHECK_BANK_TRANSFER:
+      GNUNET_free_non_null (cmd->details.check_bank_transfer.subject);
+      cmd->details.check_bank_transfer.subject = NULL;
       break;
     case OC_CHECK_BANK_TRANSFERS_EMPTY:
       break;
@@ -3038,10 +3050,12 @@ do_shutdown (void *cls)
  *
  * @param cls closure
  * @param keys information about keys of the exchange
+ * @param vc version compatibility
  */
 static void
 cert_cb (void *cls,
-         const struct TALER_EXCHANGE_Keys *keys)
+         const struct TALER_EXCHANGE_Keys *keys,
+        enum TALER_EXCHANGE_VersionCompatibility vc)
 {
   struct InterpreterState *is = cls;
 
@@ -3564,7 +3578,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/.gitignore b/src/exchange/.gitignore
index 6ea90e7..e54fe49 100644
--- a/src/exchange/.gitignore
+++ b/src/exchange/.gitignore
@@ -5,3 +5,4 @@ taler-exchange-pursemod
 taler-exchange-reservemod
 taler-exchange-httpd
 taler-exchange-wirewatch
+test_taler_exchange_wirewatch-postgres
diff --git a/src/exchange/Makefile.am b/src/exchange/Makefile.am
index 899f396..9ff98d1 100644
--- a/src/exchange/Makefile.am
+++ b/src/exchange/Makefile.am
@@ -50,11 +50,15 @@ taler_exchange_httpd_SOURCES = \
   taler-exchange-httpd_mhd.c taler-exchange-httpd_mhd.h \
   taler-exchange-httpd_parsing.c taler-exchange-httpd_parsing.h \
   taler-exchange-httpd_payback.c taler-exchange-httpd_payback.h \
-  taler-exchange-httpd_refresh.c taler-exchange-httpd_refresh.h \
+  taler-exchange-httpd_refresh_link.c taler-exchange-httpd_refresh_link.h \
+  taler-exchange-httpd_refresh_melt.c taler-exchange-httpd_refresh_melt.h \
+  taler-exchange-httpd_refresh_reveal.c taler-exchange-httpd_refresh_reveal.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_track_transaction.c 
taler-exchange-httpd_track_transaction.h \
+  taler-exchange-httpd_track_transfer.c taler-exchange-httpd_track_transfer.h \
   taler-exchange-httpd_wire.c taler-exchange-httpd_wire.h \
   taler-exchange-httpd_validation.c taler-exchange-httpd_validation.h
 taler_exchange_httpd_LDADD = \
@@ -97,8 +101,23 @@ test_taler_exchange_aggregator_postgres_LDADD = \
   -ljansson \
   -lpthread
 
+test_taler_exchange_wirewatch_postgres_SOURCES = \
+  test_taler_exchange_wirewatch.c
+test_taler_exchange_wirewatch_postgres_LDADD = \
+  $(LIBGCRYPT_LIBS) \
+  $(top_builddir)/src/exchangedb/libtalerexchangedb.la \
+  $(top_builddir)/src/bank-lib/libtalerfakebank.la \
+  $(top_builddir)/src/json/libtalerjson.la \
+  $(top_builddir)/src/util/libtalerutil.la \
+  -lmicrohttpd \
+  -lgnunetutil \
+  -lgnunetjson \
+  -ljansson \
+  -lpthread
+
 check_PROGRAMS = \
-  test_taler_exchange_aggregator-postgres
+  test_taler_exchange_aggregator-postgres \
+  test_taler_exchange_wirewatch-postgres
 
 AM_TESTS_ENVIRONMENT=export 
TALER_PREFIX=$${TALER_PREFIX:address@hidden@};export 
PATH=$${TALER_PREFIX:address@hidden@}/bin:$$PATH;
 
@@ -109,6 +128,7 @@ TESTS = \
 
 EXTRA_DIST = \
   test-taler-exchange-aggregator-postgres.conf \
+  test-taler-exchange-wirewatch-postgres.conf \
   
test_taler_exchange_httpd_home/.local/share/taler/exchange/offline-keys/master.priv
 \
   test_taler_exchange_httpd.conf \
   exchange.conf \
diff --git a/src/exchange/taler-exchange-aggregator.c 
b/src/exchange/taler-exchange-aggregator.c
index f4573aa..fdb32a2 100644
--- a/src/exchange/taler-exchange-aggregator.c
+++ b/src/exchange/taler-exchange-aggregator.c
@@ -196,6 +196,11 @@ struct CloseTransferContext
    * Wire transfer method.
    */
   char *type;
+
+  /**
+   * Wire plugin used for closing the reserve.
+   */
+  struct WirePlugin *wp;
 };
 
 
@@ -341,18 +346,19 @@ advance_fees (struct WirePlugin *wp,
  * @param wp wire transfer fee data structure to update
  * @param now timestamp to update fees to
  * @param session DB session to use
- * @return #GNUNET_OK on success, #GNUNET_SYSERR if we
- *         lack current fee information (and need to exit)
+ * @return transaction status
  */
-static int
+static enum GNUNET_DB_QueryStatus
 update_fees (struct WirePlugin *wp,
              struct GNUNET_TIME_Absolute now,
              struct TALER_EXCHANGEDB_Session *session)
 {
+  enum GNUNET_DB_QueryStatus qs;
+  
   advance_fees (wp,
                 now);
   if (NULL != wp->af)
-    return GNUNET_OK;
+    return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
   /* Let's try to load it from disk... */
   wp->af = TALER_EXCHANGEDB_fees_read (cfg,
                                        wp->type);
@@ -362,26 +368,26 @@ update_fees (struct WirePlugin *wp,
        NULL != p;
        p = p->next)
   {
-    if (GNUNET_SYSERR ==
-        db_plugin->insert_wire_fee (db_plugin->cls,
-                                    session,
-                                    wp->type,
-                                    p->start_date,
-                                    p->end_date,
-                                    &p->wire_fee,
-                                    &p->master_sig))
+    qs = db_plugin->insert_wire_fee (db_plugin->cls,
+                                    session,
+                                    wp->type,
+                                    p->start_date,
+                                    p->end_date,
+                                    &p->wire_fee,
+                                    &p->master_sig);
+    if (qs < 0)
     {
       TALER_EXCHANGEDB_fees_free (wp->af);
       wp->af = NULL;
-      return GNUNET_SYSERR;
+      return qs;
     }
   }
   if (NULL != wp->af)
-    return GNUNET_OK;
+    return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
   GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
               "Failed to find current wire transfer fees for `%s'\n",
               wp->type);
-  return GNUNET_SYSERR;
+  return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
 }
 
 
@@ -420,6 +426,26 @@ find_plugin (const char *type)
   return wp;
 }
 
+
+/**
+ * Free data stored in #au.
+ */
+static void
+cleanup_au (void)
+{
+  if (NULL == au)
+    return;
+  GNUNET_free_non_null (au->additional_rows);
+  if (NULL != au->wire)
+  {
+    json_decref (au->wire);
+    au->wire = NULL;
+  }
+  GNUNET_free (au);
+  au = NULL;
+}
+
+
 /**
  * We're being aborted with CTRL-C (or SIGTERM). Shut down.
  *
@@ -428,8 +454,8 @@ find_plugin (const char *type)
 static void
 shutdown_task (void *cls)
 {
-  struct WirePlugin *wp;
-
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Running shutdown\n");
   if (NULL != task)
   {
     GNUNET_SCHEDULER_cancel (task);
@@ -458,22 +484,34 @@ shutdown_task (void *cls)
     }
     db_plugin->rollback (db_plugin->cls,
                          au->session);
-    GNUNET_free_non_null (au->additional_rows);
-    if (NULL != au->wire)
-      json_decref (au->wire);
-    au = NULL;
-    GNUNET_free (au);
+    cleanup_au ();
+  }
+  if (NULL != ctc)
+  {
+    ctc->wp->wire_plugin->prepare_wire_transfer_cancel 
(ctc->wp->wire_plugin->cls,
+                                                        ctc->ph);
+    ctc->ph = NULL;
+    db_plugin->rollback (db_plugin->cls,
+                         ctc->session);
+    GNUNET_free (ctc->type);
+    GNUNET_free (ctc);
+    ctc = NULL;
   }
   TALER_EXCHANGEDB_plugin_unload (db_plugin);
-  while (NULL != (wp = wp_head))
+
   {
-    GNUNET_CONTAINER_DLL_remove (wp_head,
-                                 wp_tail,
-                                 wp);
-    TALER_WIRE_plugin_unload (wp->wire_plugin);
-    TALER_EXCHANGEDB_fees_free (wp->af);
-    GNUNET_free (wp->type);
-    GNUNET_free (wp);
+    struct WirePlugin *wp;
+
+    while (NULL != (wp = wp_head))
+    {
+      GNUNET_CONTAINER_DLL_remove (wp_head,
+                                   wp_tail,
+                                   wp);
+      TALER_WIRE_plugin_unload (wp->wire_plugin);
+      TALER_EXCHANGEDB_fees_free (wp->af);
+      GNUNET_free (wp->type);
+      GNUNET_free (wp);
+    }
   }
   GNUNET_CONFIGURATION_destroy (cfg);
   cfg = NULL;
@@ -516,6 +554,14 @@ exchange_serve_process_config ()
              "Failed to initialize DB subsystem\n");
     return GNUNET_SYSERR;
   }
+  if (GNUNET_OK !=
+      db_plugin->create_tables (db_plugin->cls))
+  {
+    fprintf (stderr,
+             "Failed to initialize DB tables\n");
+    TALER_EXCHANGEDB_plugin_unload (db_plugin);
+    return GNUNET_SYSERR;
+  }
 
   return GNUNET_OK;
 }
@@ -535,9 +581,9 @@ exchange_serve_process_config ()
  * @param wire_deadline by which the merchant adviced that he would like the
  *        wire transfer to be executed
  * @param wire wire details for the merchant
- * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
+ * @return transaction status code,  #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT to 
continue to iterate
  */
-static int
+static enum GNUNET_DB_QueryStatus
 deposit_cb (void *cls,
             uint64_t row_id,
             const struct TALER_MerchantPublicKeyP *merchant_pub,
@@ -548,16 +594,19 @@ 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 !=
+  if (GNUNET_SYSERR ==
       TALER_amount_subtract (&au->total_amount,
                              amount_with_fee,
                              deposit_fee))
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Fatally malformed record at row %llu\n",
-                (unsigned long long) row_id);
-    return GNUNET_SYSERR;
+                "Fatally malformed record at row %llu over %s\n",
+                (unsigned long long) row_id,
+               TALER_amount2s (amount_with_fee));
+    return GNUNET_DB_STATUS_HARD_ERROR;
   }
   au->row_id = row_id;
   GNUNET_assert (NULL == au->wire);
@@ -573,37 +622,41 @@ deposit_cb (void *cls,
 
   au->wp = find_plugin (extract_type (au->wire));
   if (NULL == au->wp)
-    return GNUNET_SYSERR;
+    return GNUNET_DB_STATUS_HARD_ERROR;
 
   /* make sure we have current fees */
   au->execution_time = GNUNET_TIME_absolute_get ();
   (void) GNUNET_TIME_round_abs (&au->execution_time);
-  if (GNUNET_OK !=
-      update_fees (au->wp,
-                   au->execution_time,
-                   au->session))
-    return GNUNET_SYSERR;
+  qs = update_fees (au->wp,
+                   au->execution_time,
+                   au->session);
+  if (qs <= 0)
+  {
+    if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
+      qs = GNUNET_DB_STATUS_HARD_ERROR;
+    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+    return qs;
+  }
   au->wire_fee = au->wp->af->wire_fee;
 
-  if (GNUNET_OK !=
-      db_plugin->insert_aggregation_tracking (db_plugin->cls,
-                                              au->session,
-                                              &au->wtid,
-                                              row_id))
+  qs = db_plugin->insert_aggregation_tracking (db_plugin->cls,
+                                              au->session,
+                                              &au->wtid,
+                                              row_id);
+  if (qs <= 0)
   {
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
+    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+    return qs;
   }
-  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)
   {
-    GNUNET_break (0);
-    au->failed = GNUNET_YES;
-    return GNUNET_SYSERR;
+    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+    return qs;
   }
-  return GNUNET_OK;
+  return qs;
 }
 
 
@@ -621,9 +674,9 @@ deposit_cb (void *cls,
  * @param wire_deadline by which the merchant adviced that he would like the
  *        wire transfer to be executed
  * @param wire wire details for the merchant
- * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
+ * @return transaction status code
  */
-static int
+static enum GNUNET_DB_QueryStatus
 aggregate_cb (void *cls,
               uint64_t row_id,
               const struct TALER_MerchantPublicKeyP *merchant_pub,
@@ -635,23 +688,28 @@ aggregate_cb (void *cls,
               const json_t *wire)
 {
   struct TALER_Amount delta;
+  enum GNUNET_DB_QueryStatus qs;
 
   GNUNET_break (0 ==
                 memcmp (&au->merchant_pub,
                         merchant_pub,
                         sizeof (struct TALER_MerchantPublicKeyP)));
   /* compute contribution of this coin after fees */
-  if (GNUNET_OK !=
+  if (GNUNET_SYSERR ==
       TALER_amount_subtract (&delta,
                              amount_with_fee,
                              deposit_fee))
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Fatally malformed record at %llu\n",
-                (unsigned long long) row_id);
-    return GNUNET_SYSERR;
+                "Fatally malformed record at %llu over amount %s\n",
+                (unsigned long long) row_id,
+               TALER_amount2s (amount_with_fee));
+    return GNUNET_DB_STATUS_HARD_ERROR;
   }
   /* add to total */
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+             "Adding transaction amount %s to aggregation\n",
+             TALER_amount2s (&delta));
   if (GNUNET_OK !=
       TALER_amount_add (&au->total_amount,
                         &au->total_amount,
@@ -661,14 +719,14 @@ aggregate_cb (void *cls,
                 "Overflow or currency incompatibility during aggregation at 
%llu\n",
                 (unsigned long long) row_id);
     /* Skip this one, but keep going! */
-    return GNUNET_OK;
+    return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
   }
   if (au->rows_offset >= aggregation_limit)
   {
     /* Bug: we asked for at most #aggregation_limit results! */
     GNUNET_break (0);
     /* Skip this one, but keep going. */
-    return GNUNET_OK;
+    return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
   }
   if (NULL == au->additional_rows)
     au->additional_rows = GNUNET_new_array (aggregation_limit,
@@ -676,25 +734,27 @@ aggregate_cb (void *cls,
   /* "append" to our list of rows */
   au->additional_rows[au->rows_offset++] = row_id;
   /* insert into aggregation tracking table */
-  if (GNUNET_OK !=
-      db_plugin->insert_aggregation_tracking (db_plugin->cls,
-                                              au->session,
-                                              &au->wtid,
-                                              row_id))
+  qs = db_plugin->insert_aggregation_tracking (db_plugin->cls,
+                                              au->session,
+                                              &au->wtid,
+                                              row_id);
+  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
   {
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
+    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+    return qs;
   }
-  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)
   {
-    GNUNET_break (0);
-    au->failed = GNUNET_YES;
-    return GNUNET_SYSERR;
+    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+    return qs;
   }
-  return GNUNET_OK;
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+             "Added row %llu to aggregation\n",
+             (unsigned long long) row_id);
+  return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
 }
 
 
@@ -733,6 +793,39 @@ run_aggregation (void *cls);
 
 
 /**
+ * Execute the wire transfers that we have committed to
+ * do.
+ *
+ * @param cls pointer to an `int` which we will return from main()
+ */
+static void
+run_transfers (void *cls);
+
+
+/**
+ * Perform a database commit. If it fails, print a warning.
+ *
+ * @param session session to perform the commit for.
+ * @return status of commit
+ */
+static enum GNUNET_DB_QueryStatus
+commit_or_warn (struct TALER_EXCHANGEDB_Session *session)
+{
+  enum GNUNET_DB_QueryStatus qs;
+
+  qs = db_plugin->commit (db_plugin->cls,
+                          session);
+  if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
+    return qs;
+  GNUNET_log ((GNUNET_DB_STATUS_SOFT_ERROR == qs)
+              ? GNUNET_ERROR_TYPE_INFO
+              : GNUNET_ERROR_TYPE_ERROR,
+              "Failed to commit database transaction!\n");
+  return qs;
+}
+
+
+/**
  * Function to be called with the prepared transfer data
  * when closing a reserve.
  *
@@ -745,7 +838,12 @@ prepare_close_cb (void *cls,
                  const char *buf,
                  size_t buf_size)
 {
+  enum GNUNET_DB_QueryStatus qs;
+    
   GNUNET_assert (cls == ctc);
+
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Prepared for reserve closing\n");
   ctc->ph = NULL;
   if (NULL == buf)
   {
@@ -762,14 +860,25 @@ prepare_close_cb (void *cls,
   }
 
   /* Commit our intention to execute the wire transfer! */
-  if (GNUNET_OK !=
-      db_plugin->wire_prepare_data_insert (db_plugin->cls,
-                                           ctc->session,
-                                           ctc->type,
-                                           buf,
-                                           buf_size))
+  qs = db_plugin->wire_prepare_data_insert (db_plugin->cls,
+                                           ctc->session,
+                                           ctc->type,
+                                           buf,
+                                           buf_size);
+  if (GNUNET_DB_STATUS_HARD_ERROR == qs)
+  {
+    GNUNET_break (0); 
+    db_plugin->rollback (db_plugin->cls,
+                         ctc->session);
+    global_ret = GNUNET_SYSERR;
+    GNUNET_SCHEDULER_shutdown ();
+    GNUNET_free (ctc->type);
+    GNUNET_free (ctc);
+    ctc = NULL;
+    return;
+  }
+  if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
   {
-    GNUNET_break (0); /* why? how to best recover? */
     db_plugin->rollback (db_plugin->cls,
                          ctc->session);
     /* start again */
@@ -782,48 +891,64 @@ prepare_close_cb (void *cls,
   }
 
   /* finally commit */
-  if (GNUNET_OK !=
-      db_plugin->commit (db_plugin->cls,
-                        ctc->session))
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-               "Failed to commit database transaction!\n");
-  }
+  (void) commit_or_warn (ctc->session);
   GNUNET_free (ctc->type);
   GNUNET_free (ctc);
   ctc = NULL;
-  task = GNUNET_SCHEDULER_add_now (&run_aggregation,
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Reserve closure committed, running transfer\n");
+  task = GNUNET_SCHEDULER_add_now (&run_transfers,
                                   NULL);
 }
 
 
 /**
+ * Closure for #expired_reserve_cb().
+ */
+struct ExpiredReserveContext
+{
+
+  /**
+   * Database session we are using.
+   */
+  struct TALER_EXCHANGEDB_Session *session;
+
+  /**
+   * Set to #GNUNET_YES if the transaction continues
+   * asynchronously.
+   */
+  int async_cont;
+};
+
+
+/**
  * Function called with details about expired reserves.
  * We trigger the reserve closure by inserting the respective
  * closing record and prewire instructions into the respective
  * tables.
  *
- * @param cls a `struct TALER_EXCHANGEDB_Session *`
+ * @param cls a `struct ExpiredReserveContext *`
  * @param reserve_pub public key of the reserve
  * @param left amount left in the reserve
  * @param account_details information about the reserve's bank account
  * @param expiration_date when did the reserve expire
- * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
+ * @return transaction status code
  */
-static int
+static enum GNUNET_DB_QueryStatus
 expired_reserve_cb (void *cls,
                    const struct TALER_ReservePublicKeyP *reserve_pub,
                    const struct TALER_Amount *left,
                    const json_t *account_details,
                    struct GNUNET_TIME_Absolute expiration_date)
 {
-  struct TALER_EXCHANGEDB_Session *session = cls;
+  struct ExpiredReserveContext *erc = cls;
+  struct TALER_EXCHANGEDB_Session *session = erc->session;
   struct GNUNET_TIME_Absolute now;
   struct TALER_WireTransferIdentifierRawP wtid;
   struct TALER_Amount amount_without_fee;
   const struct TALER_Amount *closing_fee;
   int ret;
-  int iret;
+  enum GNUNET_DB_QueryStatus qs;
   const char *type;
   struct WirePlugin *wp;
 
@@ -835,35 +960,32 @@ expired_reserve_cb (void *cls,
   if (NULL == type)
   {
     GNUNET_break (0);
-    db_plugin->rollback (db_plugin->cls,
-                        session);
     global_ret = GNUNET_SYSERR;
     GNUNET_SCHEDULER_shutdown ();
-    return GNUNET_SYSERR;
+    return GNUNET_DB_STATUS_HARD_ERROR;
   }
   wp = find_plugin (type);
   if (NULL == wp)
   {
     GNUNET_break (0);
-    db_plugin->rollback (db_plugin->cls,
-                        session);
     global_ret = GNUNET_SYSERR;
     GNUNET_SCHEDULER_shutdown ();
-    return GNUNET_SYSERR;
+    return GNUNET_DB_STATUS_HARD_ERROR;
   }
 
   /* lookup `closing_fee` */
-  if (GNUNET_OK !=
-      update_fees (wp,
-                  now,
-                  session))
+  qs = update_fees (wp,
+                   now,
+                   session);
+  if (qs <= 0)
   {
-    GNUNET_break (0);
-    db_plugin->rollback (db_plugin->cls,
-                        session);
+    if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
+      qs = GNUNET_DB_STATUS_HARD_ERROR;
+    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     global_ret = GNUNET_SYSERR;
-    GNUNET_SCHEDULER_shutdown ();
-    return GNUNET_SYSERR;
+    if (GNUNET_DB_STATUS_HARD_ERROR == qs)
+      GNUNET_SCHEDULER_shutdown ();
+    return qs;
   }
   closing_fee = &wp->af->closing_fee;
 
@@ -890,16 +1012,22 @@ expired_reserve_cb (void *cls,
          reserve_pub,
          GNUNET_MIN (sizeof (wtid),
                      sizeof (*reserve_pub)));
-  iret = db_plugin->insert_reserve_closed (db_plugin->cls,
-                                          session,
-                                          reserve_pub,
-                                          now,
-                                          account_details,
-                                          &wtid,
-                                          left,
-                                          closing_fee);
+  qs = db_plugin->insert_reserve_closed (db_plugin->cls,
+                                        session,
+                                        reserve_pub,
+                                        now,
+                                        account_details,
+                                        &wtid,
+                                        left,
+                                        closing_fee);
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Closing reserve %s over %s (%d, %d)\n",
+              TALER_B2S (reserve_pub),
+              TALER_amount2s (left),
+              ret,
+              qs);
   if ( (GNUNET_OK == ret) &&
-       (GNUNET_OK == iret) )
+       (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) )
   {
     /* success, perform wire transfer */
     if (GNUNET_SYSERR ==
@@ -907,18 +1035,17 @@ expired_reserve_cb (void *cls,
                                       &amount_without_fee))
     {
       GNUNET_break (0);
-      db_plugin->rollback (db_plugin->cls,
-                          session);
       global_ret = GNUNET_SYSERR;
       GNUNET_SCHEDULER_shutdown ();
-      return GNUNET_SYSERR;
+      return GNUNET_DB_STATUS_HARD_ERROR;
     }
     ctc = GNUNET_new (struct CloseTransferContext);
+    ctc->wp = wp;
     ctc->session = session;
     ctc->type = GNUNET_strdup (type);
     ctc->ph
       = wp->wire_plugin->prepare_wire_transfer (wp->wire_plugin->cls,
-                                               au->wire,
+                                               account_details,
                                                &amount_without_fee,
                                                exchange_base_url,
                                                &wtid,
@@ -927,37 +1054,29 @@ expired_reserve_cb (void *cls,
     if (NULL == ctc->ph)
     {
       GNUNET_break (0);
-      db_plugin->rollback (db_plugin->cls,
-                          session);
       global_ret = GNUNET_SYSERR;
       GNUNET_SCHEDULER_shutdown ();
       GNUNET_free (ctc->type);
       GNUNET_free (ctc);
       ctc = NULL;
+      return GNUNET_DB_STATUS_HARD_ERROR;
     }
-    return GNUNET_SYSERR;
+    erc->async_cont = GNUNET_YES;
+    return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
   }
   /* Check for hard failure */
-  if (GNUNET_SYSERR == iret)
+  if ( (GNUNET_SYSERR == ret) ||
+       (GNUNET_DB_STATUS_HARD_ERROR == qs) )
   {
     GNUNET_break (0);
-    db_plugin->rollback (db_plugin->cls,
-                         session);
     global_ret = GNUNET_SYSERR;
     GNUNET_SCHEDULER_shutdown ();
-    return GNUNET_SYSERR;
+    return GNUNET_DB_STATUS_HARD_ERROR;
   }
-  /* Reserve balance was almost zero; just commit */
-  if (GNUNET_OK !=
-      db_plugin->commit (db_plugin->cls,
-                        session))
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-               "Failed to commit database transaction!\n");
-  }
-  task = GNUNET_SCHEDULER_add_now (&run_reserve_closures,
-                                  NULL);
-  return GNUNET_SYSERR;
+  /* Reserve balance was almost zero OR soft error */
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Reserve was virtually empty, moving on\n");
+  return qs;
 }
 
 
@@ -971,15 +1090,16 @@ static void
 run_reserve_closures (void *cls)
 {
   struct TALER_EXCHANGEDB_Session *session;
-  int ret;
+  enum GNUNET_DB_QueryStatus qs;
   const struct GNUNET_SCHEDULER_TaskContext *tc;
-
+  struct ExpiredReserveContext erc;
+  
   task = NULL;
   reserves_idle = GNUNET_NO;
   tc = GNUNET_SCHEDULER_get_task_context ();
   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
     return;
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
               "Checking for reserves to close\n");
   if (NULL == (session = db_plugin->get_session (db_plugin->cls)))
   {
@@ -999,28 +1119,44 @@ run_reserve_closures (void *cls)
     GNUNET_SCHEDULER_shutdown ();
     return;
   }
-  ret = db_plugin->get_expired_reserves (db_plugin->cls,
-                                        session,
-                                        GNUNET_TIME_absolute_get (),
-                                        &expired_reserve_cb,
-                                        session);
-  if (GNUNET_SYSERR == ret)
+  erc.session = session;
+  erc.async_cont = GNUNET_NO;
+  qs = db_plugin->get_expired_reserves (db_plugin->cls,
+                                       session,
+                                       GNUNET_TIME_absolute_get (),
+                                       &expired_reserve_cb,
+                                       &erc);
+  switch (qs) 
   {
+  case GNUNET_DB_STATUS_HARD_ERROR:    
     GNUNET_break (0);
     db_plugin->rollback (db_plugin->cls,
                          session);
     global_ret = GNUNET_SYSERR;
     GNUNET_SCHEDULER_shutdown ();
     return;
-  }
-  if (GNUNET_NO == ret)
-  {
+  case GNUNET_DB_STATUS_SOFT_ERROR:
+    db_plugin->rollback (db_plugin->cls,
+                         session);
+    task = GNUNET_SCHEDULER_add_now (&run_reserve_closures,
+                                    NULL);
+    return;
+  case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "No more idle reserves, going back to aggregation\n");
     reserves_idle = GNUNET_YES;
     db_plugin->rollback (db_plugin->cls,
                          session);
     task = GNUNET_SCHEDULER_add_now (&run_aggregation,
                                     NULL);
     return;
+  case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+    if (GNUNET_YES == erc.async_cont)
+      break;
+    (void) commit_or_warn (session);
+    task = GNUNET_SCHEDULER_add_now (&run_reserve_closures,
+                                    NULL);
+    return;
   }
 }
 
@@ -1036,21 +1172,20 @@ run_aggregation (void *cls)
 {
   static int swap;
   struct TALER_EXCHANGEDB_Session *session;
-  unsigned int i;
-  int ret;
+  enum GNUNET_DB_QueryStatus qs;
   const struct GNUNET_SCHEDULER_TaskContext *tc;
 
   task = NULL;
   tc = GNUNET_SCHEDULER_get_task_context ();
   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
     return;
- if (0 == (++swap % 2))
+  if (0 == (++swap % 2))
   {
     task = GNUNET_SCHEDULER_add_now (&run_reserve_closures,
                                     NULL);
     return;
   }
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
               "Checking for ready deposits to aggregate\n");
   if (NULL == (session = db_plugin->get_session (db_plugin->cls)))
   {
@@ -1072,19 +1207,16 @@ run_aggregation (void *cls)
   }
   au = GNUNET_new (struct AggregationUnit);
   au->session = session;
-  ret = db_plugin->get_ready_deposit (db_plugin->cls,
-                                      session,
-                                      &deposit_cb,
-                                      au);
-  if (0 >= ret)
+  qs = db_plugin->get_ready_deposit (db_plugin->cls,
+                                    session,
+                                    &deposit_cb,
+                                    au);
+  if (0 >= qs)
   {
-    if (NULL != au->wire)
-      json_decref (au->wire);
-    GNUNET_free (au);
-    au = NULL;
+    cleanup_au ();
     db_plugin->rollback (db_plugin->cls,
                          session);
-    if (GNUNET_SYSERR == ret)
+    if (GNUNET_DB_STATUS_HARD_ERROR == qs)
     {
       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                   "Failed to execute deposit iteration!\n");
@@ -1092,20 +1224,31 @@ run_aggregation (void *cls)
       GNUNET_SCHEDULER_shutdown ();
       return;
     }
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+    if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+    {
+      /* should re-try immediately */
+      swap--; /* do not count failed attempts */
+      task = GNUNET_SCHEDULER_add_now (&run_aggregation,
+                                      NULL);
+      return;
+    }
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
                 "No more ready deposits, going to sleep\n");
-    if (GNUNET_YES == test_mode)
+    if ( (GNUNET_YES == test_mode) &&
+         (swap >= 2) )
     {
       /* in test mode, shutdown if we end up being idle */
       GNUNET_SCHEDULER_shutdown ();
     }
     else
     {
-      /* nothing to do, sleep for a minute and try again */
-      if (GNUNET_NO == reserves_idle)
+      if ( (GNUNET_NO == reserves_idle) ||
+          (GNUNET_YES == test_mode) )
+       /* Possibly more to on reserves, go for it immediately */
        task = GNUNET_SCHEDULER_add_now (&run_reserve_closures,
                                         NULL);
       else
+       /* nothing to do, sleep for a minute and try again */
        task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES,
                                             &run_aggregation,
                                             NULL);
@@ -1117,29 +1260,37 @@ run_aggregation (void *cls)
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
               "Found ready deposit for %s, aggregating\n",
               TALER_B2S (&au->merchant_pub));
-  ret = db_plugin->iterate_matching_deposits (db_plugin->cls,
-                                              session,
-                                              &au->h_wire,
-                                              &au->merchant_pub,
-                                              &aggregate_cb,
-                                              au,
-                                              aggregation_limit);
-  if ( (GNUNET_SYSERR == ret) ||
+  qs = db_plugin->iterate_matching_deposits (db_plugin->cls,
+                                            session,
+                                            &au->h_wire,
+                                            &au->merchant_pub,
+                                            &aggregate_cb,
+                                            au,
+                                            aggregation_limit);
+  if ( (GNUNET_DB_STATUS_HARD_ERROR == qs) ||
        (GNUNET_YES == au->failed) )
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                 "Failed to execute deposit iteration!\n");
-    GNUNET_free_non_null (au->additional_rows);
-    json_decref (au->wire);
-    GNUNET_free (au);
-    au = NULL;
+    cleanup_au ();
     db_plugin->rollback (db_plugin->cls,
                          session);
     global_ret = GNUNET_SYSERR;
     GNUNET_SCHEDULER_shutdown ();
     return;
   }
-
+  if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+  {
+    /* serializiability issue, try again */
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "Serialization issue, trying again later!\n");
+    db_plugin->rollback (db_plugin->cls,
+                         session);
+    task = GNUNET_SCHEDULER_add_now (&run_aggregation,
+                                    NULL);
+    return;
+  }
+  
   /* Subtract wire transfer fee and round to the unit supported by the
      wire transfer method; Check if after rounding down, we still have
      an amount to transfer, and if not mark as 'tiny'. */
@@ -1154,13 +1305,16 @@ run_aggregation (void *cls)
          (0 == au->final_amount.fraction) ) )
   {
     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                "Aggregate value too low for transfer\n");
+                "Aggregate value too low for transfer (%d/%s)\n",
+               qs,
+               TALER_amount2s (&au->final_amount));
     /* Rollback ongoing transaction, as we will not use the respective
        WTID and thus need to remove the tracking data */
     db_plugin->rollback (db_plugin->cls,
                          session);
-    /* Start another transaction to mark all* of the selected deposits
-       *as minor! */
+
+    /* There were results, just the value was too low.  Start another
+       transaction to mark all* of the selected deposits as minor! */
     if (GNUNET_OK !=
         db_plugin->start (db_plugin->cls,
                           session))
@@ -1168,41 +1322,48 @@ run_aggregation (void *cls)
       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                   "Failed to start database transaction!\n");
       global_ret = GNUNET_SYSERR;
+      cleanup_au ();      
       GNUNET_SCHEDULER_shutdown ();
-      GNUNET_free_non_null (au->additional_rows);
-      if (NULL != au->wire)
-        json_decref (au->wire);
-      GNUNET_free (au);
-      au = NULL;
       return;
     }
     /* 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;
-    /* commit */
-    if (GNUNET_OK !=
-        db_plugin->commit (db_plugin->cls,
-                           session))
+    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)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                 "Serialization issue, trying again later!\n");
+      db_plugin->rollback (db_plugin->cls,
+                          session);
+      cleanup_au ();
+      /* start again */
+      task = GNUNET_SCHEDULER_add_now (&run_aggregation,
+                                      NULL);
+      return;
+    }
+    if (GNUNET_DB_STATUS_HARD_ERROR == qs)
     {
-      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                  "Failed to commit database transaction!\n");
+      db_plugin->rollback (db_plugin->cls,
+                          session);
+      cleanup_au ();
+      GNUNET_SCHEDULER_shutdown ();
+      return;
     }
-    GNUNET_free_non_null (au->additional_rows);
-    if (NULL != au->wire)
-      json_decref (au->wire);
-    GNUNET_free (au);
-    au = NULL;
+    /* commit */
+    (void) commit_or_warn (session);
+    cleanup_au ();
     /* start again */
     task = GNUNET_SCHEDULER_add_now (&run_aggregation,
                                      NULL);
@@ -1230,11 +1391,7 @@ run_aggregation (void *cls)
     GNUNET_break (0); /* why? how to best recover? */
     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;
+    cleanup_au ();
     /* start again */
     task = GNUNET_SCHEDULER_add_now (&run_aggregation,
                                      NULL);
@@ -1245,16 +1402,6 @@ run_aggregation (void *cls)
 
 
 /**
- * Execute the wire transfers that we have committed to
- * do.
- *
- * @param cls pointer to an `int` which we will return from main()
- */
-static void
-run_transfers (void *cls);
-
-
-/**
  * Function to be called with the prepared transfer data.
  *
  * @param cls NULL
@@ -1267,8 +1414,10 @@ prepare_cb (void *cls,
             size_t buf_size)
 {
   struct TALER_EXCHANGEDB_Session *session = au->session;
+  enum GNUNET_DB_QueryStatus qs;
 
   GNUNET_free_non_null (au->additional_rows);
+  au->additional_rows = NULL;
   if (NULL == buf)
   {
     GNUNET_break (0); /* why? how to best recover? */
@@ -1277,91 +1426,83 @@ prepare_cb (void *cls,
     /* start again */
     task = GNUNET_SCHEDULER_add_now (&run_aggregation,
                                      NULL);
-    if (NULL != au->wire)
-    {
-      json_decref (au->wire);
-      au->wire = NULL;
-    }
-    GNUNET_free (au);
-    au = NULL;
+    cleanup_au ();
     return;
   }
 
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+             "Storing %u bytes of wire prepare data\n",
+             (unsigned int) buf_size);
   /* Commit our intention to execute the wire transfer! */
-  if (GNUNET_OK !=
-      db_plugin->wire_prepare_data_insert (db_plugin->cls,
-                                           session,
-                                           au->wp->type,
-                                           buf,
-                                           buf_size))
+  qs = db_plugin->wire_prepare_data_insert (db_plugin->cls,
+                                           session,
+                                           au->wp->type,
+                                           buf,
+                                           buf_size);
+  /* Commit the WTID data to 'wire_out' to finally satisfy aggregation
+     table constraints */
+  if (qs >= 0)
+    qs = db_plugin->store_wire_transfer_out (db_plugin->cls,
+                                            session,
+                                            au->execution_time,
+                                            &au->wtid,
+                                            au->wire,
+                                            &au->final_amount);
+  cleanup_au ();
+  if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
   {
-    GNUNET_break (0); /* why? how to best recover? */
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "Serialization issue for prepared wire data; trying again 
later!\n");
     db_plugin->rollback (db_plugin->cls,
                          session);
     /* start again */
     task = GNUNET_SCHEDULER_add_now (&run_aggregation,
                                      NULL);
-    if (NULL != au->wire)
-    {
-      json_decref (au->wire);
-      au->wire = NULL;
-    }
-    GNUNET_free (au);
-    au = NULL;
     return;
   }
-
-  /* Commit the WTID data to 'wire_out' to finally satisfy aggregation
-     table constraints */
-  if (GNUNET_OK !=
-      db_plugin->store_wire_transfer_out (db_plugin->cls,
-                                          session,
-                                          au->execution_time,
-                                          &au->wtid,
-                                          au->wire,
-                                          &au->final_amount))
+  if (GNUNET_DB_STATUS_HARD_ERROR == qs)
   {
-    GNUNET_break (0); /* why? how to best recover? */
+    GNUNET_break (0);
     db_plugin->rollback (db_plugin->cls,
                          session);
-    /* start again */
-    task = GNUNET_SCHEDULER_add_now (&run_aggregation,
-                                     NULL);
-    if (NULL != au->wire)
-    {
-      json_decref (au->wire);
-      au->wire = NULL;
-    }
-    GNUNET_free (au);
-    au = NULL;
+    /* die hard */
+    global_ret = GNUNET_SYSERR;
+    GNUNET_SCHEDULER_shutdown ();
     return;
   }
-  if (NULL != au->wire)
-  {
-    json_decref (au->wire);
-    au->wire = NULL;
-  }
-  GNUNET_free (au);
-  au = NULL;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Stored wire transfer out instructions\n");
 
   /* Now we can finally commit the overall transaction, as we are
      again consistent if all of this passes. */
-  if (GNUNET_OK !=
-      db_plugin->commit (db_plugin->cls,
-                         session))
+  switch (commit_or_warn (session))
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                "Failed to commit database transaction!\n");
+  case GNUNET_DB_STATUS_SOFT_ERROR:
     /* try again */
+    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                "Commit issue for prepared wire data; trying again later!\n");
     task = GNUNET_SCHEDULER_add_now (&run_aggregation,
                                      NULL);
     return;
+  case GNUNET_DB_STATUS_HARD_ERROR:
+    GNUNET_break (0);
+    global_ret = GNUNET_SYSERR;
+    GNUNET_SCHEDULER_shutdown ();
+    return;
+  case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "Preparation complete, switching to transfer mode\n");
+    /* run alternative task: actually do wire transfer! */
+    task = GNUNET_SCHEDULER_add_now (&run_transfers,
+                                     NULL);
+    return;
+  default:
+    GNUNET_break (0);
+    global_ret = GNUNET_SYSERR;
+    GNUNET_SCHEDULER_shutdown ();
+    return;
   }
-  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              "Preparation complete, switching to transfer mode\n");
-  /* run alternative task: actually do wire transfer! */
-  task = GNUNET_SCHEDULER_add_now (&run_transfers,
-                                   NULL);
 }
 
 
@@ -1380,6 +1521,7 @@ wire_confirm_cb (void *cls,
                  const char *emsg)
 {
   struct TALER_EXCHANGEDB_Session *session = wpd->session;
+  enum GNUNET_DB_QueryStatus qs;
 
   wpd->eh = NULL;
   if (GNUNET_SYSERR == success)
@@ -1395,40 +1537,57 @@ wire_confirm_cb (void *cls,
     wpd = NULL;
     return;
   }
-  if (GNUNET_OK !=
-      db_plugin->wire_prepare_data_mark_finished (db_plugin->cls,
-                                                  session,
-                                                  wpd->row_id))
+  qs = db_plugin->wire_prepare_data_mark_finished (db_plugin->cls,
+                                                  session,
+                                                  wpd->row_id);
+  if (0 >= qs)
   {
-    GNUNET_break (0); /* why!? */
+    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
     db_plugin->rollback (db_plugin->cls,
                          session);
-    global_ret = GNUNET_SYSERR;
-    GNUNET_SCHEDULER_shutdown ();
+    if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+    {
+      /* try again */
+      task = GNUNET_SCHEDULER_add_now (&run_aggregation,
+                                      NULL);
+    }
+    else
+    {
+      global_ret = GNUNET_SYSERR;
+      GNUNET_SCHEDULER_shutdown ();
+    }
     GNUNET_free (wpd);
     wpd = NULL;
     return;
   }
   GNUNET_free (wpd);
   wpd = NULL;
-  if (GNUNET_OK !=
-      db_plugin->commit (db_plugin->cls,
-                         session))
+  switch (commit_or_warn (session))
   {
-    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                "Failed to commit database transaction!\n");
+  case GNUNET_DB_STATUS_SOFT_ERROR:
     /* try again */
     task = GNUNET_SCHEDULER_add_now (&run_aggregation,
                                      NULL);
     return;
+  case GNUNET_DB_STATUS_HARD_ERROR:
+    GNUNET_break (0);
+    global_ret = GNUNET_SYSERR;
+    GNUNET_SCHEDULER_shutdown ();
+    return;
+  case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "Wire transfer complete\n");
+    /* continue with #run_transfers(), just to guard
+       against the unlikely case that there are more. */
+    task = GNUNET_SCHEDULER_add_now (&run_transfers,
+                                     NULL);
+    return;
+  default:
+    GNUNET_break (0);
+    global_ret = GNUNET_SYSERR;
+    GNUNET_SCHEDULER_shutdown ();
+    return;
   }
-  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              "Wire transfer complete\n");
-  /* continue with #run_transfers(), just to guard
-     against the unlikely case that there are more. */
-  task = GNUNET_SCHEDULER_add_now (&run_transfers,
-                                   NULL);
-
 }
 
 
@@ -1481,12 +1640,12 @@ wire_prepare_cb (void *cls,
 static void
 run_transfers (void *cls)
 {
-  int ret;
+  enum GNUNET_DB_QueryStatus qs;
   struct TALER_EXCHANGEDB_Session *session;
   const struct GNUNET_SCHEDULER_TaskContext *tc;
 
   task = NULL;
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
               "Checking for pending wire transfers\n");
   tc = GNUNET_SCHEDULER_get_task_context ();
   if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
@@ -1511,35 +1670,39 @@ run_transfers (void *cls)
   }
   wpd = GNUNET_new (struct WirePrepareData);
   wpd->session = session;
-  ret = db_plugin->wire_prepare_data_get (db_plugin->cls,
-                                          session,
-                                          &wire_prepare_cb,
-                                          NULL);
-  if (GNUNET_SYSERR == ret)
+  qs = db_plugin->wire_prepare_data_get (db_plugin->cls,
+                                        session,
+                                        &wire_prepare_cb,
+                                        NULL);
+  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
+    return;  /* continues in #wire_prepare_cb() */
+  db_plugin->rollback (db_plugin->cls,
+                      session);
+  GNUNET_free (wpd);
+  wpd = NULL;
+  switch (qs)
   {
-    GNUNET_break (0); /* why? how to best recover? */
-    db_plugin->rollback (db_plugin->cls,
-                         session);
+  case GNUNET_DB_STATUS_HARD_ERROR:
+    GNUNET_break (0);
     global_ret = GNUNET_SYSERR;
     GNUNET_SCHEDULER_shutdown ();
-    GNUNET_free (wpd);
-    wpd = NULL;
     return;
-  }
-  if (GNUNET_NO == ret)
-  {
+  case GNUNET_DB_STATUS_SOFT_ERROR:
+    /* try again */
+    task = GNUNET_SCHEDULER_add_now (&run_transfers,
+                                    NULL);      
+    return;
+  case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
     /* no more prepared wire transfers, go back to aggregation! */
     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
                 "No more pending wire transfers, starting aggregation\n");
-    db_plugin->rollback (db_plugin->cls,
-                         session);
     task = GNUNET_SCHEDULER_add_now (&run_aggregation,
                                      NULL);
-    GNUNET_free (wpd);
-    wpd = NULL;
     return;
+  case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+    /* should be impossible */
+    GNUNET_assert (0);
   }
-  /* otherwise, continues in #wire_prepare_cb() */
 }
 
 
diff --git a/src/exchange/taler-exchange-httpd.c 
b/src/exchange/taler-exchange-httpd.c
index dbc270b..1a4d286 100644
--- a/src/exchange/taler-exchange-httpd.c
+++ b/src/exchange/taler-exchange-httpd.c
@@ -31,12 +31,16 @@
 #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"
-#include "taler-exchange-httpd_tracking.h"
+#include "taler-exchange-httpd_refresh_link.h"
+#include "taler-exchange-httpd_refresh_melt.h"
+#include "taler-exchange-httpd_refresh_reveal.h"
+#include "taler-exchange-httpd_track_transfer.h"
+#include "taler-exchange-httpd_track_transaction.h"
 #include "taler-exchange-httpd_keystate.h"
+#include "taler-exchange-httpd_wire.h"
 #if HAVE_DEVELOPER
 #include "taler-exchange-httpd_test.h"
 #endif
diff --git a/src/exchange/taler-exchange-httpd_admin.c 
b/src/exchange/taler-exchange-httpd_admin.c
index 8bb4b49..83bfedf 100644
--- a/src/exchange/taler-exchange-httpd_admin.c
+++ b/src/exchange/taler-exchange-httpd_admin.c
@@ -27,6 +27,99 @@
 #include "taler-exchange-httpd_validation.h"
 
 
+/**
+ * Closure for #admin_add_incoming_transaction()
+ */
+struct AddIncomingContext
+{
+  /**
+   * public key of the reserve
+   */
+  struct TALER_ReservePublicKeyP reserve_pub;
+
+  /**
+   * amount to add to the reserve
+   */
+  struct TALER_Amount amount;
+
+  /**
+   * When did we receive the wire transfer
+   */
+  struct GNUNET_TIME_Absolute execution_time;
+
+  /**
+   * which account send the funds
+   */
+  json_t *sender_account_details;
+
+  /**
+   * Information that uniquely identifies the transfer
+   */
+  json_t *transfer_details;
+
+  /**
+   * Set to the transaction status.
+   */
+  enum GNUNET_DB_QueryStatus qs;
+};
+
+
+/** 
+ * Add an incoming transaction to the database.  Checks if the
+ * transaction is fresh (not a duplicate) and if so adds it to
+ * the database.
+ *
+ * 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 closure with the `struct AddIncomingContext *`
+ * @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
+admin_add_incoming_transaction (void *cls,
+                               struct MHD_Connection *connection,
+                               struct TALER_EXCHANGEDB_Session *session,
+                               int *mhd_ret)
+{
+  struct AddIncomingContext *aic = cls;
+  void *json_str;
+
+  json_str = json_dumps (aic->transfer_details,
+                         JSON_INDENT(2));
+  if (NULL == json_str)
+  {
+    GNUNET_break (0);
+    *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+                                                    
TALER_EC_PARSER_OUT_OF_MEMORY);
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  }
+  aic->qs = TEH_plugin->reserves_in_insert (TEH_plugin->cls,
+                                           session,
+                                           &aic->reserve_pub,
+                                           &aic->amount,
+                                           aic->execution_time,
+                                           aic->sender_account_details,
+                                           json_str,
+                                           strlen (json_str));
+  free (json_str);
+  
+  if (GNUNET_DB_STATUS_HARD_ERROR == aic->qs)
+  {
+    GNUNET_break (0);
+    *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+                                                    
TALER_EC_ADMIN_ADD_INCOMING_DB_STORE);
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  }
+  return aic->qs;
+}
+
 
 /**
  * Handle a "/admin/add/incoming" request.  Parses the
@@ -47,23 +140,20 @@ TEH_ADMIN_handler_admin_add_incoming (struct 
TEH_RequestHandler *rh,
                                       const char *upload_data,
                                       size_t *upload_data_size)
 {
-  struct TALER_ReservePublicKeyP reserve_pub;
-  struct TALER_Amount amount;
-  struct GNUNET_TIME_Absolute at;
+  struct AddIncomingContext aic;
   enum TALER_ErrorCode ec;
   char *emsg;
-  json_t *sender_account_details;
-  json_t *transfer_details;
   json_t *root;
   struct GNUNET_JSON_Specification spec[] = {
-    GNUNET_JSON_spec_fixed_auto ("reserve_pub", &reserve_pub),
-    TALER_JSON_spec_amount ("amount", &amount),
-    GNUNET_JSON_spec_absolute_time ("execution_date", &at),
-    GNUNET_JSON_spec_json ("sender_account_details", &sender_account_details),
-    GNUNET_JSON_spec_json ("transfer_details", &transfer_details),
+    GNUNET_JSON_spec_fixed_auto ("reserve_pub", &aic.reserve_pub),
+    TALER_JSON_spec_amount ("amount", &aic.amount),
+    GNUNET_JSON_spec_absolute_time ("execution_date", &aic.execution_time),
+    GNUNET_JSON_spec_json ("sender_account_details", 
&aic.sender_account_details),
+    GNUNET_JSON_spec_json ("transfer_details", &aic.transfer_details),
     GNUNET_JSON_spec_end ()
   };
   int res;
+  int mhd_ret;
 
   res = TEH_PARSE_post_json (connection,
                              connection_cls,
@@ -72,7 +162,8 @@ TEH_ADMIN_handler_admin_add_incoming (struct 
TEH_RequestHandler *rh,
                              &root);
   if (GNUNET_SYSERR == res)
     return MHD_NO;
-  if ( (GNUNET_NO == res) || (NULL == root) )
+  if ( (GNUNET_NO == res) ||
+       (NULL == root) )
     return MHD_YES;
   res = TEH_PARSE_json_data (connection,
                              root,
@@ -85,37 +176,43 @@ TEH_ADMIN_handler_admin_add_incoming (struct 
TEH_RequestHandler *rh,
     return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
   }
   if (TALER_EC_NONE !=
-      (ec = TEH_json_validate_wireformat (sender_account_details,
+      (ec = TEH_json_validate_wireformat (aic.sender_account_details,
                                           GNUNET_NO,
                                           &emsg)))
   {
     GNUNET_JSON_parse_free (spec);
-    res = TEH_RESPONSE_reply_external_error (connection,
-                                             ec,
-                                             emsg);
+    mhd_ret = TEH_RESPONSE_reply_external_error (connection,
+                                                ec,
+                                                emsg);
     GNUNET_free (emsg);
-    return res;
+    return mhd_ret;
   }
-  if (0 != strcasecmp (amount.currency,
+  if (0 != strcasecmp (aic.amount.currency,
                        TEH_exchange_currency_string))
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                 "Exchange uses currency `%s', but /admin/add/incoming tried to 
use currency `%s'\n",
                 TEH_exchange_currency_string,
-                amount.currency);
+                aic.amount.currency);
     GNUNET_JSON_parse_free (spec);
     return TEH_RESPONSE_reply_arg_invalid (connection,
                                           
TALER_EC_ADMIN_ADD_INCOMING_CURRENCY_UNSUPPORTED,
                                            "amount:currency");
   }
-  res = TEH_DB_execute_admin_add_incoming (connection,
-                                           &reserve_pub,
-                                           &amount,
-                                           at,
-                                           sender_account_details,
-                                           transfer_details);
+  res = TEH_DB_run_transaction (connection,
+                               &mhd_ret,
+                               &admin_add_incoming_transaction,
+                               &aic);
   GNUNET_JSON_parse_free (spec);
-  return res;
+  if (GNUNET_OK != res)
+    return mhd_ret;
+  return TEH_RESPONSE_reply_json_pack (connection,
+                                       MHD_HTTP_OK,
+                                       "{s:s}",
+                                       "status",
+                                       (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == 
aic.qs)
+                                       ? "NEW"
+                                       : "DUP");
 }
 
 /* end of taler-exchange-httpd_admin.c */
diff --git a/src/exchange/taler-exchange-httpd_db.c 
b/src/exchange/taler-exchange-httpd_db.c
index 51e1ef5..f2e1f7b 100644
--- a/src/exchange/taler-exchange-httpd_db.c
+++ b/src/exchange/taler-exchange-httpd_db.c
@@ -15,7 +15,7 @@
 */
 /**
  * @file taler-exchange-httpd_db.c
- * @brief High-level (transactional-layer) database operations for the 
exchange.
+ * @brief Generic database operations for the exchange.
  * @author Christian Grothoff
  */
 #include "platform.h"
@@ -32,84 +32,84 @@
  */
 #define MAX_TRANSACTION_COMMIT_RETRIES 3
 
-/**
- * Code to begin a transaction, must be inline as we define a block
- * that ends with #COMMIT_TRANSACTION() within which we perform a number
- * of retries.  Note that this code may call "return" internally, so
- * it must be called within a function where any cleanup will be done
- * by the caller. Furthermore, the function's return value must
- * match that of a #TEH_RESPONSE_reply_internal_db_error() status code.
- *
- * @param session session handle
- * @param connection connection handle
- */
-#define START_TRANSACTION(session,connection)                 \
-{ /* start new scope, will be ended by COMMIT_TRANSACTION() */\
-  unsigned int transaction_retries = 0;                       \
-  int transaction_commit_result;                              \
-transaction_start_label: /* we will use goto for retries */   \
-  if (GNUNET_OK !=                                            \
-      TEH_plugin->start (TEH_plugin->cls,                     \
-                         session))                            \
-  {                                                           \
-    GNUNET_break (0);                                         \
-    return TEH_RESPONSE_reply_internal_db_error (connection, \
-                                                TALER_EC_DB_START_FAILED);     
     \
-  }
 
 /**
- * Code to conclude a transaction, dual to #START_TRANSACTION().  Note
- * that this code may call "return" internally, so it must be called
- * within a function where any cleanup will be done by the caller.
- * Furthermore, the function's return value must match that of a
- * #TEH_RESPONSE_reply_internal_db_error() status code.
- *
- * @param session session handle
- * @param connection connection handle
+ * 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
  */
-#define COMMIT_TRANSACTION(session,connection)                             \
-  transaction_commit_result =                                              \
-    TEH_plugin->commit (TEH_plugin->cls,                                   \
-                        session);                                          \
-  if (GNUNET_SYSERR == transaction_commit_result)                          \
-  {                                                                        \
-    TALER_LOG_WARNING ("Transaction commit failed in %s\n", __FUNCTION__); \
-    return TEH_RESPONSE_reply_commit_error (connection, \
-                                           TALER_EC_DB_COMMIT_FAILED_HARD); \
-  }                                                       \
-  if (GNUNET_NO == transaction_commit_result)                              \
-  {                                                                        \
-    TALER_LOG_WARNING ("Transaction commit failed in %s\n", __FUNCTION__); \
-    if (transaction_retries++ <= MAX_TRANSACTION_COMMIT_RETRIES)           \
-      goto transaction_start_label;                                        \
-    TALER_LOG_WARNING ("Transaction commit failed %u times in %s\n",       \
-                       transaction_retries,                                \
-                       __FUNCTION__);                                      \
-    return TEH_RESPONSE_reply_commit_error (connection, \
-                                           
TALER_EC_DB_COMMIT_FAILED_ON_RETRY);                                \
-  }                                                                        \
-} /* end of scope opened by BEGIN_TRANSACTION */
+int
+TEH_DB_run_transaction (struct MHD_Connection *connection,
+                       int *mhd_ret,
+                       TEH_DB_TransactionCallback cb,
+                       void *cb_cls)
+{
+  struct TALER_EXCHANGEDB_Session *session;
 
+  if (NULL != mhd_ret)
+    *mhd_ret = -1; /* invalid value */
+  if (NULL == (session = TEH_plugin->get_session (TEH_plugin->cls)))
+  {
+    GNUNET_break (0);
+    if (NULL != mhd_ret)
+      *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;
 
-/**
- * Code to include to retry a transaction, must only be used in between
- * #START_TRANSACTION and #COMMIT_TRANSACTION.
- *
- * @param session session handle
- * @param connection connection handle
- */
-#define RETRY_TRANSACTION(session,connection)                                  
  \
-  do {                                                                         
  \
-    TEH_plugin->rollback (TEH_plugin->cls,                                     
  \
-                          session);                                            
  \
-    if (transaction_retries++ <= MAX_TRANSACTION_COMMIT_RETRIES)               
  \
-      goto transaction_start_label;                                            
  \
-    TALER_LOG_WARNING ("Transaction commit failed %u times in %s\n",           
  \
-                       transaction_retries,                                    
  \
-                       __FUNCTION__);                                          
  \
-    return TEH_RESPONSE_reply_commit_error (connection,                        
  \
-                                           
TALER_EC_DB_COMMIT_FAILED_ON_RETRY); \
-  } while (0)
+    if (GNUNET_OK !=                                            
+       TEH_plugin->start (TEH_plugin->cls,                     
+                          session))                            
+    {                                      
+      GNUNET_break (0);
+      if (NULL != mhd_ret)
+       *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)
+    {
+      if (NULL != mhd_ret)
+       *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 ( (NULL == mhd_ret) ||
+                   (-1 == *mhd_ret) );
+    if (0 <= qs)
+      return GNUNET_OK;
+  }
+  TALER_LOG_WARNING ("Transaction commit failed %u times\n",
+                    MAX_TRANSACTION_COMMIT_RETRIES);
+  if (NULL != mhd_ret)
+    *mhd_ret = TEH_RESPONSE_reply_commit_error (connection,
+                                               
TALER_EC_DB_COMMIT_FAILED_ON_RETRY);
+  return GNUNET_SYSERR;
+}
 
 
 /**
@@ -119,13 +119,14 @@ transaction_start_label: /* we will use goto for retries 
*/   \
  *
  * @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)
+// FIXME: maybe move to another module, i.e. exchangedb???
+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;
@@ -206,2259 +207,4 @@ 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.
- *
- * @param connection the MHD connection to handle
- * @param refund refund details
- * @return MHD result code
- */
-int
-TEH_DB_execute_refund (struct MHD_Connection *connection,
-                       const struct TALER_EXCHANGEDB_Refund *refund)
-{
-  struct TALER_EXCHANGEDB_Session *session;
-  struct TALER_EXCHANGEDB_TransactionList *tl;
-  struct TALER_EXCHANGEDB_TransactionList *tlp;
-  const struct TALER_EXCHANGEDB_Deposit *dep;
-  const struct TALER_EXCHANGEDB_Refund *ref;
-  struct TEH_KS_StateHandle *mks;
-  struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki;
-  struct TALER_Amount expect_fee;
-  int ret;
-  int deposit_found;
-  int refund_found;
-  int done;
-  int fee_cmp;
-
-  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);
-  }
-  dep = NULL;
-  ref = NULL;
-  START_TRANSACTION (session, connection);
-  tl = TEH_plugin->get_coin_transactions (TEH_plugin->cls,
-                                          session,
-                                          &refund->coin.coin_pub);
-  if (NULL == tl)
-  {
-    TEH_plugin->rollback (TEH_plugin->cls,
-                          session);
-    return TEH_RESPONSE_reply_refund_failure (connection,
-                                             MHD_HTTP_NOT_FOUND,
-                                             TALER_EC_REFUND_COIN_NOT_FOUND);
-  }
-  deposit_found = GNUNET_NO;
-  refund_found = GNUNET_NO;
-  for (tlp = tl; NULL != tlp; tlp = tlp->next)
-  {
-    switch (tlp->type)
-    {
-    case TALER_EXCHANGEDB_TT_DEPOSIT:
-      if (GNUNET_NO == deposit_found)
-      {
-        if ( (0 == memcmp (&tlp->details.deposit->merchant_pub,
-                           &refund->merchant_pub,
-                           sizeof (struct TALER_MerchantPublicKeyP))) &&
-             (0 == memcmp (&tlp->details.deposit->h_contract_terms,
-                           &refund->h_contract_terms,
-                           sizeof (struct GNUNET_HashCode))) )
-        {
-          dep = tlp->details.deposit;
-          deposit_found = GNUNET_YES;
-          break;
-        }
-      }
-      break;
-    case TALER_EXCHANGEDB_TT_REFRESH_MELT:
-      /* Melts cannot be refunded, ignore here */
-      break;
-    case TALER_EXCHANGEDB_TT_REFUND:
-      if (GNUNET_NO == refund_found)
-      {
-        /* First, check if existing refund request is identical */
-        if ( (0 == memcmp (&tlp->details.refund->merchant_pub,
-                           &refund->merchant_pub,
-                           sizeof (struct TALER_MerchantPublicKeyP))) &&
-             (0 == memcmp (&tlp->details.refund->h_contract_terms,
-                           &refund->h_contract_terms,
-                           sizeof (struct GNUNET_HashCode))) &&
-             (tlp->details.refund->rtransaction_id == refund->rtransaction_id) 
)
-        {
-          ref = tlp->details.refund;
-          refund_found = GNUNET_YES;
-          break;
-        }
-        /* Second, check if existing refund request conflicts */
-        if ( (0 == memcmp (&tlp->details.refund->merchant_pub,
-                           &refund->merchant_pub,
-                           sizeof (struct TALER_MerchantPublicKeyP))) &&
-             (0 == memcmp (&tlp->details.refund->h_contract_terms,
-                           &refund->h_contract_terms,
-                           sizeof (struct GNUNET_HashCode))) &&
-             (tlp->details.refund->rtransaction_id != refund->rtransaction_id) 
)
-        {
-          GNUNET_break_op (0); /* conflicting refund found */
-          refund_found = GNUNET_SYSERR;
-          /* NOTE: Alternatively we could total up all existing
-             refunds and check if the sum still permits the
-             refund requested (thus allowing multiple, partial
-             refunds). Fow now, we keep it simple. */
-          break;
-        }
-      }
-      break;
-    case TALER_EXCHANGEDB_TT_PAYBACK:
-      /* Paybacks cannot be refunded, ignore here */
-      break;
-    }
-  }
-  /* handle if deposit was NOT found */
-  if (GNUNET_NO == deposit_found)
-  {
-    TEH_plugin->rollback (TEH_plugin->cls,
-                          session);
-    TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
-                                            tl);
-    return TEH_RESPONSE_reply_transaction_unknown (connection,
-                                                  
TALER_EC_REFUND_DEPOSIT_NOT_FOUND);
-  }
-  /* handle if conflicting refund found */
-  if (GNUNET_SYSERR == refund_found)
-  {
-    TEH_plugin->rollback (TEH_plugin->cls,
-                          session);
-    ret = TEH_RESPONSE_reply_refund_conflict (connection,
-                                              tl);
-    TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
-                                            tl);
-    return ret;
-  }
-  /* handle if identical refund found */
-  if (GNUNET_YES == refund_found)
-  {
-    /* /refund already done, simply re-transmit confirmation */
-    TEH_plugin->rollback (TEH_plugin->cls,
-                          session);
-    ret = TEH_RESPONSE_reply_refund_success (connection,
-                                             ref);
-    TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
-                                            tl);
-    return ret;
-  }
-
-  /* check currency is compatible */
-  if ( (GNUNET_YES !=
-        TALER_amount_cmp_currency (&refund->refund_amount,
-                                   &dep->amount_with_fee)) ||
-       (GNUNET_YES !=
-        TALER_amount_cmp_currency (&refund->refund_fee,
-                                   &dep->deposit_fee)) )
-  {
-    GNUNET_break_op (0); /* currency missmatch */
-    TEH_plugin->rollback (TEH_plugin->cls,
-                          session);
-    return TEH_RESPONSE_reply_refund_failure (connection,
-                                              MHD_HTTP_PRECONDITION_FAILED,
-                                             
TALER_EC_REFUND_CURRENCY_MISSMATCH);
-  }
-
-  /* check if we already send the money for the /deposit */
-  done = TEH_plugin->test_deposit_done (TEH_plugin->cls,
-                                        session,
-                                        dep);
-  if (GNUNET_SYSERR == done)
-  {
-    /* Internal error, we first had the deposit in the history,
-       but now it is gone? */
-    GNUNET_break (0);
-    TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
-                                            tl);
-    TEH_plugin->rollback (TEH_plugin->cls,
-                          session);
-    return TEH_RESPONSE_reply_internal_error (connection,
-                                             TALER_EC_REFUND_DB_INCONSISTENT,
-                                              "database inconsistent");
-  }
-  if (GNUNET_YES == done)
-  {
-    /* money was already transferred to merchant, can no longer refund */
-    TEH_plugin->rollback (TEH_plugin->cls,
-                          session);
-    TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
-                                            tl);
-    return TEH_RESPONSE_reply_refund_failure (connection,
-                                              MHD_HTTP_GONE,
-                                             
TALER_EC_REFUND_MERCHANT_ALREADY_PAID);
-  }
-
-  /* check refund amount is sufficiently low */
-  if (1 == TALER_amount_cmp (&refund->refund_amount,
-                             &dep->amount_with_fee) )
-  {
-    GNUNET_break_op (0); /* cannot refund more than original value */
-    TEH_plugin->rollback (TEH_plugin->cls,
-                          session);
-    TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
-                                            tl);
-    return TEH_RESPONSE_reply_refund_failure (connection,
-                                              MHD_HTTP_PRECONDITION_FAILED,
-                                             
TALER_EC_REFUND_INSUFFICIENT_FUNDS);
-  }
-
-  /* Check refund fee matches fee of denomination key! */
-  mks = TEH_KS_acquire ();
-  dki = TEH_KS_denomination_key_lookup (mks,
-                                        &dep->coin.denom_pub,
-                                       TEH_KS_DKU_DEPOSIT);
-  if (NULL == dki)
-  {
-    /* DKI not found, but we do have a coin with this DK in our database;
-       not good... */
-    GNUNET_break (0);
-    TEH_plugin->rollback (TEH_plugin->cls,
-                          session);
-    TEH_KS_release (mks);
-    TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
-                                            tl);
-    return TEH_RESPONSE_reply_internal_error (connection,
-                                             
TALER_EC_REFUND_DENOMINATION_KEY_NOT_FOUND,
-                                              "denomination key not found");
-  }
-  TALER_amount_ntoh (&expect_fee,
-                     &dki->issue.properties.fee_refund);
-  fee_cmp = TALER_amount_cmp (&refund->refund_fee,
-                              &expect_fee);
-  TEH_KS_release (mks);
-
-  if (-1 == fee_cmp)
-  {
-    TEH_plugin->rollback (TEH_plugin->cls,
-                          session);
-    TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
-                                            tl);
-    return TEH_RESPONSE_reply_arg_invalid (connection,
-                                          TALER_EC_REFUND_FEE_TOO_LOW,
-                                           "refund_fee");
-  }
-  if (1 == fee_cmp)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                "Refund fee proposed by merchant is higher than necessary.\n");
-  }
-  TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
-                                          tl);
-
-  /* Finally, store new refund data */
-  if (GNUNET_OK !=
-      TEH_plugin->insert_refund (TEH_plugin->cls,
-                                 session,
-                                 refund))
-  {
-    TALER_LOG_WARNING ("Failed to store /refund information in database\n");
-    TEH_plugin->rollback (TEH_plugin->cls,
-                          session);
-    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))
-  {
-    /* did not encounter any wire transfer operations, how can we have a 
reserve? */
-    GNUNET_break (0);
-    TEH_plugin->rollback (TEH_plugin->cls,
-                          session);
-    return TEH_RESPONSE_reply_internal_db_error (connection,
-                                                
TALER_EC_WITHDRAW_RESERVE_WITHOUT_WIRE_TRANSFER);
-  }
-  if (0 == (res & 2))
-  {
-    /* did not encounter any withdraw operations, set to zero */
-    TALER_amount_get_zero (deposit_total.currency,
-                           &withdraw_total);
-  }
-  /* All reserve balances should be non-negative */
-  if (GNUNET_SYSERR ==
-      TALER_amount_subtract (&balance,
-                             &deposit_total,
-                             &withdraw_total))
-  {
-    GNUNET_break (0); /* database inconsistent */
-    TEH_plugin->rollback (TEH_plugin->cls,
-                          session);
-    return TEH_RESPONSE_reply_internal_db_error (connection,
-                                                 
TALER_EC_WITHDRAW_RESERVE_HISTORY_IMPOSSIBLE);
-  }
-  if (0 < TALER_amount_cmp (&amount_required,
-                            &balance))
-  {
-    TEH_plugin->rollback (TEH_plugin->cls,
-                          session);
-    res = TEH_RESPONSE_reply_reserve_withdraw_insufficient_funds (connection,
-                                                                  rh);
-    TEH_plugin->free_reserve_history (TEH_plugin->cls,
-                                      rh);
-    return res;
-  }
-  TEH_plugin->free_reserve_history (TEH_plugin->cls,
-                                    rh);
-
-  /* Balance is good, sign the coin! */
-  denom_sig->rsa_signature
-    = GNUNET_CRYPTO_rsa_sign_blinded (dki->denom_priv.rsa_private_key,
-                                      blinded_msg,
-                                      blinded_msg_len);
-  if (NULL == denom_sig->rsa_signature)
-  {
-    GNUNET_break (0);
-    TEH_plugin->rollback (TEH_plugin->cls,
-                          session);
-    return TEH_RESPONSE_reply_internal_error (connection,
-                                             
TALER_EC_WITHDRAW_SIGNATURE_FAILED,
-                                              "Internal error");
-  }
-  collectable.sig = *denom_sig;
-  collectable.denom_pub = *denomination_pub;
-  collectable.amount_with_fee = amount_required;
-  collectable.withdraw_fee = fee_withdraw;
-  collectable.reserve_pub = *reserve;
-  collectable.h_coin_envelope = *h_blind;
-  collectable.reserve_sig = *signature;
-  ret = TEH_plugin->insert_withdraw_info (TEH_plugin->cls,
-                                          session,
-                                          &collectable);
-  if (GNUNET_SYSERR == ret)
-  {
-    GNUNET_break (0);
-    TEH_plugin->rollback (TEH_plugin->cls,
-                          session);
-    return TEH_RESPONSE_reply_internal_db_error (connection,
-                                                
TALER_EC_WITHDRAW_DB_STORE_ERROR);
-  }
-  if (GNUNET_NO == ret)
-    RETRY_TRANSACTION(session, connection);
-  COMMIT_TRANSACTION (session, connection);
-
-  return TEH_RESPONSE_reply_reserve_withdraw_success (connection,
-                                                      &collectable);
-}
-
-
-
-/**
- * Execute a "/reserve/withdraw". Given a reserve and a properly signed
- * request to withdraw a coin, check the balance of the reserve and
- * if it is sufficient, store the request and return the signed
- * blinded envelope.
- *
- * @param connection the MHD connection to handle
- * @param reserve public key of the reserve
- * @param denomination_pub public key of the denomination requested
- * @param blinded_msg blinded message to be signed
- * @param blinded_msg_len number of bytes in @a blinded_msg
- * @param signature signature over the withdraw request, to be stored in DB
- * @return MHD result code
- */
-int
-TEH_DB_execute_reserve_withdraw (struct MHD_Connection *connection,
-                                 const struct TALER_ReservePublicKeyP *reserve,
-                                 const struct TALER_DenominationPublicKey 
*denomination_pub,
-                                 const char *blinded_msg,
-                                 size_t blinded_msg_len,
-                                 const struct TALER_ReserveSignatureP 
*signature)
-{
-  struct TALER_EXCHANGEDB_Session *session;
-  struct TEH_KS_StateHandle *key_state;
-  struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki;
-  struct TALER_EXCHANGEDB_CollectableBlindcoin collectable;
-  struct TALER_DenominationSignature denom_sig;
-  struct GNUNET_HashCode h_blind;
-  int res;
-
-  GNUNET_CRYPTO_hash (blinded_msg,
-                      blinded_msg_len,
-                      &h_blind);
-  if (NULL == (session = TEH_plugin->get_session (TEH_plugin->cls)))
-  {
-    GNUNET_break (0);
-    return TEH_RESPONSE_reply_internal_db_error (connection,
-                                                TALER_EC_DB_SETUP_FAILED);
-  }
-  res = TEH_plugin->get_withdraw_info (TEH_plugin->cls,
-                                       session,
-                                       &h_blind,
-                                       &collectable);
-  if (GNUNET_SYSERR == res)
-  {
-    GNUNET_break (0);
-    return TEH_RESPONSE_reply_internal_db_error (connection,
-                                                
TALER_EC_WITHDRAW_DB_FETCH_ERROR);
-  }
-
-  /* Don't sign again if we have already signed the coin */
-  if (GNUNET_YES == res)
-  {
-    res = TEH_RESPONSE_reply_reserve_withdraw_success (connection,
-                                                       &collectable);
-    GNUNET_CRYPTO_rsa_signature_free (collectable.sig.rsa_signature);
-    GNUNET_CRYPTO_rsa_public_key_free (collectable.denom_pub.rsa_public_key);
-    return res;
-  }
-  GNUNET_assert (GNUNET_NO == res);
-
-  /* FIXME: do we have to do this a second time here? */
-  key_state = TEH_KS_acquire ();
-  dki = TEH_KS_denomination_key_lookup (key_state,
-                                        denomination_pub,
-                                       TEH_KS_DKU_WITHDRAW);
-  if (NULL == dki)
-  {
-    TEH_KS_release (key_state);
-    return TEH_RESPONSE_reply_json_pack (connection,
-                                         MHD_HTTP_NOT_FOUND,
-                                         "{s:s, s:I}",
-                                         "error",
-                                         "Denomination not found",
-                                        "code",
-                                        (json_int_t) 
TALER_EC_WITHDRAW_DENOMINATION_KEY_NOT_FOUND);
-  }
-  denom_sig.rsa_signature = NULL;
-  res = execute_reserve_withdraw_transaction (connection,
-                                              session,
-                                              key_state,
-                                              reserve,
-                                              denomination_pub,
-                                              dki,
-                                              blinded_msg,
-                                              blinded_msg_len,
-                                              &h_blind,
-                                              signature,
-                                              &denom_sig);
-  if (NULL != denom_sig.rsa_signature)
-    GNUNET_CRYPTO_rsa_signature_free (denom_sig.rsa_signature);
-  TEH_KS_release (key_state);
-  return res;
-}
-
-
-/**
- * Parse coin melt requests from a JSON object and write them to
- * the database.
- *
- * @param connection the connection to send errors to
- * @param session the database connection
- * @param key_state the exchange's key state
- * @param session_hash hash identifying the refresh session
- * @param coin_details details about the coin being melted
- * @param[out] meltp on success, set to melt details
- * @return #GNUNET_OK on success,
- *         #GNUNET_NO if an error message was generated,
- *         #GNUNET_SYSERR on internal errors (no response generated)
- */
-static int
-refresh_check_melt (struct MHD_Connection *connection,
-                    struct TALER_EXCHANGEDB_Session *session,
-                    const struct TEH_KS_StateHandle *key_state,
-                    const struct GNUNET_HashCode *session_hash,
-                    const struct TEH_DB_MeltDetails *coin_details,
-                    struct TALER_EXCHANGEDB_RefreshMelt *meltp)
-{
-  struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dk;
-  struct TALER_EXCHANGEDB_DenominationKeyInformationP *dki;
-  struct TALER_EXCHANGEDB_TransactionList *tl;
-  struct TALER_Amount coin_value;
-  struct TALER_Amount coin_residual;
-  struct TALER_Amount spent;
-  int res;
-
-  dk = TEH_KS_denomination_key_lookup (key_state,
-                                       &coin_details->coin_info.denom_pub,
-                                       TEH_KS_DKU_DEPOSIT);
-  if (NULL == dk)
-    return (MHD_YES ==
-            TEH_RESPONSE_reply_internal_error (connection,
-                                              
TALER_EC_REFRESH_MELT_DB_DENOMINATION_KEY_NOT_FOUND,
-                                              "denomination key no longer 
available while executing transaction"))
-        ? GNUNET_NO : GNUNET_SYSERR;
-  dki = &dk->issue;
-  TALER_amount_ntoh (&coin_value,
-                     &dki->properties.value);
-  /* fee for THIS transaction; the melt amount includes the fee! */
-  spent = coin_details->melt_amount_with_fee;
-  /* add historic transaction costs of this coin */
-  tl = TEH_plugin->get_coin_transactions (TEH_plugin->cls,
-                                          session,
-                                          &coin_details->coin_info.coin_pub);
-  if (GNUNET_OK !=
-      calculate_transaction_list_totals (tl,
-                                         &spent,
-                                         &spent))
-  {
-    GNUNET_break (0);
-    TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
-                                            tl);
-    return (MHD_YES ==
-            TEH_RESPONSE_reply_internal_db_error (connection,
-                                                 
TALER_EC_REFRESH_MELT_COIN_HISTORY_COMPUTATION_FAILED))
-      ? GNUNET_NO : GNUNET_SYSERR;
-  }
-  /* Refuse to refresh when the coin's value is insufficient
-     for the cost of all transactions. */
-  if (TALER_amount_cmp (&coin_value,
-                        &spent) < 0)
-  {
-    GNUNET_assert (GNUNET_SYSERR !=
-                   TALER_amount_subtract (&coin_residual,
-                                          &spent,
-                                          
&coin_details->melt_amount_with_fee));
-    res = (MHD_YES ==
-           TEH_RESPONSE_reply_refresh_melt_insufficient_funds (connection,
-                                                               
&coin_details->coin_info.coin_pub,
-                                                               coin_value,
-                                                               tl,
-                                                               
coin_details->melt_amount_with_fee,
-                                                               coin_residual))
-        ? GNUNET_NO : GNUNET_SYSERR;
-    TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
-                                            tl);
-    return res;
-  }
-  TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
-                                          tl);
-
-  meltp->coin = coin_details->coin_info;
-  meltp->coin_sig = coin_details->melt_sig;
-  meltp->session_hash = *session_hash;
-  meltp->amount_with_fee = coin_details->melt_amount_with_fee;
-  meltp->melt_fee = coin_details->melt_fee;
-  return GNUNET_OK;
-}
-
-
-/**
- * Execute a "/refresh/melt".  We have been given a list of valid
- * coins and a request to melt them into the given
- * @a refresh_session_pub.  Check that the coins all have the
- * required value left and if so, store that they have been
- * melted and confirm the melting operation to the client.
- *
- * @param connection the MHD connection to handle
- * @param session_hash hash code of the session the coins are melted into
- * @param num_new_denoms number of entries in @a denom_pubs, size of 
y-dimension of @a commit_coin array
- * @param denom_pubs public keys of the coins we want to withdraw in the end
- * @param coin_melt_detail signature and (residual) value of the respective 
coin should be melted
- * @param commit_coin 2d array of coin commitments (what the exchange is to 
sign
- *                    once the "/refres/reveal" of cut and choose is done),
- *                    x-dimension must be #TALER_CNC_KAPPA
- * @param transfer_pubs array of transfer public keys (what the exchange is
- *                    to return via "/refresh/link" to enable linkage in the
- *                    future) of length #TALER_CNC_KAPPA
- * @return MHD result code
- */
-int
-TEH_DB_execute_refresh_melt (struct MHD_Connection *connection,
-                             const struct GNUNET_HashCode *session_hash,
-                             unsigned int num_new_denoms,
-                             const struct TALER_DenominationPublicKey 
*denom_pubs,
-                             const struct TEH_DB_MeltDetails *coin_melt_detail,
-                             struct TALER_EXCHANGEDB_RefreshCommitCoin *const* 
commit_coin,
-                             const struct TALER_TransferPublicKeyP 
*transfer_pubs)
-{
-  struct TEH_KS_StateHandle *key_state;
-  struct TALER_EXCHANGEDB_RefreshSession refresh_session;
-  struct TALER_EXCHANGEDB_Session *session;
-  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);
-  res = TEH_plugin->get_refresh_session (TEH_plugin->cls,
-                                         session,
-                                         session_hash,
-                                         &refresh_session);
-  if (GNUNET_YES == res)
-  {
-    TEH_plugin->rollback (TEH_plugin->cls,
-                          session);
-    res = TEH_RESPONSE_reply_refresh_melt_success (connection,
-                                                   session_hash,
-                                                   
refresh_session.noreveal_index);
-    return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
-  }
-  if (GNUNET_SYSERR == res)
-  {
-    TEH_plugin->rollback (TEH_plugin->cls,
-                          session);
-    return TEH_RESPONSE_reply_internal_db_error (connection,
-                                                
TALER_EC_REFRESH_MELT_DB_FETCH_ERROR);
-  }
-
-  /* store 'global' session data */
-  refresh_session.num_newcoins = num_new_denoms;
-  refresh_session.noreveal_index
-    = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_STRONG,
-                               TALER_CNC_KAPPA);
-  key_state = TEH_KS_acquire ();
-  if (GNUNET_OK !=
-      (res = refresh_check_melt (connection,
-                                 session,
-                                 key_state,
-                                 session_hash,
-                                 coin_melt_detail,
-                                 &refresh_session.melt)))
-  {
-    TEH_KS_release (key_state);
-    TEH_plugin->rollback (TEH_plugin->cls,
-                          session);
-    return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
-  }
-  TEH_KS_release (key_state);
-
-  if (GNUNET_OK !=
-      (res = TEH_plugin->create_refresh_session (TEH_plugin->cls,
-                                                 session,
-                                                 session_hash,
-                                                 &refresh_session)))
-  {
-    TEH_plugin->rollback (TEH_plugin->cls,
-                          session);
-    return TEH_RESPONSE_reply_internal_db_error (connection,
-                                                
TALER_EC_REFRESH_MELT_DB_STORE_SESSION_ERROR);
-  }
-
-  /* store requested new denominations */
-  if (GNUNET_OK !=
-      TEH_plugin->insert_refresh_order (TEH_plugin->cls,
-                                        session,
-                                        session_hash,
-                                        num_new_denoms,
-                                        denom_pubs))
-  {
-    TEH_plugin->rollback (TEH_plugin->cls,
-                          session);
-    return TEH_RESPONSE_reply_internal_db_error (connection,
-                                                
TALER_EC_REFRESH_MELT_DB_STORE_ORDER_ERROR);
-  }
-
-  if (GNUNET_OK !=
-      TEH_plugin->insert_refresh_commit_coins (TEH_plugin->cls,
-                                               session,
-                                               session_hash,
-                                               num_new_denoms,
-                                               
commit_coin[refresh_session.noreveal_index]))
-  {
-    TEH_plugin->rollback (TEH_plugin->cls,
-                          session);
-    return TEH_RESPONSE_reply_internal_db_error (connection,
-                                                
TALER_EC_REFRESH_MELT_DB_STORE_ORDER_ERROR);
-  }
-  if (GNUNET_OK !=
-      TEH_plugin->insert_refresh_transfer_public_key (TEH_plugin->cls,
-                                                      session,
-                                                      session_hash,
-                                                      
&transfer_pubs[refresh_session.noreveal_index]))
-  {
-    TEH_plugin->rollback (TEH_plugin->cls,
-                          session);
-    return TEH_RESPONSE_reply_internal_db_error (connection,
-                                                
TALER_EC_REFRESH_MELT_DB_STORE_TRANSFER_ERROR);
-  }
-
-  COMMIT_TRANSACTION (session, connection);
-  return TEH_RESPONSE_reply_refresh_melt_success (connection,
-                                                  session_hash,
-                                                  
refresh_session.noreveal_index);
-}
-
-
-/**
- * Check if the given @a transfer_privs correspond to an honest
- * commitment for the given session.
- * Checks that the transfer private keys match their commitments.
- * Then derives the shared secret for each #TALER_CNC_KAPPA, and check that 
they match.
- *
- * @param connection the MHD connection to handle
- * @param session database connection to use
- * @param session_hash hash of session to query
- * @param off commitment offset to check
- * @param transfer_priv private transfer key
- * @param melt information about the melted coin
- * @param num_newcoins number of newcoins being generated
- * @param denom_pubs array of @a num_newcoins keys for the new coins
- * @param hash_context hash context to update by hashing in the data
- *                     from this offset
- * @return #GNUNET_OK if the committment was honest,
- *         #GNUNET_NO if there was a problem and we generated an error message
- *         #GNUNET_SYSERR if we could not even generate an error message
- */
-static int
-check_commitment (struct MHD_Connection *connection,
-                  struct TALER_EXCHANGEDB_Session *session,
-                  const struct GNUNET_HashCode *session_hash,
-                  unsigned int off,
-                  const struct TALER_TransferPrivateKeyP *transfer_priv,
-                  const struct TALER_EXCHANGEDB_RefreshMelt *melt,
-                  unsigned int num_newcoins,
-                  const struct TALER_DenominationPublicKey *denom_pubs,
-                  struct GNUNET_HashContext *hash_context)
-{
-  struct TALER_TransferSecretP transfer_secret;
-  unsigned int j;
-
-  TALER_link_reveal_transfer_secret (transfer_priv,
-                                     &melt->coin.coin_pub,
-                                     &transfer_secret);
-
-  /* Check that the commitments for all new coins were correct */
-  for (j = 0; j < num_newcoins; j++)
-  {
-    struct TALER_FreshCoinP fc;
-    struct TALER_CoinSpendPublicKeyP coin_pub;
-    struct GNUNET_HashCode h_msg;
-    char *buf;
-    size_t buf_len;
-
-    TALER_setup_fresh_coin (&transfer_secret,
-                            j,
-                            &fc);
-    GNUNET_CRYPTO_eddsa_key_get_public (&fc.coin_priv.eddsa_priv,
-                                        &coin_pub.eddsa_pub);
-    GNUNET_CRYPTO_hash (&coin_pub,
-                        sizeof (struct TALER_CoinSpendPublicKeyP),
-                        &h_msg);
-    if (GNUNET_YES !=
-        GNUNET_CRYPTO_rsa_blind (&h_msg,
-                                 &fc.blinding_key.bks,
-                                 denom_pubs[j].rsa_public_key,
-                                 &buf,
-                                 &buf_len))
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                  "Blind failed (bad denomination key!?)\n");
-      return (MHD_YES ==
-             TEH_RESPONSE_reply_internal_error (connection,
-                                                
TALER_EC_REFRESH_REVEAL_BLINDING_ERROR,
-                                                "Blinding error"))
-        ? GNUNET_NO : GNUNET_SYSERR;
-    }
-    GNUNET_CRYPTO_hash_context_read (hash_context,
-                                     buf,
-                                     buf_len);
-    GNUNET_free (buf);
-  }
-  return GNUNET_OK;
-}
-
-
-/**
- * Exchange a coin as part of a refresh operation.  Obtains the
- * envelope from the database and performs the signing operation.
- *
- * @param connection the MHD connection to handle
- * @param session database connection to use
- * @param session_hash hash of session to query
- * @param key_state key state to lookup denomination pubs
- * @param denom_pub denomination key for the coin to create
- * @param commit_coin the coin that was committed
- * @param coin_off number of the coin
- * @return NULL on error, otherwise signature over the coin
- */
-static struct TALER_DenominationSignature
-refresh_exchange_coin (struct MHD_Connection *connection,
-                       struct TALER_EXCHANGEDB_Session *session,
-                       const struct GNUNET_HashCode *session_hash,
-                       struct TEH_KS_StateHandle *key_state,
-                       const struct TALER_DenominationPublicKey *denom_pub,
-                       const struct TALER_EXCHANGEDB_RefreshCommitCoin 
*commit_coin,
-                       unsigned int coin_off)
-{
-  struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki;
-  struct TALER_DenominationSignature ev_sig;
-
-  dki = TEH_KS_denomination_key_lookup (key_state,
-                                        denom_pub,
-                                       TEH_KS_DKU_WITHDRAW);
-  if (NULL == dki)
-  {
-    GNUNET_break (0);
-    ev_sig.rsa_signature = NULL;
-    return ev_sig;
-  }
-  if (GNUNET_OK ==
-      TEH_plugin->get_refresh_out (TEH_plugin->cls,
-                                   session,
-                                   session_hash,
-                                   coin_off,
-                                   &ev_sig))
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                "Returning cashed reply for /refresh/reveal signature\n");
-    return ev_sig;
-  }
-
-  ev_sig.rsa_signature
-    = GNUNET_CRYPTO_rsa_sign_blinded (dki->denom_priv.rsa_private_key,
-                                      commit_coin->coin_ev,
-                                      commit_coin->coin_ev_size);
-  if (NULL == ev_sig.rsa_signature)
-  {
-    GNUNET_break (0);
-    return ev_sig;
-  }
-  if (GNUNET_SYSERR ==
-      TEH_plugin->insert_refresh_out (TEH_plugin->cls,
-                                      session,
-                                      session_hash,
-                                      coin_off,
-                                      &ev_sig))
-  {
-    GNUNET_break (0);
-    GNUNET_CRYPTO_rsa_signature_free (ev_sig.rsa_signature);
-    ev_sig.rsa_signature = NULL;
-  }
-
-  return ev_sig;
-}
-
-
-/**
- * The client request was well-formed, now execute the DB transaction
- * of a "/refresh/reveal" operation.  We use the @a ev_sigs and
- * @a commit_coins to clean up resources after this function returns
- * as we might experience retries of the database transaction.
- *
- * @param connection the MHD connection to handle
- * @param session database session
- * @param session_hash hash identifying the refresh session
- * @param refresh_session information about the refresh operation we are doing
- * @param denom_pubs array of "num_newcoins" denomination keys for the new 
coins
- * @param[out] ev_sigs where to store generated signatures for the new coins,
- *                     array of length "num_newcoins", memory released by the
- *                     caller
- * @param[out] commit_coins array of length "num_newcoins" to be used for
- *                     information about the new coins from the commitment.
- * @return MHD result code
- */
-static int
-execute_refresh_reveal_transaction (struct MHD_Connection *connection,
-                                    struct TALER_EXCHANGEDB_Session *session,
-                                    const struct GNUNET_HashCode *session_hash,
-                                    const struct 
TALER_EXCHANGEDB_RefreshSession *refresh_session,
-                                    const struct TALER_DenominationPublicKey 
*denom_pubs,
-                                    struct TALER_DenominationSignature 
*ev_sigs,
-                                    struct TALER_EXCHANGEDB_RefreshCommitCoin 
*commit_coins)
-{
-  unsigned int j;
-  struct TEH_KS_StateHandle *key_state;
-  int ret;
-
-  START_TRANSACTION (session, connection);
-  key_state = TEH_KS_acquire ();
-  for (j=0;j<refresh_session->num_newcoins;j++)
-  {
-    if (NULL == ev_sigs[j].rsa_signature) /* could be non-NULL during retries 
*/
-      ev_sigs[j] = refresh_exchange_coin (connection,
-                                          session,
-                                          session_hash,
-                                          key_state,
-                                          &denom_pubs[j],
-                                          &commit_coins[j],
-                                          j);
-    if (NULL == ev_sigs[j].rsa_signature)
-    {
-      TEH_plugin->rollback (TEH_plugin->cls,
-                            session);
-      ret = TEH_RESPONSE_reply_internal_db_error (connection,
-                                                 
TALER_EC_REFRESH_REVEAL_SIGNING_ERROR);
-      goto cleanup;
-    }
-  }
-  COMMIT_TRANSACTION (session, connection);
-  ret = TEH_RESPONSE_reply_refresh_reveal_success (connection,
-                                                   
refresh_session->num_newcoins,
-                                                   ev_sigs);
- cleanup:
-  TEH_KS_release (key_state);
-  return ret;
-}
-
-
-/**
- * Execute a "/refresh/reveal".  The client is revealing to us the
- * transfer keys for @a #TALER_CNC_KAPPA-1 sets of coins.  Verify that the
- * revealed transfer keys would allow linkage to the blinded coins,
- * and if so, return the signed coins for corresponding to the set of
- * coins that was not chosen.
- *
- * @param connection the MHD connection to handle
- * @param session_hash hash identifying the refresh session
- * @param transfer_privs array with the revealed transfer keys,
- *                      length must be #TALER_CNC_KAPPA - 1
- * @return MHD result code
- */
-int
-TEH_DB_execute_refresh_reveal (struct MHD_Connection *connection,
-                               const struct GNUNET_HashCode *session_hash,
-                               struct TALER_TransferPrivateKeyP 
*transfer_privs)
-{
-  int res;
-  struct TALER_EXCHANGEDB_Session *session;
-  struct TALER_EXCHANGEDB_RefreshSession refresh_session;
-  struct TALER_DenominationPublicKey *denom_pubs;
-  struct TALER_DenominationSignature *ev_sigs;
-  struct TALER_EXCHANGEDB_RefreshCommitCoin *commit_coins;
-  unsigned int i;
-  unsigned int j;
-  unsigned int off;
-  struct GNUNET_HashContext *hash_context;
-  struct GNUNET_HashCode sh_check;
-  int ret;
-  struct TALER_TransferPublicKeyP gamma_tp;
-
-  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_refresh_session (TEH_plugin->cls,
-                                         session,
-                                         session_hash,
-                                         &refresh_session);
-  if (GNUNET_NO == res)
-    return TEH_RESPONSE_reply_arg_invalid (connection,
-                                          
TALER_EC_REFRESH_REVEAL_SESSION_UNKNOWN,
-                                           "session_hash");
-  if ( (GNUNET_SYSERR == res) ||
-       (refresh_session.noreveal_index >= TALER_CNC_KAPPA) )
-    return TEH_RESPONSE_reply_internal_db_error (connection,
-                                                
TALER_EC_REFRESH_REVEAL_DB_FETCH_SESSION_ERROR);
-  denom_pubs = GNUNET_new_array (refresh_session.num_newcoins,
-                                 struct TALER_DenominationPublicKey);
-  if (GNUNET_OK !=
-      TEH_plugin->get_refresh_order (TEH_plugin->cls,
-                                     session,
-                                     session_hash,
-                                     refresh_session.num_newcoins,
-                                     denom_pubs))
-  {
-    GNUNET_break (0);
-    GNUNET_free (denom_pubs);
-    GNUNET_CRYPTO_rsa_signature_free 
(refresh_session.melt.coin.denom_sig.rsa_signature);
-    GNUNET_CRYPTO_rsa_public_key_free 
(refresh_session.melt.coin.denom_pub.rsa_public_key);
-    return (MHD_YES == TEH_RESPONSE_reply_internal_db_error (connection,
-                                                            
TALER_EC_REFRESH_REVEAL_DB_FETCH_ORDER_ERROR))
-        ? GNUNET_NO : GNUNET_SYSERR;
-  }
-
-  hash_context = GNUNET_CRYPTO_hash_context_start ();
-  /* first, iterate over transfer public keys for hash_context */
-  off = 0;
-  for (i=0;i<TALER_CNC_KAPPA;i++)
-  {
-    if (i == refresh_session.noreveal_index)
-    {
-      off = 1;
-      /* obtain gamma_tp from db */
-      if (GNUNET_OK !=
-          TEH_plugin->get_refresh_transfer_public_key (TEH_plugin->cls,
-                                                       session,
-                                                       session_hash,
-                                                       &gamma_tp))
-      {
-        GNUNET_break (0);
-        GNUNET_free (denom_pubs);
-        GNUNET_CRYPTO_rsa_signature_free 
(refresh_session.melt.coin.denom_sig.rsa_signature);
-        GNUNET_CRYPTO_rsa_public_key_free 
(refresh_session.melt.coin.denom_pub.rsa_public_key);
-        GNUNET_CRYPTO_hash_context_abort (hash_context);
-        return (MHD_YES == TEH_RESPONSE_reply_internal_db_error (connection,
-                                                                
TALER_EC_REFRESH_REVEAL_DB_FETCH_TRANSFER_ERROR))
-          ? GNUNET_NO : GNUNET_SYSERR;
-      }
-      GNUNET_CRYPTO_hash_context_read (hash_context,
-                                       &gamma_tp,
-                                       sizeof (struct 
TALER_TransferPublicKeyP));
-    }
-    else
-    {
-      /* compute tp from private key */
-      struct TALER_TransferPublicKeyP tp;
-
-      GNUNET_CRYPTO_ecdhe_key_get_public (&transfer_privs[i - off].ecdhe_priv,
-                                          &tp.ecdhe_pub);
-      GNUNET_CRYPTO_hash_context_read (hash_context,
-                                       &tp,
-                                       sizeof (struct 
TALER_TransferPublicKeyP));
-    }
-  }
-
-  /* next, add all of the hashes from the denomination keys to the
-     hash_context */
-  {
-    struct TALER_DenominationPublicKey 
denom_pubs[refresh_session.num_newcoins];
-
-    if (GNUNET_OK !=
-        TEH_plugin->get_refresh_order (TEH_plugin->cls,
-                                       session,
-                                       session_hash,
-                                       refresh_session.num_newcoins,
-                                       denom_pubs))
-    {
-      GNUNET_break (0);
-      GNUNET_free (denom_pubs);
-      GNUNET_CRYPTO_rsa_signature_free 
(refresh_session.melt.coin.denom_sig.rsa_signature);
-      GNUNET_CRYPTO_rsa_public_key_free 
(refresh_session.melt.coin.denom_pub.rsa_public_key);
-      GNUNET_CRYPTO_hash_context_abort (hash_context);
-      return (MHD_YES == TEH_RESPONSE_reply_internal_db_error (connection,
-                                                              
TALER_EC_REFRESH_REVEAL_DB_FETCH_ORDER_ERROR))
-        ? GNUNET_NO : GNUNET_SYSERR;
-    }
-    for (i=0;i<refresh_session.num_newcoins;i++)
-    {
-      char *buf;
-      size_t buf_size;
-
-      buf_size = GNUNET_CRYPTO_rsa_public_key_encode 
(denom_pubs[i].rsa_public_key,
-                                                      &buf);
-      GNUNET_CRYPTO_hash_context_read (hash_context,
-                                       buf,
-                                       buf_size);
-      GNUNET_free (buf);
-      GNUNET_CRYPTO_rsa_public_key_free (denom_pubs[i].rsa_public_key);
-    }
-  }
-
-  /* next, add public key of coin and amount being refreshed */
-  {
-    struct TALER_AmountNBO melt_amountn;
-
-    GNUNET_CRYPTO_hash_context_read (hash_context,
-                                     &refresh_session.melt.coin.coin_pub,
-                                     sizeof (struct 
TALER_CoinSpendPublicKeyP));
-    TALER_amount_hton (&melt_amountn,
-                       &refresh_session.melt.amount_with_fee);
-    GNUNET_CRYPTO_hash_context_read (hash_context,
-                                     &melt_amountn,
-                                     sizeof (struct TALER_AmountNBO));
-  }
-
-  commit_coins = GNUNET_new_array (refresh_session.num_newcoins,
-                                   struct TALER_EXCHANGEDB_RefreshCommitCoin);
-  off = 0;
-  for (i=0;i<TALER_CNC_KAPPA;i++)
-  {
-    if (i == refresh_session.noreveal_index)
-    {
-      off = 1;
-      /* obtain commit_coins for the selected gamma value from DB */
-      if (GNUNET_OK !=
-          TEH_plugin->get_refresh_commit_coins (TEH_plugin->cls,
-                                                session,
-                                                session_hash,
-                                                refresh_session.num_newcoins,
-                                                commit_coins))
-      {
-        GNUNET_break (0);
-        GNUNET_free (denom_pubs);
-        GNUNET_CRYPTO_rsa_signature_free 
(refresh_session.melt.coin.denom_sig.rsa_signature);
-        GNUNET_CRYPTO_rsa_public_key_free 
(refresh_session.melt.coin.denom_pub.rsa_public_key);
-        GNUNET_CRYPTO_hash_context_abort (hash_context);
-        return TEH_RESPONSE_reply_internal_db_error (connection,
-                                                    
TALER_EC_REFRESH_REVEAL_DB_FETCH_COMMIT_ERROR);
-      }
-      /* add envelopes to hash_context */
-      for (j=0;j<refresh_session.num_newcoins;j++)
-      {
-        GNUNET_CRYPTO_hash_context_read (hash_context,
-                                         commit_coins[j].coin_ev,
-                                         commit_coins[j].coin_ev_size);
-      }
-      continue;
-    }
-    if (GNUNET_OK !=
-        (res = check_commitment (connection,
-                                 session,
-                                 session_hash,
-                                 i,
-                                 &transfer_privs[i - off],
-                                 &refresh_session.melt,
-                                 refresh_session.num_newcoins,
-                                 denom_pubs,
-                                 hash_context)))
-    {
-      GNUNET_break_op (0);
-      for (j=0;j<refresh_session.num_newcoins;j++)
-      {
-        GNUNET_CRYPTO_rsa_public_key_free (denom_pubs[j].rsa_public_key);
-        GNUNET_free (commit_coins[j].coin_ev);
-      }
-      GNUNET_free (commit_coins);
-      GNUNET_free (denom_pubs);
-      GNUNET_CRYPTO_rsa_signature_free 
(refresh_session.melt.coin.denom_sig.rsa_signature);
-      GNUNET_CRYPTO_rsa_public_key_free 
(refresh_session.melt.coin.denom_pub.rsa_public_key);
-      GNUNET_CRYPTO_hash_context_abort (hash_context);
-      return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
-    }
-  }
-
-  /* Check session hash matches */
-  GNUNET_CRYPTO_hash_context_finish (hash_context,
-                                     &sh_check);
-  if (0 != memcmp (&sh_check,
-                   session_hash,
-                   sizeof (struct GNUNET_HashCode)))
-  {
-    GNUNET_break_op (0);
-    ret = TEH_RESPONSE_reply_refresh_reveal_missmatch (connection,
-                                                       &refresh_session,
-                                                       commit_coins,
-                                                       denom_pubs,
-                                                       &gamma_tp);
-    for (j=0;j<refresh_session.num_newcoins;j++)
-    {
-      GNUNET_free (commit_coins[j].coin_ev);
-      GNUNET_CRYPTO_rsa_public_key_free (denom_pubs[j].rsa_public_key);
-    }
-    GNUNET_free (commit_coins);
-    GNUNET_free (denom_pubs);
-    GNUNET_CRYPTO_rsa_signature_free 
(refresh_session.melt.coin.denom_sig.rsa_signature);
-    GNUNET_CRYPTO_rsa_public_key_free 
(refresh_session.melt.coin.denom_pub.rsa_public_key);
-
-    return ret;
-  }
-
-  /* Client request OK, start transaction */
-  ev_sigs = GNUNET_new_array (refresh_session.num_newcoins,
-                              struct TALER_DenominationSignature);
-
-  /* FIXME: might need to store revealed transfer private keys for
-     the auditor for later; should pass them as arguments here! #4792*/
-  res = execute_refresh_reveal_transaction (connection,
-                                            session,
-                                            session_hash,
-                                            &refresh_session,
-                                            denom_pubs,
-                                            ev_sigs,
-                                            commit_coins);
-  for (i=0;i<refresh_session.num_newcoins;i++)
-  {
-    if (NULL != ev_sigs[i].rsa_signature)
-      GNUNET_CRYPTO_rsa_signature_free (ev_sigs[i].rsa_signature);
-    GNUNET_free (commit_coins[i].coin_ev);
-  }
-  for (j=0;j<refresh_session.num_newcoins;j++)
-    if (NULL != denom_pubs[j].rsa_public_key)
-      GNUNET_CRYPTO_rsa_public_key_free (denom_pubs[j].rsa_public_key);
-  GNUNET_CRYPTO_rsa_signature_free 
(refresh_session.melt.coin.denom_sig.rsa_signature);
-  GNUNET_CRYPTO_rsa_public_key_free 
(refresh_session.melt.coin.denom_pub.rsa_public_key);
-  GNUNET_free (ev_sigs);
-  GNUNET_free (denom_pubs);
-  GNUNET_free (commit_coins);
-  return res;
-}
-
-
-/**
- * Closure for #handle_transfer_data().
- */
-struct HTD_Context
-{
-
-  /**
-   * Session link data we collect.
-   */
-  struct TEH_RESPONSE_LinkSessionInfo *sessions;
-
-  /**
-   * Database session. Nothing to do with @a sessions.
-   */
-  struct TALER_EXCHANGEDB_Session *session;
-
-  /**
-   * MHD connection, for queueing replies.
-   */
-  struct MHD_Connection *connection;
-
-  /**
-   * Number of sessions the coin was melted into.
-   */
-  unsigned int num_sessions;
-
-  /**
-   * How are we expected to proceed. #GNUNET_SYSERR if we
-   * failed to return an error (should return #MHD_NO).
-   * #GNUNET_NO if we succeeded in queueing an MHD error
-   * (should return #MHD_YES from #TEH_execute_refresh_link),
-   * #GNUNET_OK if we should call #TEH_RESPONSE_reply_refresh_link_success().
-   */
-  int status;
-};
-
-
-/**
- * Function called with the session hashes and transfer secret
- * information for a given coin.  Gets the linkage data and
- * builds the reply for the client.
- *
- *
- * @param cls closure, a `struct HTD_Context`
- * @param session_hash a session the coin was melted in
- * @param transfer_pub public transfer key for the session
- */
-static void
-handle_transfer_data (void *cls,
-                      const struct GNUNET_HashCode *session_hash,
-                      const struct TALER_TransferPublicKeyP *transfer_pub)
-{
-  struct HTD_Context *ctx = cls;
-  struct TALER_EXCHANGEDB_LinkDataList *ldl;
-  struct TEH_RESPONSE_LinkSessionInfo *lsi;
-
-  if (GNUNET_OK != ctx->status)
-    return;
-  ldl = TEH_plugin->get_link_data_list (TEH_plugin->cls,
-                                        ctx->session,
-                                        session_hash);
-  if (NULL == ldl)
-  {
-    ctx->status = GNUNET_NO;
-    if (MHD_NO ==
-        TEH_RESPONSE_reply_json_pack (ctx->connection,
-                                      MHD_HTTP_NOT_FOUND,
-                                      "{s:s}",
-                                      "error",
-                                      "link data not found (link)"))
-      ctx->status = GNUNET_SYSERR;
-    return;
-  }
-  GNUNET_array_grow (ctx->sessions,
-                     ctx->num_sessions,
-                     ctx->num_sessions + 1);
-  lsi = &ctx->sessions[ctx->num_sessions - 1];
-  lsi->transfer_pub = *transfer_pub;
-  lsi->ldl = ldl;
-}
-
-
-/**
- * Execute a "/refresh/link".  Returns the linkage information that
- * will allow the owner of a coin to follow the refresh trail to
- * the refreshed coin.
- *
- * @param connection the MHD connection to handle
- * @param coin_pub public key of the coin to link
- * @return MHD result code
- */
-int
-TEH_DB_execute_refresh_link (struct MHD_Connection *connection,
-                             const struct TALER_CoinSpendPublicKeyP *coin_pub)
-{
-  struct HTD_Context ctx;
-  int res;
-  unsigned int i;
-
-  if (NULL == (ctx.session = TEH_plugin->get_session (TEH_plugin->cls)))
-  {
-    GNUNET_break (0);
-    return TEH_RESPONSE_reply_internal_db_error (connection,
-                                                TALER_EC_DB_SETUP_FAILED);
-  }
-  ctx.connection = connection;
-  ctx.num_sessions = 0;
-  ctx.sessions = NULL;
-  ctx.status = GNUNET_OK;
-  res = TEH_plugin->get_transfer (TEH_plugin->cls,
-                                  ctx.session,
-                                  coin_pub,
-                                  &handle_transfer_data,
-                                  &ctx);
-  if (GNUNET_SYSERR == ctx.status)
-  {
-    res = MHD_NO;
-    goto cleanup;
-  }
-  if (GNUNET_NO == ctx.status)
-  {
-    res = MHD_YES;
-    goto cleanup;
-  }
-  GNUNET_assert (GNUNET_OK == ctx.status);
-  if (0 == ctx.num_sessions)
-    return TEH_RESPONSE_reply_arg_unknown (connection,
-                                          TALER_EC_REFRESH_LINK_COIN_UNKNOWN,
-                                           "coin_pub");
-  res = TEH_RESPONSE_reply_refresh_link_success (connection,
-                                                 ctx.num_sessions,
-                                                 ctx.sessions);
- cleanup:
-  for (i=0;i<ctx.num_sessions;i++)
-    TEH_plugin->free_link_data_list (TEH_plugin->cls,
-                                     ctx.sessions[i].ldl);
-  GNUNET_free_non_null (ctx.sessions);
-  return res;
-}
-
-
-/**
- * Add an incoming transaction to the database.  Checks if the
- * transaction is fresh (not a duplicate) and if so adds it to
- * the database.
- *
- * @param connection the MHD connection to handle
- * @param reserve_pub public key of the reserve
- * @param amount amount to add to the reserve
- * @param execution_time when did we receive the wire transfer
- * @param sender_account_details which account send the funds
- * @param transfer_details information that uniquely identifies the transfer
- * @return MHD result code
- */
-int
-TEH_DB_execute_admin_add_incoming (struct MHD_Connection *connection,
-                                   const struct TALER_ReservePublicKeyP 
*reserve_pub,
-                                   const struct TALER_Amount *amount,
-                                   struct GNUNET_TIME_Absolute execution_time,
-                                   const json_t *sender_account_details,
-                                   const json_t *transfer_details)
-{
-  struct TALER_EXCHANGEDB_Session *session;
-  int ret;
-  void *json_str;
-
-  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);
-  }
-  json_str = json_dumps (transfer_details,
-                         JSON_INDENT(2));
-  if (NULL == json_str)
-  {
-    GNUNET_break (0);
-    return TEH_RESPONSE_reply_internal_db_error (connection,
-                                                TALER_EC_PARSER_OUT_OF_MEMORY);
-  }
-  ret = TEH_plugin->reserves_in_insert (TEH_plugin->cls,
-                                        session,
-                                        reserve_pub,
-                                        amount,
-                                        execution_time,
-                                        sender_account_details,
-                                        json_str,
-                                        strlen (json_str));
-  free (json_str);
-  if (GNUNET_SYSERR == ret)
-  {
-    GNUNET_break (0);
-    return TEH_RESPONSE_reply_internal_db_error (connection,
-                                                
TALER_EC_ADMIN_ADD_INCOMING_DB_STORE);
-  }
-  return TEH_RESPONSE_reply_json_pack (connection,
-                                       MHD_HTTP_OK,
-                                       "{s:s}",
-                                       "status",
-                                       (GNUNET_OK == ret)
-                                       ? "NEW"
-                                       : "DUP");
-}
-
-
-/**
- * Closure for #handle_transaction_data.
- */
-struct WtidTransactionContext
-{
-
-  /**
-   * Total amount of the wire transfer, as calculated by
-   * summing up the individual amounts. To be rounded down
-   * to calculate the real transfer amount at the end.
-   * Only valid if @e is_valid is #GNUNET_YES.
-   */
-  struct TALER_Amount total;
-
-  /**
-   * Public key of the merchant, only valid if @e is_valid
-   * is #GNUNET_YES.
-   */
-  struct TALER_MerchantPublicKeyP merchant_pub;
-
-  /**
-   * Which method was used to wire the funds?
-   */
-  char *wire_method;
-
-  /**
-   * Hash of the wire details of the merchant (identical for all
-   * deposits), only valid if @e is_valid is #GNUNET_YES.
-   */
-  struct GNUNET_HashCode h_wire;
-
-  /**
-   * Execution time of the wire transfer
-   */
-  struct GNUNET_TIME_Absolute exec_time;
-
-  /**
-   * Head of DLL with details for /wire/deposit response.
-   */
-  struct TEH_TrackTransferDetail *wdd_head;
-
-  /**
-   * Head of DLL with details for /wire/deposit response.
-   */
-  struct TEH_TrackTransferDetail *wdd_tail;
-
-  /**
-   * JSON array with details about the individual deposits.
-   */
-  json_t *deposits;
-
-  /**
-   * Initially #GNUNET_NO, if we found no deposits so far.  Set to
-   * #GNUNET_YES if we got transaction data, and the database replies
-   * remained consistent with respect to @e merchant_pub and @e h_wire
-   * (as they should).  Set to #GNUNET_SYSERR if we encountered an
-   * internal error.
-   */
-  int is_valid;
-
-};
-
-
-/**
- * Function called with the results of the lookup of the
- * transaction data for the given wire transfer identifier.
- *
- * @param cls our context for transmission
- * @param rowid which row in the DB is the information from (for diagnostics)
- * @param merchant_pub public key of the merchant (should be same for all 
callbacks with the same @e cls)
- * @param wire_method which wire plugin was used
- * @param h_wire hash of wire transfer details of the merchant (should be same 
for all callbacks with the same @e cls)
- * @param exec_time execution time of the wire transfer (should be same for 
all callbacks with the same @e cls)
- * @param h_contract_terms which proposal was this payment about
- * @param coin_pub which public key was this payment about
- * @param deposit_value amount contributed by this coin in total
- * @param deposit_fee deposit fee charged by exchange for this coin
- */
-static void
-handle_transaction_data (void *cls,
-                         uint64_t rowid,
-                         const struct TALER_MerchantPublicKeyP *merchant_pub,
-                         const char *wire_method,
-                         const struct GNUNET_HashCode *h_wire,
-                         struct GNUNET_TIME_Absolute exec_time,
-                         const struct GNUNET_HashCode *h_contract_terms,
-                         const struct TALER_CoinSpendPublicKeyP *coin_pub,
-                         const struct TALER_Amount *deposit_value,
-                         const struct TALER_Amount *deposit_fee)
-{
-  struct WtidTransactionContext *ctx = cls;
-  struct TALER_Amount delta;
-  struct TEH_TrackTransferDetail *wdd;
-
-  if (GNUNET_SYSERR == ctx->is_valid)
-    return;
-  if (GNUNET_NO == ctx->is_valid)
-  {
-    ctx->merchant_pub = *merchant_pub;
-    ctx->h_wire = *h_wire;
-    ctx->exec_time = exec_time;
-    ctx->wire_method = GNUNET_strdup (wire_method);
-    ctx->is_valid = GNUNET_YES;
-    if (GNUNET_OK !=
-        TALER_amount_subtract (&ctx->total,
-                               deposit_value,
-                               deposit_fee))
-    {
-      GNUNET_break (0);
-      ctx->is_valid = GNUNET_SYSERR;
-      return;
-    }
-  }
-  else
-  {
-    if ( (0 != memcmp (&ctx->merchant_pub,
-                       merchant_pub,
-                       sizeof (struct TALER_MerchantPublicKeyP))) ||
-         (0 != strcmp (wire_method,
-                       ctx->wire_method)) ||
-         (0 != memcmp (&ctx->h_wire,
-                       h_wire,
-                       sizeof (struct GNUNET_HashCode))) )
-    {
-      GNUNET_break (0);
-      ctx->is_valid = GNUNET_SYSERR;
-      return;
-    }
-    if (GNUNET_OK !=
-        TALER_amount_subtract (&delta,
-                               deposit_value,
-                               deposit_fee))
-    {
-      GNUNET_break (0);
-      ctx->is_valid = GNUNET_SYSERR;
-      return;
-    }
-    if (GNUNET_OK !=
-        TALER_amount_add (&ctx->total,
-                          &ctx->total,
-                          &delta))
-    {
-      GNUNET_break (0);
-      ctx->is_valid = GNUNET_SYSERR;
-      return;
-    }
-  }
-  wdd = GNUNET_new (struct TEH_TrackTransferDetail);
-  wdd->deposit_value = *deposit_value;
-  wdd->deposit_fee = *deposit_fee;
-  wdd->h_contract_terms = *h_contract_terms;
-  wdd->coin_pub = *coin_pub;
-  GNUNET_CONTAINER_DLL_insert (ctx->wdd_head,
-                               ctx->wdd_tail,
-                               wdd);
-}
-
-
-/**
- * Execute a "/track/transfer".  Returns the transaction information
- * associated with the given wire transfer identifier.
- *
- * @param connection the MHD connection to handle
- * @param wtid wire transfer identifier to resolve
- * @return MHD result code
- */
-int
-TEH_DB_execute_track_transfer (struct MHD_Connection *connection,
-                               const struct TALER_WireTransferIdentifierRawP 
*wtid)
-{
-  int ret;
-  struct WtidTransactionContext ctx;
-  struct TALER_EXCHANGEDB_Session *session;
-  struct TEH_TrackTransferDetail *wdd;
-  struct GNUNET_TIME_Absolute wire_fee_start_date;
-  struct GNUNET_TIME_Absolute wire_fee_end_date;
-  struct TALER_Amount wire_fee;
-  struct TALER_MasterSignatureP wire_fee_master_sig;
-
-  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);
-  }
-  ctx.is_valid = GNUNET_NO;
-  ctx.wdd_head = NULL;
-  ctx.wdd_tail = NULL;
-  ctx.wire_method = NULL;
-  ret = TEH_plugin->lookup_wire_transfer (TEH_plugin->cls,
-                                          session,
-                                          wtid,
-                                          &handle_transaction_data,
-                                          &ctx);
-  if (GNUNET_SYSERR == ret)
-  {
-    GNUNET_break (0);
-    ret = TEH_RESPONSE_reply_internal_db_error (connection,
-                                               
TALER_EC_TRACK_TRANSFER_DB_FETCH_FAILED);
-    goto cleanup;
-  }
-  if (GNUNET_SYSERR == ctx.is_valid)
-  {
-    GNUNET_break (0);
-    ret = TEH_RESPONSE_reply_internal_db_error (connection,
-                                               
TALER_EC_TRACK_TRANSFER_DB_INCONSISTENT);
-    goto cleanup;
-  }
-  if (GNUNET_NO == ctx.is_valid)
-  {
-    ret = TEH_RESPONSE_reply_arg_unknown (connection,
-                                         
TALER_EC_TRACK_TRANSFER_WTID_NOT_FOUND,
-                                          "wtid");
-    goto cleanup;
-  }
-  if (GNUNET_OK !=
-      TEH_plugin->get_wire_fee (TEH_plugin->cls,
-                                session,
-                                ctx.wire_method,
-                                ctx.exec_time,
-                                &wire_fee_start_date,
-                                &wire_fee_end_date,
-                                &wire_fee,
-                                &wire_fee_master_sig))
-  {
-    GNUNET_break (0);
-    ret = TEH_RESPONSE_reply_internal_db_error (connection,
-                                               
TALER_EC_TRACK_TRANSFER_WIRE_FEE_NOT_FOUND);
-    goto cleanup;
-  }
-  if (GNUNET_OK !=
-      TALER_amount_subtract (&ctx.total,
-                             &ctx.total,
-                             &wire_fee))
-  {
-    GNUNET_break (0);
-    ret = TEH_RESPONSE_reply_internal_db_error (connection,
-                                               
TALER_EC_TRACK_TRANSFER_WIRE_FEE_INCONSISTENT);
-    goto cleanup;
-  }
-  ret = TEH_RESPONSE_reply_track_transfer_details (connection,
-                                                   &ctx.total,
-                                                   &ctx.merchant_pub,
-                                                   &ctx.h_wire,
-                                                   &wire_fee,
-                                                   ctx.exec_time,
-                                                   ctx.wdd_head);
- cleanup:
-  while (NULL != (wdd = ctx.wdd_head))
-  {
-    GNUNET_CONTAINER_DLL_remove (ctx.wdd_head,
-                                 ctx.wdd_tail,
-                                 wdd);
-    GNUNET_free (wdd);
-  }
-  GNUNET_free_non_null (ctx.wire_method);
-  return ret;
-}
-
-
-/**
- * Closure for #handle_wtid_data.
- */
-struct DepositWtidContext
-{
-
-  /**
-   * Where should we send the reply?
-   */
-  struct MHD_Connection *connection;
-
-  /**
-   * Hash of the proposal data we are looking up.
-   */
-  struct GNUNET_HashCode h_contract_terms;
-
-  /**
-   * Hash of the wire transfer details we are looking up.
-   */
-  struct GNUNET_HashCode h_wire;
-
-  /**
-   * Public key we are looking up.
-   */
-  struct TALER_CoinSpendPublicKeyP coin_pub;
-
-  /**
-   * MHD result code to return.
-   */
-  int res;
-};
-
-
-/**
- * Function called with the results of the lookup of the
- * wire transfer identifier information.
- *
- * @param cls our context for transmission
- * @param wtid raw wire transfer identifier, NULL
- *         if the transaction was not yet done
- * @param coin_contribution how much did the coin we asked about
- *        contribute to the total transfer value? (deposit value including fee)
- * @param coin_fee how much did the exchange charge for the deposit fee
- * @param execution_time when was the transaction done, or
- *         when we expect it to be done (if @a wtid was NULL);
- *         #GNUNET_TIME_UNIT_FOREVER_ABS if the /deposit is unknown
- *         to the exchange
- */
-static void
-handle_wtid_data (void *cls,
-                 const struct TALER_WireTransferIdentifierRawP *wtid,
-                  const struct TALER_Amount *coin_contribution,
-                  const struct TALER_Amount *coin_fee,
-                 struct GNUNET_TIME_Absolute execution_time)
-{
-  struct DepositWtidContext *ctx = cls;
-  struct TALER_Amount coin_delta;
-
-  if (NULL == wtid)
-  {
-    ctx->res = TEH_RESPONSE_reply_transfer_pending (ctx->connection,
-                                                    execution_time);
-  }
-  else
-  {
-    if (GNUNET_SYSERR ==
-        TALER_amount_subtract (&coin_delta,
-                               coin_contribution,
-                               coin_fee))
-    {
-      GNUNET_break (0);
-      ctx->res = TEH_RESPONSE_reply_internal_db_error (ctx->connection,
-                                                      
TALER_EC_TRACK_TRANSACTION_DB_FEE_INCONSISTENT);
-    }
-    else
-    {
-      ctx->res = TEH_RESPONSE_reply_track_transaction (ctx->connection,
-                                                       &ctx->h_contract_terms,
-                                                       &ctx->h_wire,
-                                                       &ctx->coin_pub,
-                                                       &coin_delta,
-                                                       wtid,
-                                                       execution_time);
-    }
-  }
-}
-
-
-/**
- * Execute a "/track/transaction".  Returns the transfer information
- * associated with the given deposit.
- *
- * @param connection the MHD connection to handle
- * @param h_contract_terms hash of the proposal data
- * @param h_wire hash of the wire details
- * @param coin_pub public key of the coin to link
- * @param merchant_pub public key of the merchant
- * @return MHD result code
- */
-int
-TEH_DB_execute_track_transaction (struct MHD_Connection *connection,
-                                  const struct GNUNET_HashCode 
*h_contract_terms,
-                                  const struct GNUNET_HashCode *h_wire,
-                                  const struct TALER_CoinSpendPublicKeyP 
*coin_pub,
-                                  const struct TALER_MerchantPublicKeyP 
*merchant_pub)
-{
-  int ret;
-  struct DepositWtidContext ctx;
-  struct TALER_EXCHANGEDB_Session *session;
-
-  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);
-  }
-  ctx.connection = connection;
-  ctx.h_contract_terms = *h_contract_terms;
-  ctx.h_wire = *h_wire;
-  ctx.coin_pub = *coin_pub;
-  ctx.res = GNUNET_SYSERR;
-  ret = TEH_plugin->wire_lookup_deposit_wtid (TEH_plugin->cls,
-                                              session,
-                                             h_contract_terms,
-                                             h_wire,
-                                             coin_pub,
-                                             merchant_pub,
-                                             &handle_wtid_data,
-                                             &ctx);
-  if (GNUNET_SYSERR == ret)
-  {
-    GNUNET_break (0);
-    GNUNET_break (GNUNET_SYSERR == ctx.res);
-    return TEH_RESPONSE_reply_internal_db_error (connection,
-                                                
TALER_EC_TRACK_TRANSACTION_DB_FETCH_FAILED);
-  }
-  if (GNUNET_NO == ret)
-  {
-    GNUNET_break (GNUNET_SYSERR == ctx.res);
-    return TEH_RESPONSE_reply_transaction_unknown (connection,
-                                                  
TALER_EC_TRACK_TRANSACTION_NOT_FOUND);
-  }
-  if (GNUNET_SYSERR == ctx.res)
-  {
-    GNUNET_break (0);
-    return TEH_RESPONSE_reply_internal_error (connection,
-                                             
TALER_EC_TRACK_TRANSACTION_WTID_RESOLUTION_ERROR,
-                                              "bug resolving deposit wtid");
-  }
-  return ctx.res;
-}
-
-
-/**
- * Execute a "/payback".  The validity of the coin and signature have
- * already been checked.  The database must now check that the coin is
- * not (double) spent, and execute the transaction (record details,
- * generate success or failure response).
- *
- * @param connection the MHD connection to handle
- * @param coin information about the coin
- * @param value how much are coins of the @a coin's denomination worth?
- * @param h_blind blinded coin to use for the lookup
- * @param coin_blind blinding factor used (for later verification by the 
auditor)
- * @param coin_sig signature of the coin (to be stored)
- * @return MHD result code
- */
-int
-TEH_DB_execute_payback (struct MHD_Connection *connection,
-                        const struct TALER_CoinPublicInfo *coin,
-                        const struct TALER_Amount *value,
-                        const struct GNUNET_HashCode *h_blind,
-                        const struct TALER_DenominationBlindingKeyP 
*coin_blind,
-                        const struct TALER_CoinSpendSignatureP *coin_sig)
-{
-  int ret;
-  struct TALER_EXCHANGEDB_Session *session;
-  struct TALER_EXCHANGEDB_TransactionList *tl;
-  struct TALER_ReservePublicKeyP reserve_pub;
-  struct TALER_Amount amount;
-  struct TALER_Amount spent;
-  struct GNUNET_TIME_Absolute now;
-
-  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);
-
-  /* Check whether a payback is allowed, and if so, to which
-     reserve / account the money should go */
-  ret = TEH_plugin->get_reserve_by_h_blind (TEH_plugin->cls,
-                                            session,
-                                            h_blind,
-                                            &reserve_pub);
-  if (GNUNET_SYSERR == ret)
-  {
-    GNUNET_break (0);
-    TEH_plugin->rollback (TEH_plugin->cls,
-                          session);
-    return TEH_RESPONSE_reply_internal_db_error (connection,
-                                                
TALER_EC_PAYBACK_DB_FETCH_FAILED);
-  }
-  if (GNUNET_NO == ret)
-  {
-    GNUNET_break_op (0);
-    TEH_plugin->rollback (TEH_plugin->cls,
-                          session);
-    return TEH_RESPONSE_reply_payback_unknown (connection,
-                                               
TALER_EC_PAYBACK_WITHDRAW_NOT_FOUND);
-  }
-
-  /* Calculate remaining balance. */
-  tl = TEH_plugin->get_coin_transactions (TEH_plugin->cls,
-                                          session,
-                                          &coin->coin_pub);
-  TALER_amount_get_zero (value->currency,
-                         &spent);
-  if (GNUNET_OK !=
-      calculate_transaction_list_totals (tl,
-                                         &spent,
-                                         &spent))
-  {
-    GNUNET_break (0);
-    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_PAYBACK_HISTORY_DB_ERROR);
-  }
-  if (GNUNET_SYSERR ==
-      TALER_amount_subtract (&amount,
-                             value,
-                             &spent))
-  {
-    GNUNET_break (0);
-    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_PAYBACK_COIN_BALANCE_NEGATIVE);
-  }
-  if ( (0 == amount.fraction) &&
-       (0 == amount.value) )
-  {
-    TEH_plugin->rollback (TEH_plugin->cls,
-                          session);
-    ret = TEH_RESPONSE_reply_coin_insufficient_funds (connection,
-                                                      
TALER_EC_PAYBACK_COIN_BALANCE_ZERO,
-                                                      tl);
-    TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
-                                            tl);
-    return ret;
-  }
-  TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
-                                          tl);
-  now = GNUNET_TIME_absolute_get ();
-  (void) GNUNET_TIME_round_abs (&now);
-
-  /* add coin to list of wire transfers for payback */
-  ret = TEH_plugin->insert_payback_request (TEH_plugin->cls,
-                                            session,
-                                            &reserve_pub,
-                                            coin,
-                                            coin_sig,
-                                            coin_blind,
-                                            &amount,
-                                            h_blind,
-                                            now);
-  if (GNUNET_SYSERR == ret)
-  {
-    TALER_LOG_WARNING ("Failed to store /payback information in database\n");
-    TEH_plugin->rollback (TEH_plugin->cls,
-                          session);
-    return TEH_RESPONSE_reply_internal_db_error (connection,
-                                                
TALER_EC_PAYBACK_DB_PUT_FAILED);
-  }
-
-  COMMIT_TRANSACTION(session, connection);
-
-  return TEH_RESPONSE_reply_payback_success (connection,
-                                             &coin->coin_pub,
-                                             &reserve_pub,
-                                             &amount,
-                                             now);
-}
-
-
 /* end of taler-exchange-httpd_db.c */
diff --git a/src/exchange/taler-exchange-httpd_db.h 
b/src/exchange/taler-exchange-httpd_db.h
index 55faafa..7e342c8 100644
--- a/src/exchange/taler-exchange-httpd_db.h
+++ b/src/exchange/taler-exchange-httpd_db.h
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  Copyright (C) 2014, 2015 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 General Public License as published by the Free Software
@@ -24,239 +24,63 @@
 #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).
- *
- * @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);
-
-
-/**
- * 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
- */
-int
-TEH_DB_execute_refund (struct MHD_Connection *connection,
-                       const struct TALER_EXCHANGEDB_Refund *refund);
-
-
-/**
- * Execute a "/reserve/status".  Given the public key of a reserve,
- * return the associated transaction history.
- *
- * @param connection the MHD connection to handle
- * @param reserve_pub public key of the reserve to check
- * @return MHD result code
- */
-int
-TEH_DB_execute_reserve_status (struct MHD_Connection *connection,
-                               const struct TALER_ReservePublicKeyP 
*reserve_pub);
-
-
-/**
- * Execute a "/reserve/withdraw".  Given a reserve and a properly signed
- * request to withdraw a coin, check the balance of the reserve and
- * if it is sufficient, store the request and return the signed
- * blinded envelope.
- *
- * @param connection the MHD connection to handle
- * @param reserve public key of the reserve
- * @param denomination_pub public key of the denomination requested
- * @param blinded_msg blinded message to be signed
- * @param blinded_msg_len number of bytes in @a blinded_msg
- * @param signature signature over the withdraw request, to be stored in DB
- * @return MHD result code
- */
-int
-TEH_DB_execute_reserve_withdraw (struct MHD_Connection *connection,
-                                 const struct TALER_ReservePublicKeyP *reserve,
-                                 const struct TALER_DenominationPublicKey 
*denomination_pub,
-                                 const char *blinded_msg,
-                                 size_t blinded_msg_len,
-                                 const struct TALER_ReserveSignatureP 
*signature);
-
-
-/**
- * @brief Details about a melt operation of an individual coin.
- */
-struct TEH_DB_MeltDetails
-{
-
-  /**
-   * Information about the coin being melted.
-   */
-  struct TALER_CoinPublicInfo coin_info;
-
-  /**
-   * Signature allowing the melt (using
-   * a `struct TALER_EXCHANGEDB_RefreshMeltConfirmSignRequestBody`) to sign 
over.
-   */
-  struct TALER_CoinSpendSignatureP melt_sig;
-
-  /**
-   * How much of the coin's value did the client allow to be melted?
-   * This amount includes the fees, so the final amount contributed
-   * to the melt is this value minus the fee for melting the coin.
-   */
-  struct TALER_Amount melt_amount_with_fee;
-
-  /**
-   * What fee is earned by the exchange?  Set delayed during
-   * #verify_coin_public_info().
-   */
-  struct TALER_Amount melt_fee;
-};
-
-
-/**
- * Execute a "/refresh/melt". We have been given a list of valid
- * coins and a request to melt them into the given
- * @a refresh_session_pub.  Check that the coins all have the
- * required value left and if so, store that they have been
- * melted and confirm the melting operation to the client.
- *
- * @param connection the MHD connection to handle
- * @param session_hash hash code of the session the coins are melted into
- * @param num_new_denoms number of entries in @a denom_pubs, size of 
y-dimension of @a commit_coin array
- * @param denom_pubs array of public denomination keys for the refresh (?)
- * @param coin_melt_detail signatures and (residual) value of and information 
about the respective coin to be melted
- * @param commit_coin 2d array of coin commitments (what the exchange is to 
sign
- *                    once the "/refres/reveal" of cut and choose is done)
- * @param transfer_pubs array of transfer public keys (what the exchange is
- *                    to return via "/refresh/link" to enable linkage in the
- *                    future) of length #TALER_CNC_KAPPA
- * @return MHD result code
- */
-int
-TEH_DB_execute_refresh_melt (struct MHD_Connection *connection,
-                             const struct GNUNET_HashCode *session_hash,
-                             unsigned int num_new_denoms,
-                             const struct TALER_DenominationPublicKey 
*denom_pubs,
-                             const struct TEH_DB_MeltDetails *coin_melt_detail,
-                             struct TALER_EXCHANGEDB_RefreshCommitCoin *const* 
commit_coin,
-                             const struct TALER_TransferPublicKeyP 
*transfer_pubs);
-
-
-/**
- * Execute a "/refresh/reveal".  The client is revealing to us the
- * transfer keys for #TALER_CNC_KAPPA-1 sets of coins.  Verify that the
- * revealed transfer keys would allow linkage to the blinded coins,
- * and if so, return the signed coins for corresponding to the set of
- * coins that was not chosen.
- *
- * @param connection the MHD connection to handle
- * @param session_hash hash over the refresh session
- * @param transfer_privs array of length #TALER_CNC_KAPPA-1 with the revealed 
transfer keys
- * @return MHD result code
- */
-int
-TEH_DB_execute_refresh_reveal (struct MHD_Connection *connection,
-                               const struct GNUNET_HashCode *session_hash,
-                               struct TALER_TransferPrivateKeyP 
*transfer_privs);
-
-
 /**
- * Execute a "/refresh/link".  Returns the linkage information that
- * will allow the owner of a coin to follow the refresh trail to the
- * refreshed coin.
+ * 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 coin_pub public key of the coin to link
- * @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_refresh_link (struct MHD_Connection *connection,
-                             const struct TALER_CoinSpendPublicKeyP *coin_pub);
-
-
-
-/**
- * Add an incoming transaction to the database.
- *
- * @param connection the MHD connection to handle
- * @param reserve_pub public key of the reserve
- * @param amount amount to add to the reserve
- * @param execution_time when did we receive the wire transfer
- * @param sender_account_details which account send the funds
- * @param transfer_details information that uniquely identifies the transfer
- * @return MHD result code
- */
-int
-TEH_DB_execute_admin_add_incoming (struct MHD_Connection *connection,
-                                   const struct TALER_ReservePublicKeyP 
*reserve_pub,
-                                   const struct TALER_Amount *amount,
-                                   struct GNUNET_TIME_Absolute execution_time,
-                                   const json_t *sender_account_details,
-                                   const json_t *transfer_details);
+typedef enum GNUNET_DB_QueryStatus
+(*TEH_DB_TransactionCallback)(void *cls,
+                             struct MHD_Connection *connection,
+                             struct TALER_EXCHANGEDB_Session *session,
+                             int *mhd_ret);
 
 
 /**
- * Execute a "/track/transfer".  Returns the transaction information
- * associated with the given wire transfer identifier.
- *
- * @param connection the MHD connection to handle
- * @param wtid wire transfer identifier to resolve
- * @return MHD result code
- */
-int
-TEH_DB_execute_track_transfer (struct MHD_Connection *connection,
-                               const struct TALER_WireTransferIdentifierRawP 
*wtid);
-
-
-/**
- * Execute a "/track/transaction".  Returns the transfer information
- * associated with the given deposit.
- *
- * @param connection the MHD connection to handle
- * @param h_contract_terms hash of the contract
- * @param h_wire hash of the wire details
- * @param coin_pub public key of the coin to link
- * @param merchant_pub public key of the merchant
- * @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_track_transaction (struct MHD_Connection *connection,
-                                  const struct GNUNET_HashCode 
*h_contract_terms,
-                                  const struct GNUNET_HashCode *h_wire,
-                                  const struct TALER_CoinSpendPublicKeyP 
*coin_pub,
-                                  const struct TALER_MerchantPublicKeyP 
*merchant_pub);
+TEH_DB_run_transaction (struct MHD_Connection *connection,
+                       int *mhd_ret,
+                       TEH_DB_TransactionCallback cb,
+                       void *cb_cls);
 
 
 /**
- * Execute a "/payback".  The validity of the coin and signature have
- * already been checked.  The database must now check that the coin is
- * not (double) spent, and execute the transaction (record details,
- * generate success or failure response).
+ * 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 coin information about the coin
- * @param value how much are coins of the @a coin's denomination worth?
- * @param h_blind blinded coin to use for the lookup
- * @param coin_blind blinding factor used (for later verification by the 
auditor)
- * @param coin_sig signature of the coin
- * @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, i.e. exchangedb???
 int
-TEH_DB_execute_payback (struct MHD_Connection *connection,
-                        const struct TALER_CoinPublicInfo *coin,
-                        const struct TALER_Amount *value,
-                        const struct GNUNET_HashCode *h_blind,
-                        const struct TALER_DenominationBlindingKeyP 
*coin_blind,
-                        const struct TALER_CoinSpendSignatureP *coin_sig);
+TEH_DB_calculate_transaction_list_totals (struct 
TALER_EXCHANGEDB_TransactionList *tl,
+                                         const struct TALER_Amount *off,
+                                         struct TALER_Amount *ret);
 
 
 #endif
diff --git a/src/exchange/taler-exchange-httpd_deposit.c 
b/src/exchange/taler-exchange-httpd_deposit.c
index 85504d8..ccbd775 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,177 @@
 
 
 /**
+ * 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 */
+  qs = TEH_plugin->get_coin_transactions (TEH_plugin->cls,
+                                          session,
+                                          &deposit->coin.coin_pub,
+                                         &tl);
+  if (0 > qs)
+    return qs;
+  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 +222,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 +253,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 +411,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 +424,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 +437,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_keystate.c 
b/src/exchange/taler-exchange-httpd_keystate.c
index 3f81b2e..ec0a470 100644
--- a/src/exchange/taler-exchange-httpd_keystate.c
+++ b/src/exchange/taler-exchange-httpd_keystate.c
@@ -308,6 +308,81 @@ handle_signal (int signal_number)
 
 
 /**
+ * Closure for #add_revocations_transaction().
+ */
+struct AddRevocationContext
+{
+  /**
+   * Denomination key that is revoked.
+   */
+  const struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki;
+
+  /**
+   * Signature affirming the revocation.
+   */
+  const struct TALER_MasterSignatureP *revocation_master_sig;
+};
+
+
+/**
+ * Execute transaction to add revocations.  
+ *
+ * @param cls closure with the `struct AddRevocationContext *`
+ * @param connection NULL
+ * @param session database session to use
+ * @param[out] mhd_ret NULL
+ * @return transaction status
+ */
+static enum GNUNET_DB_QueryStatus
+add_revocations_transaction (void *cls,
+                            struct MHD_Connection *connection,
+                            struct TALER_EXCHANGEDB_Session *session,
+                            int *mhd_ret)
+{
+  struct AddRevocationContext *arc = cls;
+  
+  return TEH_plugin->insert_denomination_revocation (TEH_plugin->cls,
+                                                    session,
+                                                    
&arc->dki->issue.properties.denom_hash,
+                                                    
arc->revocation_master_sig);
+}
+
+
+/**
+ * Execute transaction to add a denomination to the DB.  
+ *
+ * @param cls closure with the `const struct 
TALER_EXCHANGEDB_DenominationKeyIssueInformation *`
+ * @param connection NULL
+ * @param session database session to use
+ * @param[out] mhd_ret NULL
+ * @return transaction status
+ */
+static enum GNUNET_DB_QueryStatus
+add_denomination_transaction (void *cls,
+                             struct MHD_Connection *connection,
+                             struct TALER_EXCHANGEDB_Session *session,
+                             int *mhd_ret)
+{
+  const struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki = cls;
+  enum GNUNET_DB_QueryStatus qs;
+  struct TALER_EXCHANGEDB_DenominationKeyInformationP issue_exists;
+
+  qs = TEH_plugin->get_denomination_info (TEH_plugin->cls,
+                                         session,
+                                         &dki->denom_pub,
+                                         &issue_exists);
+  if (0 > qs)
+    return qs;
+  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
+    return qs;
+  return TEH_plugin->insert_denomination_info (TEH_plugin->cls,
+                                              session,
+                                              &dki->denom_pub,
+                                              &dki->issue);
+}
+
+
+/**
  * Iterator for (re)loading/initializing denomination keys.
  *
  * @param cls closure
@@ -330,7 +405,6 @@ reload_keys_denom_iter (void *cls,
   struct GNUNET_TIME_Absolute horizon;
   struct GNUNET_TIME_Absolute expire_deposit;
   struct GNUNET_HashCode denom_key_hash;
-  struct TALER_EXCHANGEDB_Session *session;
   int res;
 
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
@@ -355,16 +429,12 @@ reload_keys_denom_iter (void *cls,
     return GNUNET_OK;
   }
 
-  session = TEH_plugin->get_session (TEH_plugin->cls);
-  if (NULL == session)
-    return GNUNET_SYSERR;
-
   if (NULL != revocation_master_sig)
   {
-    unsigned int thresh = 0;
-
+    struct AddRevocationContext arc;
+    
     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                "Adding denomination key `%s' to revokation set\n",
+                "Adding denomination key `%s' to revocation set\n",
                 alias);
     res = store_in_map (ctx->revoked_map,
                         dki);
@@ -373,45 +443,20 @@ reload_keys_denom_iter (void *cls,
     /* Try to insert DKI into DB until we succeed; note that if the DB
        failure is persistent, we need to die, as we cannot continue
        without the DKI being in the DB). */
-    res = GNUNET_SYSERR;
-    while (GNUNET_OK != res)
+    arc.dki = dki;
+    arc.revocation_master_sig = revocation_master_sig;
+    if (GNUNET_OK !=
+       TEH_DB_run_transaction (NULL,
+                               NULL,
+                               &add_revocations_transaction,
+                               &arc))
     {
-      thresh++;
-      if (thresh > 16)
-      {
-        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                    "Giving up, this is fatal. Committing suicide via 
SIGTERM.\n");
-        handle_signal (SIGTERM);
-        return GNUNET_SYSERR;
-      }
-      res = TEH_plugin->start (TEH_plugin->cls,
-                               session);
-      if (GNUNET_OK != res)
-      {
-        /* Transaction start failed!? Very bad error, log and retry */
-        GNUNET_break (0);
-        continue;
-      }
-      res = TEH_plugin->insert_denomination_revocation (TEH_plugin->cls,
-                                                        session,
-                                                        
&dki->issue.properties.denom_hash,
-                                                        revocation_master_sig);
-      if (GNUNET_SYSERR == res)
-      {
-        GNUNET_break (0);
-        TEH_plugin->rollback (TEH_plugin->cls,
-                              session);
-        continue;
-      }
-      if (GNUNET_NO == res)
-      {
-        TEH_plugin->rollback (TEH_plugin->cls,
-                              session);
-        break; /* already in is also OK! */
-      }
-      res = TEH_plugin->commit (TEH_plugin->cls,
-                                session);
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                 "Giving up, this is fatal. Committing suicide via 
SIGTERM.\n");
+      handle_signal (SIGTERM);
+      return GNUNET_SYSERR;      
     }
+       
     GNUNET_assert (0 ==
                    json_array_append_new (ctx->payback_array,
                                           GNUNET_JSON_from_data_auto 
(&dki->issue.properties.denom_hash)));
@@ -435,63 +480,16 @@ reload_keys_denom_iter (void *cls,
                                    sizeof (struct GNUNET_HashCode));
 
 
-
-  session = TEH_plugin->get_session (TEH_plugin->cls);
-  if (NULL == session)
-    return GNUNET_SYSERR;
-  /* Try to insert DKI into DB until we succeed; note that if the DB
-     failure is persistent, this code may loop forever (as there is no
-     sane alternative, we cannot continue without the DKI being in the
-     DB). */
-  res = GNUNET_SYSERR;
-  while (GNUNET_OK != res)
+  if (GNUNET_OK !=
+      TEH_DB_run_transaction (NULL,
+                             NULL,
+                             &add_denomination_transaction,
+                             (void *) dki))
   {
-    res = TEH_plugin->start (TEH_plugin->cls,
-                             session);
-    if (GNUNET_OK != res)
-    {
-      /* Transaction start failed!? Very bad error, log and retry */
-      GNUNET_break (0);
-      continue;
-    }
-    res = TEH_plugin->get_denomination_info (TEH_plugin->cls,
-                                             session,
-                                             &dki->denom_pub,
-                                             NULL);
-    if (GNUNET_SYSERR == res)
-    {
-      /* Fetch failed!? Very bad error, log and retry */
-      GNUNET_break (0);
-      TEH_plugin->rollback (TEH_plugin->cls,
-                            session);
-      continue;
-    }
-    if (GNUNET_OK == res)
-    {
-      /* Record exists, we're good, just exit */
-      TEH_plugin->rollback (TEH_plugin->cls,
-                            session);
-      break;
-    }
-    res = TEH_plugin->insert_denomination_info (TEH_plugin->cls,
-                                                session,
-                                                &dki->denom_pub,
-                                                &dki->issue);
-    if (GNUNET_OK != res)
-    {
-      /* Insert failed!? Very bad error, log and retry */
-      GNUNET_break (0);
-      TEH_plugin->rollback (TEH_plugin->cls,
-                            session);
-      continue;
-    }
-    res = TEH_plugin->commit (TEH_plugin->cls,
-                              session);
-    /* If commit succeeded, we're done, otherwise we retry; this
-       time without logging, as theroetically commits can fail
-       in a transactional DB due to concurrent activities that
-       cannot be reconciled. This should be rare for DKIs, but
-       as it is possible we just retry until we succeed. */
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+               "Giving up, this is fatal. Committing suicide via SIGTERM.\n");
+    handle_signal (SIGTERM);
+    return GNUNET_SYSERR;      
   }
 
   res = store_in_map (ctx->denomkey_map,
@@ -663,7 +661,6 @@ reload_auditor_iter (void *cls,
                      const struct TALER_DenominationKeyValidityPS *dki)
 {
   struct TEH_KS_StateHandle *ctx = cls;
-  unsigned int i;
   unsigned int keep;
   const struct TALER_AuditorSignatureP *kept_asigs[dki_len];
   const struct TALER_DenominationKeyValidityPS *kept_dkis[dki_len];
@@ -680,7 +677,7 @@ reload_auditor_iter (void *cls,
   /* Filter the auditor information for those for which the
      keys actually match the denomination keys that are active right now */
   keep = 0;
-  for (i=0;i<dki_len;i++)
+  for (unsigned int i=0;i<dki_len;i++)
   {
     if (GNUNET_YES ==
         GNUNET_CONTAINER_multihashmap_contains (ctx->denomkey_map,
@@ -1276,8 +1273,36 @@ TEH_KS_handler_keys (struct TEH_RequestHandler *rh,
   char *json;
   size_t json_len;
   int comp;
+  const char *have;
+  struct GNUNET_TIME_Absolute last_issue_date;
 
+  have = MHD_lookup_connection_value (connection,
+                                     MHD_GET_ARGUMENT_KIND,
+                                     "last_issue_date");
+  if (NULL != have)
+  {
+    unsigned long long haven;
+    
+    if (1 !=
+       sscanf (have,
+               "%llu",
+               &haven))
+    {
+      GNUNET_break_op (0);
+      return TEH_RESPONSE_reply_arg_invalid (connection,
+                                            TALER_EC_KEYS_HAVE_NOT_NUMERIC,
+                                            "have");
+    }
+    last_issue_date.abs_value_us = (uint64_t) haven;
+  }
+  else
+  {
+    last_issue_date.abs_value_us = 0LLU;
+  }
+  
   key_state = TEH_KS_acquire ();
+  /* FIXME: #4840: compute /keys delta from last_issue_date */
+  (void) last_issue_date;
   comp = MHD_NO;
   if (NULL != key_state->keys_jsonz)
     comp = TEH_RESPONSE_can_compress (connection);
diff --git a/src/exchange/taler-exchange-httpd_payback.c 
b/src/exchange/taler-exchange-httpd_payback.c
index 7c5230d..8b4051c 100644
--- a/src/exchange/taler-exchange-httpd_payback.c
+++ b/src/exchange/taler-exchange-httpd_payback.c
@@ -35,6 +35,253 @@
 
 
 /**
+ * A wallet asked for /payback, but we do not know anything about the
+ * original withdraw operation specified. Generates a 404 reply.
+ *
+ * @param connection connection to the client
+ * @param ec Taler error code
+ * @return MHD result code
+ */
+static int
+reply_payback_unknown (struct MHD_Connection *connection,
+                      enum TALER_ErrorCode ec)
+{
+  return TEH_RESPONSE_reply_json_pack (connection,
+                                       MHD_HTTP_NOT_FOUND,
+                                       "{s:s, s:I}",
+                                       "error", "blinded coin unknown",
+                                      "code", (json_int_t) ec);
+}
+
+
+/**
+ * A wallet asked for /payback, return the successful response.
+ *
+ * @param connection connection to the client
+ * @param coin_pub coin for which we are processing the payback request
+ * @param reserve_pub public key of the reserve that will receive the payback
+ * @param amount the amount we will wire back
+ * @param timestamp when did the exchange receive the /payback request
+ * @return MHD result code
+ */
+static int
+reply_payback_success (struct MHD_Connection *connection,
+                      const struct TALER_CoinSpendPublicKeyP *coin_pub,
+                      const struct TALER_ReservePublicKeyP *reserve_pub,
+                      const struct TALER_Amount *amount,
+                      struct GNUNET_TIME_Absolute timestamp)
+{
+  struct TALER_PaybackConfirmationPS pc;
+  struct TALER_ExchangePublicKeyP pub;
+  struct TALER_ExchangeSignatureP sig;
+
+  pc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_PAYBACK);
+  pc.purpose.size = htonl (sizeof (struct TALER_PaybackConfirmationPS));
+  pc.timestamp = GNUNET_TIME_absolute_hton (timestamp);
+  TALER_amount_hton (&pc.payback_amount,
+                     amount);
+  pc.coin_pub = *coin_pub;
+  pc.reserve_pub = *reserve_pub;
+  TEH_KS_sign (&pc.purpose,
+               &pub,
+               &sig);
+  return TEH_RESPONSE_reply_json_pack (connection,
+                                       MHD_HTTP_OK,
+                                       "{s:o, s:o, s:o, s:o, s:o}",
+                                       "reserve_pub", 
GNUNET_JSON_from_data_auto (reserve_pub),
+                                       "timestamp", GNUNET_JSON_from_time_abs 
(timestamp),
+                                       "amount", TALER_JSON_from_amount 
(amount),
+                                       "exchange_sig", 
GNUNET_JSON_from_data_auto (&sig),
+                                       "exchange_pub", 
GNUNET_JSON_from_data_auto (&pub));
+}
+
+
+/**
+ * Closure for #payback_transaction.
+ */
+struct PaybackContext
+{
+  /**
+   * Hash of the blinded coin.
+   */
+  struct GNUNET_HashCode h_blind;
+
+  /**
+   * Full value of the coin.
+   */
+  struct TALER_Amount value;
+
+  /**
+   * Details about the coin.
+   */
+  const struct TALER_CoinPublicInfo *coin;
+  
+  /**
+   * Key used to blind the coin.
+   */
+  const struct TALER_DenominationBlindingKeyP *coin_bks;
+
+  /**
+   * Signature of the coin requesting payback.
+   */
+  const struct TALER_CoinSpendSignatureP *coin_sig;
+
+  /**
+   * Set by #payback_transaction() to the reserve that will
+   * receive the payback.
+   */
+  struct TALER_ReservePublicKeyP reserve_pub;
+
+  /**
+   * Set by #payback_transaction() to the amount that will be paid back
+   */
+  struct TALER_Amount amount;
+
+  /**
+   * Set by #payback_transaction to the timestamp when the payback
+   * was accepted.
+   */
+  struct GNUNET_TIME_Absolute now;
+
+};
+
+
+/**
+ * Execute a "/payback".  The validity of the coin and signature have
+ * already been checked.  The database must now check that the coin is
+ * not (double) spent, and execute the transaction.
+ *
+ * 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 the `struct PaybackContext *`
+ * @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 code
+ */
+static enum GNUNET_DB_QueryStatus
+payback_transaction (void *cls,
+                    struct MHD_Connection *connection,
+                    struct TALER_EXCHANGEDB_Session *session,
+                    int *mhd_ret)
+{
+  struct PaybackContext *pc = cls;
+  struct TALER_EXCHANGEDB_TransactionList *tl;
+  struct TALER_Amount spent;
+  enum GNUNET_DB_QueryStatus qs;
+  
+  /* Check whether a payback is allowed, and if so, to which
+     reserve / account the money should go */
+  qs = TEH_plugin->get_reserve_by_h_blind (TEH_plugin->cls,
+                                          session,
+                                          &pc->h_blind,
+                                          &pc->reserve_pub);
+  if (0 > qs)
+  {
+    if (GNUNET_DB_STATUS_HARD_ERROR == qs)
+    {
+      GNUNET_break (0);
+      *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+                                                      
TALER_EC_PAYBACK_DB_FETCH_FAILED);
+    }
+    return qs;
+  }
+  if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
+  {
+    GNUNET_break_op (0);
+    *mhd_ret = reply_payback_unknown (connection,
+                                     TALER_EC_PAYBACK_WITHDRAW_NOT_FOUND);
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  }
+
+  /* Calculate remaining balance. */
+  qs = TEH_plugin->get_coin_transactions (TEH_plugin->cls,
+                                          session,
+                                          &pc->coin->coin_pub,
+                                         &tl);
+  if (0 > qs)
+  {
+    if (GNUNET_DB_STATUS_HARD_ERROR == qs)
+    {
+      GNUNET_break (0);
+      *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+                                                      
TALER_EC_PAYBACK_DB_FETCH_FAILED);
+    }
+    return qs;
+  }
+  TALER_amount_get_zero (pc->value.currency,
+                         &spent);
+  if (GNUNET_OK !=
+      TEH_DB_calculate_transaction_list_totals (tl,
+                                               &spent,
+                                               &spent))
+  {
+    GNUNET_break (0);
+    TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
+                                            tl);
+    *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+                                                    
TALER_EC_PAYBACK_HISTORY_DB_ERROR);
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  }
+  if (GNUNET_SYSERR ==
+      TALER_amount_subtract (&pc->amount,
+                             &pc->value,
+                             &spent))
+  {
+    GNUNET_break (0);
+    TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
+                                            tl);
+    *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+                                                    
TALER_EC_PAYBACK_COIN_BALANCE_NEGATIVE);
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  }
+  if ( (0 == pc->amount.fraction) &&
+       (0 == pc->amount.value) )
+  {
+    TEH_plugin->rollback (TEH_plugin->cls,
+                          session);
+    *mhd_ret = TEH_RESPONSE_reply_coin_insufficient_funds (connection,
+                                                          
TALER_EC_PAYBACK_COIN_BALANCE_ZERO,
+                                                          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);
+  pc->now = GNUNET_TIME_absolute_get ();
+  (void) GNUNET_TIME_round_abs (&pc->now);
+
+  /* add coin to list of wire transfers for payback */
+  qs = TEH_plugin->insert_payback_request (TEH_plugin->cls,
+                                          session,
+                                          &pc->reserve_pub,
+                                          pc->coin,
+                                          pc->coin_sig,
+                                          pc->coin_bks,
+                                          &pc->amount,
+                                          &pc->h_blind,
+                                          pc->now);
+  if (0 > qs)
+  {
+    if (GNUNET_DB_STATUS_HARD_ERROR == qs)
+    {
+      TALER_LOG_WARNING ("Failed to store /payback information in database\n");
+      *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+                                                      
TALER_EC_PAYBACK_DB_PUT_FAILED);
+    }
+    return qs;
+  }
+  return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
+}
+
+
+/**
  * We have parsed the JSON information about the payback request. Do
  * some basic sanity checks (especially that the signature on the
  * request and coin is valid) and then execute the payback operation.
@@ -53,14 +300,14 @@ verify_and_execute_payback (struct MHD_Connection 
*connection,
                             const struct TALER_DenominationBlindingKeyP 
*coin_bks,
                             const struct TALER_CoinSpendSignatureP *coin_sig)
 {
+  struct PaybackContext pc;
   struct TEH_KS_StateHandle *key_state;
   const struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki;
   struct TALER_PaybackRequestPS pr;
-  struct TALER_Amount value;
-  struct GNUNET_HashCode h_blind;
   struct GNUNET_HashCode c_hash;
   char *coin_ev;
   size_t coin_ev_size;
+  int mhd_ret;
 
   /* check denomination exists and is in payback mode */
   key_state = TEH_KS_acquire ();
@@ -75,7 +322,7 @@ verify_and_execute_payback (struct MHD_Connection 
*connection,
                                           
TALER_EC_PAYBACK_DENOMINATION_KEY_UNKNOWN,
                                            "denom_pub");
   }
-  TALER_amount_ntoh (&value,
+  TALER_amount_ntoh (&pc.value,
                      &dki->issue.properties.value);
 
   /* check denomination signature */
@@ -127,15 +374,24 @@ verify_and_execute_payback (struct MHD_Connection 
*connection,
   }
   GNUNET_CRYPTO_hash (coin_ev,
                       coin_ev_size,
-                      &h_blind);
+                      &pc.h_blind);
   GNUNET_free (coin_ev);
 
-  return TEH_DB_execute_payback (connection,
-                                 coin,
-                                 &value,
-                                 &h_blind,
-                                 coin_bks,
-                                 coin_sig);
+  pc.coin_sig = coin_sig;
+  pc.coin_bks = coin_bks;
+  pc.coin = coin;
+  if (GNUNET_OK !=
+      TEH_DB_run_transaction (connection,
+                             &mhd_ret,
+                             &payback_transaction,
+                             &pc))
+    return mhd_ret;
+  
+  return reply_payback_success (connection,
+                               &coin->coin_pub,
+                               &pc.reserve_pub,
+                               &pc.amount,
+                               pc.now);
 }
 
 
diff --git a/src/exchange/taler-exchange-httpd_refresh.c 
b/src/exchange/taler-exchange-httpd_refresh.c
deleted file mode 100644
index 3a8875f..0000000
--- a/src/exchange/taler-exchange-httpd_refresh.c
+++ /dev/null
@@ -1,755 +0,0 @@
-/*
-  This file is part of TALER
-  Copyright (C) 2014, 2015, 2016 Inria & 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_refresh.c
- * @brief Handle /refresh/ requests
- * @author Florian Dold
- * @author Benedikt Mueller
- * @author Christian Grothoff
- */
-#include "platform.h"
-#include <gnunet/gnunet_util_lib.h>
-#include <jansson.h>
-#include <microhttpd.h>
-#include "taler-exchange-httpd_parsing.h"
-#include "taler-exchange-httpd_mhd.h"
-#include "taler-exchange-httpd_refresh.h"
-#include "taler-exchange-httpd_responses.h"
-#include "taler-exchange-httpd_keystate.h"
-
-
-/**
- * Handle a "/refresh/melt" request after the main JSON parsing has happened.
- * We now need to validate the coins being melted and the session signature
- * and then hand things of to execute the melt operation.
- *
- * @param connection the MHD connection to handle
- * @param num_new_denoms number of coins to be created, size of y-dimension of 
@a commit_link array
- * @param denom_pubs array of @a num_new_denoms keys
- * @param coin_melt_details melting details
- * @param session_hash hash over the data that the client commits to
- * @param commit_coin 2d array of coin commitments (what the exchange is to 
sign
- *                    once the "/refres/reveal" of cut and choose is done)
- * @param transfer_pubs array of transfer public keys (which the exchange is
- *                    to return via "/refresh/link" to enable linkage in the
- *                    future) of length #TALER_CNC_KAPPA
- * @return MHD result code
- */
-static int
-handle_refresh_melt_binary (struct MHD_Connection *connection,
-                            unsigned int num_new_denoms,
-                            const struct TALER_DenominationPublicKey 
*denom_pubs,
-                            const struct TEH_DB_MeltDetails *coin_melt_details,
-                            const struct GNUNET_HashCode *session_hash,
-                            struct TALER_EXCHANGEDB_RefreshCommitCoin *const* 
commit_coin,
-                            const struct TALER_TransferPublicKeyP 
*transfer_pubs)
-{
-  unsigned int i;
-  struct TEH_KS_StateHandle *key_state;
-  struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dk;
-  struct TALER_EXCHANGEDB_DenominationKeyInformationP *dki;
-  struct TALER_Amount cost;
-  struct TALER_Amount total_cost;
-  struct TALER_Amount value;
-  struct TALER_Amount fee_withdraw;
-  struct TALER_Amount fee_melt;
-  struct TALER_Amount total_melt;
-
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "/refresh/melt request for session %s\n",
-              GNUNET_h2s (session_hash));
-
-  GNUNET_assert (GNUNET_OK ==
-                 TALER_amount_get_zero (TEH_exchange_currency_string,
-                                        &total_cost));
-  key_state = TEH_KS_acquire ();
-  for (i=0;i<num_new_denoms;i++)
-  {
-    dk = TEH_KS_denomination_key_lookup (key_state,
-                                         &denom_pubs[i],
-                                         TEH_KS_DKU_WITHDRAW);
-    if (NULL == dk)
-    {
-      GNUNET_break_op (0);
-      TEH_KS_release (key_state);
-      return TEH_RESPONSE_reply_arg_invalid (connection,
-                                            
TALER_EC_REFRESH_MELT_FRESH_DENOMINATION_KEY_NOT_FOUND,
-                                             "new_denoms");
-    }
-    dki = &dk->issue;
-    TALER_amount_ntoh (&value,
-                       &dki->properties.value);
-    TALER_amount_ntoh (&fee_withdraw,
-                       &dki->properties.fee_withdraw);
-    if ( (GNUNET_OK !=
-          TALER_amount_add (&cost,
-                            &value,
-                            &fee_withdraw)) ||
-         (GNUNET_OK !=
-          TALER_amount_add (&total_cost,
-                            &cost,
-                            &total_cost)) )
-    {
-      GNUNET_break_op (0);
-      TEH_KS_release (key_state);
-      return TEH_RESPONSE_reply_internal_error (connection,
-                                               
TALER_EC_REFRESH_MELT_COST_CALCULATION_OVERFLOW,
-                                                "cost calculation failure");
-    }
-  }
-
-  dk = TEH_KS_denomination_key_lookup (key_state,
-                                       &coin_melt_details->coin_info.denom_pub,
-                                       TEH_KS_DKU_DEPOSIT);
-  if (NULL == dk)
-  {
-    GNUNET_break (0);
-    return TEH_RESPONSE_reply_arg_unknown (connection,
-                                          
TALER_EC_REFRESH_MELT_DENOMINATION_KEY_NOT_FOUND,
-                                          "denom_pub");
-  }
-  dki = &dk->issue;
-  TALER_amount_ntoh (&fee_melt,
-                     &dki->properties.fee_refresh);
-  if (GNUNET_OK !=
-      TALER_amount_subtract (&total_melt,
-                             &coin_melt_details->melt_amount_with_fee,
-                             &fee_melt))
-  {
-    GNUNET_break_op (0);
-    TEH_KS_release (key_state);
-    return TEH_RESPONSE_reply_external_error (connection,
-                                             
TALER_EC_REFRESH_MELT_FEES_EXCEED_CONTRIBUTION,
-                                              "Melt contribution below melting 
fee");
-  }
-  TEH_KS_release (key_state);
-  if (0 !=
-      TALER_amount_cmp (&total_cost,
-                        &total_melt))
-  {
-    GNUNET_break_op (0);
-    /* We require total value of coins being melted and
-       total value of coins being generated to match! */
-    return TEH_RESPONSE_reply_json_pack (connection,
-                                         MHD_HTTP_BAD_REQUEST,
-                                         "{s:s, s:I}",
-                                         "error", "value mismatch",
-                                        "code", (json_int_t) 
TALER_EC_REFRESH_MELT_FEES_MISSMATCH);
-  }
-  return TEH_DB_execute_refresh_melt (connection,
-                                      session_hash,
-                                      num_new_denoms,
-                                      denom_pubs,
-                                      coin_melt_details,
-                                      commit_coin,
-                                      transfer_pubs);
-}
-
-
-/**
- * Extract public coin information from a JSON object.
- *
- * @param connection the connection to send error responses to
- * @param coin_info the JSON object to extract the coin info from
- * @param[out] r_melt_detail set to details about the coin's melting 
permission (if valid)
- * @return #GNUNET_YES if coin public info in JSON was valid
- *         #GNUNET_NO JSON was invalid, response was generated
- *         #GNUNET_SYSERR on internal error
- */
-static int
-get_coin_public_info (struct MHD_Connection *connection,
-                      const json_t *coin_info,
-                      struct TEH_DB_MeltDetails *r_melt_detail)
-{
-  int ret;
-  struct TALER_CoinSpendSignatureP melt_sig;
-  struct TALER_DenominationSignature sig;
-  struct TALER_DenominationPublicKey pk;
-  struct TALER_Amount amount;
-  struct GNUNET_JSON_Specification spec[] = {
-    GNUNET_JSON_spec_fixed_auto ("coin_pub", 
&r_melt_detail->coin_info.coin_pub),
-    TALER_JSON_spec_denomination_signature ("denom_sig", &sig),
-    TALER_JSON_spec_denomination_public_key ("denom_pub", &pk),
-    GNUNET_JSON_spec_fixed_auto ("confirm_sig", &melt_sig),
-    TALER_JSON_spec_amount ("value_with_fee", &amount),
-    GNUNET_JSON_spec_end ()
-  };
-
-  ret = TEH_PARSE_json_data (connection,
-                             coin_info,
-                             spec);
-  if (GNUNET_OK != ret)
-  {
-    GNUNET_break_op (0);
-    return ret;
-  }
-  /* check exchange signature on the coin */
-  r_melt_detail->coin_info.denom_sig = sig;
-  r_melt_detail->coin_info.denom_pub = pk;
-  if (GNUNET_OK !=
-      TALER_test_coin_valid (&r_melt_detail->coin_info))
-  {
-    GNUNET_break_op (0);
-    GNUNET_JSON_parse_free (spec);
-    r_melt_detail->coin_info.denom_sig.rsa_signature = NULL;
-    r_melt_detail->coin_info.denom_pub.rsa_public_key = NULL;
-    return (MHD_YES ==
-            TEH_RESPONSE_reply_signature_invalid (connection,
-                                                 
TALER_EC_REFRESH_MELT_DENOMINATION_SIGNATURE_INVALID,
-                                                  "denom_sig"))
-      ? GNUNET_NO : GNUNET_SYSERR;
-  }
-  r_melt_detail->melt_sig = melt_sig;
-  r_melt_detail->melt_amount_with_fee = amount;
-  return GNUNET_OK;
-}
-
-
-/**
- * Verify that the signature shows that this coin is to be melted into
- * the given @a session_hash melting session, and that this is a valid
- * coin (we know the denomination key and the signature on it is
- * valid).  Essentially, this does all of the per-coin checks that can
- * be done before the transaction starts.
- *
- * @param connection the connection to send error responses to
- * @param session_hash hash over refresh session the coin is melted into
- * @param[in,out] melt_detail details about the coin's melting permission,
- *                            the `melt_fee` is updated
- * @return #GNUNET_YES if coin public info in JSON was valid
- *         #GNUNET_NO JSON was invalid, response was generated
- *         #GNUNET_SYSERR on internal error
- */
-static int
-verify_coin_public_info (struct MHD_Connection *connection,
-                         const struct GNUNET_HashCode *session_hash,
-                         struct TEH_DB_MeltDetails *melt_detail)
-{
-  struct TALER_RefreshMeltCoinAffirmationPS body;
-  struct TEH_KS_StateHandle *key_state;
-  struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki;
-  struct TALER_Amount fee_refresh;
-
-  /* FIXME: we lookup the dki twice during /refresh/melt.
-     This should be avoided. */
-  key_state = TEH_KS_acquire ();
-  dki = TEH_KS_denomination_key_lookup (key_state,
-                                        &melt_detail->coin_info.denom_pub,
-                                       TEH_KS_DKU_DEPOSIT);
-  if (NULL == dki)
-  {
-    TEH_KS_release (key_state);
-    TALER_LOG_WARNING ("Unknown denomination key in /refresh/melt request\n");
-    return TEH_RESPONSE_reply_arg_unknown (connection,
-                                          
TALER_EC_REFRESH_MELT_DENOMINATION_KEY_NOT_FOUND,
-                                           "denom_pub");
-  }
-  TALER_amount_ntoh (&fee_refresh,
-                     &dki->issue.properties.fee_refresh);
-  melt_detail->melt_fee = fee_refresh;
-  body.purpose.size = htonl (sizeof (struct 
TALER_RefreshMeltCoinAffirmationPS));
-  body.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_MELT);
-  body.session_hash = *session_hash;
-  TALER_amount_hton (&body.amount_with_fee,
-                     &melt_detail->melt_amount_with_fee);
-  TALER_amount_hton (&body.melt_fee,
-                     &fee_refresh);
-  body.coin_pub = melt_detail->coin_info.coin_pub;
-  if (TALER_amount_cmp (&fee_refresh,
-                        &melt_detail->melt_amount_with_fee) > 0)
-  {
-    GNUNET_break_op (0);
-    TEH_KS_release (key_state);
-    return (MHD_YES ==
-            TEH_RESPONSE_reply_external_error (connection,
-                                              
TALER_EC_REFRESH_MELT_AMOUNT_INSUFFICIENT,
-                                               "melt amount smaller than 
melting fee"))
-      ? GNUNET_NO : GNUNET_SYSERR;
-  }
-
-  TEH_KS_release (key_state);
-  if (GNUNET_OK !=
-      GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_MELT,
-                                  &body.purpose,
-                                  &melt_detail->melt_sig.eddsa_signature,
-                                  &melt_detail->coin_info.coin_pub.eddsa_pub))
-  {
-    GNUNET_break_op (0);
-    if (MHD_YES !=
-        TEH_RESPONSE_reply_signature_invalid (connection,
-                                             
TALER_EC_REFRESH_MELT_COIN_SIGNATURE_INVALID,
-                                              "confirm_sig"))
-      return GNUNET_SYSERR;
-    return GNUNET_NO;
-  }
-  return GNUNET_OK;
-}
-
-
-/**
- * Release memory from the @a commit_coin array.
- *
- * @param commit_coin array to release
- * @param kappa size of 1st dimension
- * @param num_new_coins size of 2nd dimension
- */
-static void
-free_commit_coins (struct TALER_EXCHANGEDB_RefreshCommitCoin **commit_coin,
-                   unsigned int kappa,
-                   unsigned int num_new_coins)
-{
-  unsigned int i;
-  unsigned int j;
-
-  for (i=0;i<kappa;i++)
-  {
-    if (NULL == commit_coin[i])
-      break;
-    for (j=0;j<num_new_coins;j++)
-      GNUNET_free_non_null (commit_coin[i][j].coin_ev);
-    GNUNET_free (commit_coin[i]);
-  }
-}
-
-
-/**
- * Handle a "/refresh/melt" request after the first parsing has happened.
- * We now need to validate the coins being melted and the session signature
- * and then hand things of to execute the melt operation.  This function
- * parses the JSON arrays and then passes processing on to
- * #handle_refresh_melt_binary().
- *
- * @param connection the MHD connection to handle
- * @param new_denoms array of denomination keys
- * @param melt_coin coin to melt
- * @param transfer_pubs #TALER_CNC_KAPPA-dimensional array of transfer keys
- * @param coin_evs #TALER_CNC_KAPPA-dimensional array of envelopes to sign
- * @return MHD result code
- */
-static int
-handle_refresh_melt_json (struct MHD_Connection *connection,
-                          const json_t *new_denoms,
-                          const json_t *melt_coin,
-                          const json_t *transfer_pubs,
-                          const json_t *coin_evs)
-{
-  int res;
-  unsigned int i;
-  unsigned int j;
-  struct TALER_DenominationPublicKey *denom_pubs;
-  unsigned int num_newcoins;
-  struct TEH_DB_MeltDetails coin_melt_details;
-  struct GNUNET_HashCode session_hash;
-  struct GNUNET_HashContext *hash_context;
-  struct TALER_EXCHANGEDB_RefreshCommitCoin *commit_coin[TALER_CNC_KAPPA];
-  struct TALER_TransferPublicKeyP transfer_pub[TALER_CNC_KAPPA];
-
-  /* For the signature check, we hash most of the inputs together
-     (except for the signatures on the coins). */
-  hash_context = GNUNET_CRYPTO_hash_context_start ();
-
-  for (i = 0; i < TALER_CNC_KAPPA; i++)
-  {
-    struct GNUNET_JSON_Specification trans_spec[] = {
-      GNUNET_JSON_spec_fixed_auto (NULL, &transfer_pub[i]),
-      GNUNET_JSON_spec_end ()
-    };
-
-    res = TEH_PARSE_json_array (connection,
-                                transfer_pubs,
-                                trans_spec,
-                                i, -1);
-    if (GNUNET_OK != res)
-    {
-      GNUNET_break_op (0);
-      res = (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
-      goto cleanup_hc;
-    }
-    GNUNET_CRYPTO_hash_context_read (hash_context,
-                                     &transfer_pub[i],
-                                     sizeof (struct TALER_TransferPublicKeyP));
-  }
-
-
-  num_newcoins = json_array_size (new_denoms);
-  denom_pubs = GNUNET_new_array (num_newcoins,
-                                 struct TALER_DenominationPublicKey);
-  for (i=0;i<num_newcoins;i++)
-  {
-    char *buf;
-    size_t buf_size;
-    struct GNUNET_JSON_Specification spec[] = {
-      TALER_JSON_spec_denomination_public_key (NULL,
-                                               &denom_pubs[i]),
-      GNUNET_JSON_spec_end ()
-    };
-
-    res = TEH_PARSE_json_array (connection,
-                                new_denoms,
-                                spec,
-                                i, -1);
-    if (GNUNET_OK != res)
-    {
-      res = (GNUNET_NO == res) ? MHD_YES : MHD_NO;
-      goto cleanup_denoms;
-    }
-    buf_size = GNUNET_CRYPTO_rsa_public_key_encode 
(denom_pubs[i].rsa_public_key,
-                                                    &buf);
-    GNUNET_CRYPTO_hash_context_read (hash_context,
-                                     buf,
-                                     buf_size);
-    GNUNET_free (buf);
-  }
-
-  {
-    /* decode JSON data on coin to melt */
-    struct TALER_AmountNBO melt_amount;
-
-    res = get_coin_public_info (connection,
-                                melt_coin,
-                                &coin_melt_details);
-    if (GNUNET_OK != res)
-    {
-      GNUNET_break_op (0);
-      res = (GNUNET_NO == res) ? MHD_YES : MHD_NO;
-      goto cleanup_melt_details;
-    }
-    TALER_amount_hton (&melt_amount,
-                       &coin_melt_details.melt_amount_with_fee);
-    GNUNET_CRYPTO_hash_context_read (hash_context,
-                                     &coin_melt_details.coin_info.coin_pub,
-                                     sizeof (struct 
TALER_CoinSpendPublicKeyP));
-    GNUNET_CRYPTO_hash_context_read (hash_context,
-                                     &melt_amount,
-                                     sizeof (struct TALER_AmountNBO));
-  }
-
-  /* parse JSON arrays into binary arrays and hash everything
-     together for the signature check */
-  memset (commit_coin,
-          0,
-          sizeof (commit_coin));
-  for (i = 0; i < TALER_CNC_KAPPA; i++)
-  {
-    commit_coin[i] = GNUNET_new_array (num_newcoins,
-                                       struct 
TALER_EXCHANGEDB_RefreshCommitCoin);
-    for (j = 0; j < num_newcoins; j++)
-    {
-      struct TALER_EXCHANGEDB_RefreshCommitCoin *rcc = &commit_coin[i][j];
-      struct GNUNET_JSON_Specification coin_spec[] = {
-        GNUNET_JSON_spec_varsize (NULL,
-                                  (void **) &rcc->coin_ev,
-                                  &rcc->coin_ev_size),
-        GNUNET_JSON_spec_end ()
-      };
-
-      res = TEH_PARSE_json_array (connection,
-                                  coin_evs,
-                                  coin_spec,
-                                  i, j, -1);
-      if (GNUNET_OK != res)
-      {
-        GNUNET_break_op (0);
-        res = (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
-        goto cleanup;
-      }
-
-      GNUNET_CRYPTO_hash_context_read (hash_context,
-                                       rcc->coin_ev,
-                                       rcc->coin_ev_size);
-    }
-  }
-
-  GNUNET_CRYPTO_hash_context_finish (hash_context,
-                                     &session_hash);
-  hash_context = NULL;
-  /* verify signature on coins to melt */
-  res = verify_coin_public_info (connection,
-                                 &session_hash,
-                                 &coin_melt_details);
-  if (GNUNET_OK != res)
-  {
-    GNUNET_break_op (0);
-    res = (GNUNET_NO == res) ? MHD_YES : MHD_NO;
-    goto cleanup;
-  }
-
-  /* execute commit */
-  res = handle_refresh_melt_binary (connection,
-                                    num_newcoins,
-                                    denom_pubs,
-                                    &coin_melt_details,
-                                    &session_hash,
-                                    commit_coin,
-                                    transfer_pub);
- cleanup:
-  free_commit_coins (commit_coin,
-                     TALER_CNC_KAPPA,
-                     num_newcoins);
- cleanup_melt_details:
-  if (NULL != coin_melt_details.coin_info.denom_pub.rsa_public_key)
-    GNUNET_CRYPTO_rsa_public_key_free 
(coin_melt_details.coin_info.denom_pub.rsa_public_key);
-  if (NULL != coin_melt_details.coin_info.denom_sig.rsa_signature)
-    GNUNET_CRYPTO_rsa_signature_free 
(coin_melt_details.coin_info.denom_sig.rsa_signature);
- cleanup_denoms:
-  if (NULL != denom_pubs)
-  {
-    for (j=0;j<num_newcoins;j++)
-      if (NULL != denom_pubs[j].rsa_public_key)
-        GNUNET_CRYPTO_rsa_public_key_free (denom_pubs[j].rsa_public_key);
-    GNUNET_free (denom_pubs);
-  }
- cleanup_hc:
-  if (NULL != hash_context)
-    GNUNET_CRYPTO_hash_context_abort (hash_context);
-  return res;
-}
-
-
-/**
- * Handle a "/refresh/melt" request.  Parses the request into the JSON
- * components and then hands things of to #handle_refresh_melt_json()
- * to validate the melted coins, the signature and execute the melt
- * using TEH_DB_execute_refresh_melt().
- *
- * @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_REFRESH_handler_refresh_melt (struct TEH_RequestHandler *rh,
-                                  struct MHD_Connection *connection,
-                                  void **connection_cls,
-                                  const char *upload_data,
-                                  size_t *upload_data_size)
-{
-  json_t *root;
-  json_t *new_denoms;
-  json_t *melt_coin;
-  json_t *coin_evs;
-  json_t *transfer_pubs;
-  int res;
-  struct GNUNET_JSON_Specification spec[] = {
-    GNUNET_JSON_spec_json ("new_denoms", &new_denoms),
-    GNUNET_JSON_spec_json ("melt_coin", &melt_coin),
-    GNUNET_JSON_spec_json ("coin_evs", &coin_evs),
-    GNUNET_JSON_spec_json ("transfer_pubs", &transfer_pubs),
-    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;
-
-  /* Determine dimensionality of the request (kappa, #old and #new coins) */
-  if (TALER_CNC_KAPPA != json_array_size (coin_evs))
-  {
-    GNUNET_break_op (0);
-    GNUNET_JSON_parse_free (spec);
-    return TEH_RESPONSE_reply_arg_invalid (connection,
-                                          
TALER_EC_REFRESH_MELT_CNC_COIN_ARRAY_SIZE_INVALID,
-                                           "coin_evs");
-  }
-  if (TALER_CNC_KAPPA != json_array_size (transfer_pubs))
-  {
-    GNUNET_break_op (0);
-    GNUNET_JSON_parse_free (spec);
-    return TEH_RESPONSE_reply_arg_invalid (connection,
-                                          
TALER_EC_REFRESH_MELT_CNC_TRANSFER_ARRAY_SIZE_INVALID,
-                                           "transfer_pubs");
-  }
-  res = handle_refresh_melt_json (connection,
-                                  new_denoms,
-                                  melt_coin,
-                                  transfer_pubs,
-                                  coin_evs);
-  GNUNET_JSON_parse_free (spec);
-  return res;
-}
-
-
-/**
- * Handle a "/refresh/reveal" request.   Parses the given JSON
- * transfer private keys and if successful, passes everything to
- * #TEH_DB_execute_refresh_reveal() which will verify that the
- * revealed information is valid then returns the signed refreshed
- * coins.
- *
- * @param connection the MHD connection to handle
- * @param session_hash hash identifying the melting session
- * @param tp_json private transfer keys in JSON format
- * @return MHD result code
-  */
-static int
-handle_refresh_reveal_json (struct MHD_Connection *connection,
-                            const struct GNUNET_HashCode *session_hash,
-                            const json_t *tp_json)
-{
-  struct TALER_TransferPrivateKeyP transfer_privs[TALER_CNC_KAPPA - 1];
-  unsigned int i;
-  int res;
-
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "reveal request for session %s\n",
-              GNUNET_h2s (session_hash));
-
-  res = GNUNET_OK;
-  for (i = 0; i < TALER_CNC_KAPPA - 1; i++)
-  {
-    struct GNUNET_JSON_Specification tp_spec[] = {
-      GNUNET_JSON_spec_fixed_auto (NULL, &transfer_privs[i]),
-      GNUNET_JSON_spec_end ()
-    };
-
-    if (GNUNET_OK != res)
-      break;
-    res = TEH_PARSE_json_array (connection,
-                                tp_json,
-                                tp_spec,
-                                i, -1);
-    GNUNET_break_op (GNUNET_OK == res);
-  }
-  if (GNUNET_OK != res)
-    res = (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
-  else
-    res = TEH_DB_execute_refresh_reveal (connection,
-                                        session_hash,
-                                        transfer_privs);
-  return res;
-}
-
-
-/**
- * Handle a "/refresh/reveal" request. This time, the client reveals
- * the private transfer keys except for the cut-and-choose value
- * returned from "/refresh/melt".  This function parses the revealed
- * keys and secrets and ultimately passes everything to
- * #TEH_DB_execute_refresh_reveal() which will verify that the
- * revealed information is valid then returns the signed refreshed
- * coins.
- *
- * @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_REFRESH_handler_refresh_reveal (struct TEH_RequestHandler *rh,
-                                    struct MHD_Connection *connection,
-                                    void **connection_cls,
-                                    const char *upload_data,
-                                    size_t *upload_data_size)
-{
-  struct GNUNET_HashCode session_hash;
-  int res;
-  json_t *root;
-  json_t *transfer_privs;
-  struct GNUNET_JSON_Specification spec[] = {
-    GNUNET_JSON_spec_fixed_auto ("session_hash", &session_hash),
-    GNUNET_JSON_spec_json ("transfer_privs", &transfer_privs),
-    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)
-  {
-    GNUNET_break_op (0);
-    return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
-  }
-  /* Determine dimensionality of the request (kappa and #old coins) */
-  /* Note we do +1 as 1 row (cut-and-choose!) is missing! */
-  if (TALER_CNC_KAPPA != json_array_size (transfer_privs) + 1)
-  {
-    GNUNET_JSON_parse_free (spec);
-    GNUNET_break_op (0);
-    return TEH_RESPONSE_reply_arg_invalid (connection,
-                                          
TALER_EC_REFRESH_REVEAL_CNC_TRANSFER_ARRAY_SIZE_INVALID,
-                                           "transfer_privs");
-  }
-  res = handle_refresh_reveal_json (connection,
-                                    &session_hash,
-                                    transfer_privs);
-  GNUNET_JSON_parse_free (spec);
-  return res;
-}
-
-
-/**
- * Handle a "/refresh/link" request.  Note that for "/refresh/link"
- * we do use a simple HTTP GET, and a HTTP POST!
- *
- * @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_REFRESH_handler_refresh_link (struct TEH_RequestHandler *rh,
-                                  struct MHD_Connection *connection,
-                                  void **connection_cls,
-                                  const char *upload_data,
-                                  size_t *upload_data_size)
-{
-  struct TALER_CoinSpendPublicKeyP coin_pub;
-  int res;
-
-  res = TEH_PARSE_mhd_request_arg_data (connection,
-                                        "coin_pub",
-                                        &coin_pub,
-                                        sizeof (struct 
TALER_CoinSpendPublicKeyP));
-  if (GNUNET_SYSERR == res)
-    return MHD_NO;
-  if (GNUNET_OK != res)
-    return MHD_YES;
-  return TEH_DB_execute_refresh_link (connection,
-                                      &coin_pub);
-}
-
-
-/* end of taler-exchange-httpd_refresh.c */
diff --git a/src/exchange/taler-exchange-httpd_refresh.h 
b/src/exchange/taler-exchange-httpd_refresh.h
deleted file mode 100644
index 61b3b4f..0000000
--- a/src/exchange/taler-exchange-httpd_refresh.h
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
-  This file is part of TALER
-  Copyright (C) 2014, 2015 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_refresh.h
- * @brief Handle /refresh/ requests
- * @author Florian Dold
- * @author Benedikt Mueller
- * @author Christian Grothoff
- */
-#ifndef TALER_EXCHANGE_HTTPD_REFRESH_H
-#define TALER_EXCHANGE_HTTPD_REFRESH_H
-
-#include <gnunet/gnunet_util_lib.h>
-#include <microhttpd.h>
-#include "taler-exchange-httpd.h"
-
-
-/**
- * Handle a "/refresh/melt" request.  Parses the request into the JSON
- * components and then hands things of to #handle_refresh_melt_json()
- * to validate the melted coins, the signature and execute the melt
- * using TEH_DB_execute_refresh_melt().
- *
- * @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_REFRESH_handler_refresh_melt (struct TEH_RequestHandler *rh,
-                                  struct MHD_Connection *connection,
-                                  void **connection_cls,
-                                  const char *upload_data,
-                                  size_t *upload_data_size);
-
-
-/**
- * Handle a "/refresh/reveal" request.  This time, the client reveals
- * the private transfer keys except for the cut-and-choose value
- * returned from "/refresh/commit".  This function parses the revealed
- * keys and secrets and ultimately passes everything to
- * #TEH_DB_execute_refresh_reveal() which will verify that the
- * revealed information is valid then returns the signed refreshed
- * coins.
- *
- * @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_REFRESH_handler_refresh_reveal (struct TEH_RequestHandler *rh,
-                                    struct MHD_Connection *connection,
-                                    void **connection_cls,
-                                    const char *upload_data,
-                                    size_t *upload_data_size);
-
-
-/**
- * Handle a "/refresh/link" request
- *
- * @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_REFRESH_handler_refresh_link (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_refresh_link.c 
b/src/exchange/taler-exchange-httpd_refresh_link.c
new file mode 100644
index 0000000..0be6998
--- /dev/null
+++ b/src/exchange/taler-exchange-httpd_refresh_link.c
@@ -0,0 +1,326 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2014-2017 Inria & 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_refresh_link.c
+ * @brief Handle /refresh/link requests
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <jansson.h>
+#include <microhttpd.h>
+#include "taler-exchange-httpd_parsing.h"
+#include "taler-exchange-httpd_mhd.h"
+#include "taler-exchange-httpd_refresh_link.h"
+#include "taler-exchange-httpd_responses.h"
+#include "taler-exchange-httpd_keystate.h"
+
+
+/**
+ * @brief Information for each session a coin was melted into.
+ */
+struct TEH_RESPONSE_LinkSessionInfo
+{
+  /**
+   * Transfer public key of the coin.
+   */
+  struct TALER_TransferPublicKeyP transfer_pub;
+
+  /**
+   * Linked data of coins being created in the session.
+   */
+  struct TALER_EXCHANGEDB_LinkDataList *ldl;
+
+};
+
+
+/**
+ * Closure for #handle_transfer_data().
+ */
+struct HTD_Context
+{
+
+  /**
+   * Public key of the coin that we are tracing.
+   */
+  struct TALER_CoinSpendPublicKeyP coin_pub;
+
+  /**
+   * Session link data we collect.
+   */
+  struct TEH_RESPONSE_LinkSessionInfo *sessions;
+
+  /**
+   * Database session. Nothing to do with @a sessions.
+   */
+  struct TALER_EXCHANGEDB_Session *session;
+
+  /**
+   * MHD connection, for queueing replies.
+   */
+  struct MHD_Connection *connection;
+
+  /**
+   * Number of sessions the coin was melted into.
+   */
+  unsigned int num_sessions;
+
+  /**
+   * How are we expected to proceed. #GNUNET_SYSERR if we
+   * failed to return an error (should return #MHD_NO).
+   * #GNUNET_NO if we succeeded in queueing an MHD error
+   * (should return #MHD_YES from #TEH_execute_refresh_link),
+   * #GNUNET_OK if we should call #reply_refresh_link_success().
+   */
+  int status;
+};
+
+
+/**
+ * Send a response for "/refresh/link".
+ *
+ * @param connection the connection to send the response to
+ * @param num_sessions number of sessions the coin was used in
+ * @param sessions array of @a num_session entries with
+ *                  information for each session
+ * @return a MHD result code
+ */
+static int
+reply_refresh_link_success (struct MHD_Connection *connection,
+                           unsigned int num_sessions,
+                           const struct TEH_RESPONSE_LinkSessionInfo *sessions)
+{
+  json_t *mlist;
+  int res;
+
+  mlist = json_array ();
+  for (unsigned int i=0;i<num_sessions;i++)
+  {
+    json_t *list = json_array ();
+    json_t *root;
+
+    for (const struct TALER_EXCHANGEDB_LinkDataList *pos = sessions[i].ldl;
+        NULL != pos;
+        pos = pos->next)
+    {
+      json_t *obj;
+
+      obj = json_object ();
+      json_object_set_new (obj,
+                           "denom_pub",
+                           GNUNET_JSON_from_rsa_public_key 
(pos->denom_pub.rsa_public_key));
+      json_object_set_new (obj,
+                           "ev_sig",
+                           GNUNET_JSON_from_rsa_signature 
(pos->ev_sig.rsa_signature));
+      GNUNET_assert (0 ==
+                     json_array_append_new (list,
+                                            obj));
+    }
+    root = json_object ();
+    json_object_set_new (root,
+                         "new_coins",
+                         list);
+    json_object_set_new (root,
+                         "transfer_pub",
+                         GNUNET_JSON_from_data_auto 
(&sessions[i].transfer_pub));
+    GNUNET_assert (0 ==
+                   json_array_append_new (mlist,
+                                          root));
+  }
+  res = TEH_RESPONSE_reply_json (connection,
+                                 mlist,
+                                 MHD_HTTP_OK);
+  json_decref (mlist);
+  return res;
+}
+
+
+/**
+ * Function called with the session hashes and transfer secret
+ * information for a given coin.  Gets the linkage data and
+ * builds the reply for the client.
+ *
+ *
+ * @param cls closure, a `struct HTD_Context`
+ * @param session_hash a session the coin was melted in
+ * @param transfer_pub public transfer key for the session
+ */
+static void
+handle_transfer_data (void *cls,
+                      const struct GNUNET_HashCode *session_hash,
+                      const struct TALER_TransferPublicKeyP *transfer_pub)
+{
+  struct HTD_Context *ctx = cls;
+  struct TALER_EXCHANGEDB_LinkDataList *ldl;
+  struct TEH_RESPONSE_LinkSessionInfo *lsi;
+  enum GNUNET_DB_QueryStatus qs;
+
+  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != ctx->status)
+    return;
+  ldl = NULL;
+  qs = TEH_plugin->get_link_data_list (TEH_plugin->cls,
+                                      ctx->session,
+                                      session_hash,
+                                      &ldl);
+  if (qs <= 0) 
+  {
+    if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
+      ctx->status = GNUNET_DB_STATUS_HARD_ERROR;
+    else
+      ctx->status = qs;
+    return;
+  }
+  GNUNET_assert (NULL != ldl);
+  GNUNET_array_grow (ctx->sessions,
+                     ctx->num_sessions,
+                     ctx->num_sessions + 1);
+  lsi = &ctx->sessions[ctx->num_sessions - 1];
+  lsi->transfer_pub = *transfer_pub;
+  lsi->ldl = ldl;
+}
+
+
+/**
+ * Free session data kept in @a ctx
+ *
+ * @param ctx context to clean up
+ */
+static void
+purge_context (struct HTD_Context *ctx)
+{
+  for (unsigned int i=0;i<ctx->num_sessions;i++)
+    TEH_plugin->free_link_data_list (TEH_plugin->cls,
+                                    ctx->sessions[i].ldl);
+  GNUNET_free_non_null (ctx->sessions);
+  ctx->sessions = NULL;
+  ctx->num_sessions = 0;
+}
+
+
+/**
+ * Execute a "/refresh/link".  Returns the linkage information that
+ * will allow the owner of a coin to follow the refresh trail to
+ * the refreshed coin.
+ *
+ * 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 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
+ */
+static enum GNUNET_DB_QueryStatus
+refresh_link_transaction (void *cls,
+                         struct MHD_Connection *connection,
+                         struct TALER_EXCHANGEDB_Session *session,
+                         int *mhd_ret)
+{
+  struct HTD_Context *ctx = cls;
+  enum GNUNET_DB_QueryStatus qs;
+
+  ctx->session = session;
+  ctx->status = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
+  qs = TEH_plugin->get_transfer (TEH_plugin->cls,
+                                session,
+                                &ctx->coin_pub,
+                                &handle_transfer_data,
+                                ctx);
+  ctx->session = NULL;
+  if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
+  {
+    *mhd_ret = TEH_RESPONSE_reply_arg_unknown (connection,
+                                              
TALER_EC_REFRESH_LINK_COIN_UNKNOWN,
+                                              "coin_pub");
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  }
+  if (0 < qs)
+  {
+    qs = ctx->status;
+    if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
+    {
+      *mhd_ret = TEH_RESPONSE_reply_json_pack (ctx->connection,
+                                              MHD_HTTP_NOT_FOUND,
+                                              "{s:s}",
+                                              "error",
+                                              "link data not found (link)");
+      return GNUNET_DB_STATUS_HARD_ERROR;
+    }
+    return qs;
+  }
+  purge_context (ctx);
+  return qs;
+}
+
+
+/**
+ * Handle a "/refresh/link" request.  Note that for "/refresh/link"
+ * we do use a simple HTTP GET, and a HTTP POST!
+ *
+ * @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_REFRESH_handler_refresh_link (struct TEH_RequestHandler *rh,
+                                  struct MHD_Connection *connection,
+                                  void **connection_cls,
+                                  const char *upload_data,
+                                  size_t *upload_data_size)
+{
+  int mhd_ret;
+  int res;
+  struct HTD_Context ctx;
+
+  memset (&ctx,
+         0,
+         sizeof (ctx));
+  res = TEH_PARSE_mhd_request_arg_data (connection,
+                                        "coin_pub",
+                                        &ctx.coin_pub,
+                                        sizeof (struct 
TALER_CoinSpendPublicKeyP));
+  if (GNUNET_SYSERR == res)
+    return MHD_NO;
+  if (GNUNET_OK != res)
+    return MHD_YES;
+  if (GNUNET_OK !=
+      TEH_DB_run_transaction (connection,
+                             &mhd_ret,
+                             &refresh_link_transaction,
+                             &ctx))
+  {
+    purge_context (&ctx);
+    return mhd_ret;
+  }
+  mhd_ret = reply_refresh_link_success (connection,
+                                       ctx.num_sessions,
+                                       ctx.sessions);
+  purge_context (&ctx);
+  return mhd_ret;
+}
+
+
+/* end of taler-exchange-httpd_refresh_link.c */
diff --git a/src/exchange/taler-exchange-httpd_deposit.h 
b/src/exchange/taler-exchange-httpd_refresh_link.h
similarity index 63%
copy from src/exchange/taler-exchange-httpd_deposit.h
copy to src/exchange/taler-exchange-httpd_refresh_link.h
index 7bfe572..037b0d3 100644
--- a/src/exchange/taler-exchange-httpd_deposit.h
+++ b/src/exchange/taler-exchange-httpd_refresh_link.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,14 +14,14 @@
   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
 */
 /**
- * @file taler-exchange-httpd_deposit.h
- * @brief Handle /deposit requests
+ * @file taler-exchange-httpd_refresh_link.h
+ * @brief Handle /refresh/link requests
  * @author Florian Dold
  * @author Benedikt Mueller
  * @author Christian Grothoff
  */
-#ifndef TALER_EXCHANGE_HTTPD_DEPOSIT_H
-#define TALER_EXCHANGE_HTTPD_DEPOSIT_H
+#ifndef TALER_EXCHANGE_HTTPD_REFRESH_LINK_H
+#define TALER_EXCHANGE_HTTPD_REFRESH_LINK_H
 
 #include <gnunet/gnunet_util_lib.h>
 #include <microhttpd.h>
@@ -29,9 +29,7 @@
 
 
 /**
- * Handle a "/deposit" request.  Parses the JSON, and, if successful,
- * checks the signatures.  If everything checks out, this will
- * ultimately lead to the "/deposit" being executed, or rejected.
+ * Handle a "/refresh/link" request
  *
  * @param rh context of the handler
  * @param connection the MHD connection to handle
@@ -41,10 +39,11 @@
  * @return MHD result code
   */
 int
-TEH_DEPOSIT_handler_deposit (struct TEH_RequestHandler *rh,
-                             struct MHD_Connection *connection,
-                             void **connection_cls,
-                             const char *upload_data,
-                             size_t *upload_data_size);
+TEH_REFRESH_handler_refresh_link (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_refresh_melt.c 
b/src/exchange/taler-exchange-httpd_refresh_melt.c
new file mode 100644
index 0000000..edcd849
--- /dev/null
+++ b/src/exchange/taler-exchange-httpd_refresh_melt.c
@@ -0,0 +1,918 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2014-2017 Inria & 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_refresh_melt.c
+ * @brief Handle /refresh/melt requests
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <jansson.h>
+#include <microhttpd.h>
+#include "taler-exchange-httpd_parsing.h"
+#include "taler-exchange-httpd_mhd.h"
+#include "taler-exchange-httpd_refresh_melt.h"
+#include "taler-exchange-httpd_responses.h"
+#include "taler-exchange-httpd_keystate.h"
+
+
+/**
+ * @brief Details about a melt operation of an individual coin.
+ */
+struct TEH_DB_MeltDetails
+{
+
+  /**
+   * Information about the coin being melted.
+   */
+  struct TALER_CoinPublicInfo coin_info;
+
+  /**
+   * Signature allowing the melt (using
+   * a `struct TALER_EXCHANGEDB_RefreshMeltConfirmSignRequestBody`) to sign 
over.
+   */
+  struct TALER_CoinSpendSignatureP melt_sig;
+
+  /**
+   * How much of the coin's value did the client allow to be melted?
+   * This amount includes the fees, so the final amount contributed
+   * to the melt is this value minus the fee for melting the coin.
+   */
+  struct TALER_Amount melt_amount_with_fee;
+
+  /**
+   * What fee is earned by the exchange?  Set delayed during
+   * #verify_coin_public_info().
+   */
+  struct TALER_Amount melt_fee;
+};
+
+
+/**
+ * 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
+ * contribution of the coin for the melt.  Thus, the exchange
+ * refuses the melt operation.
+ *
+ * @param connection the connection to send the response to
+ * @param coin_pub public key of the coin
+ * @param coin_value original value of the coin
+ * @param tl transaction history for the coin
+ * @param requested how much this coin was supposed to contribute, including 
fee
+ * @param residual remaining value of the coin (after subtracting @a tl)
+ * @return a MHD result code
+ */
+static int
+reply_refresh_melt_insufficient_funds (struct MHD_Connection *connection,
+                                      const struct TALER_CoinSpendPublicKeyP 
*coin_pub,
+                                      struct TALER_Amount coin_value,
+                                      struct TALER_EXCHANGEDB_TransactionList 
*tl,
+                                      const struct TALER_Amount *requested,
+                                      const struct TALER_Amount *residual)
+{
+  json_t *history;
+
+  history = TEH_RESPONSE_compile_transaction_history (tl);
+  if (NULL == history)
+    return TEH_RESPONSE_reply_internal_db_error (connection,
+                                                
TALER_EC_REFRESH_MELT_HISTORY_DB_ERROR_INSUFFICIENT_FUNDS);
+  return TEH_RESPONSE_reply_json_pack (connection,
+                                       MHD_HTTP_FORBIDDEN,
+                                       "{s:s, s:I, s:o, s:o, s:o, s:o, s:o}",
+                                       "error",
+                                      "insufficient funds",
+                                      "code",
+                                      (json_int_t) 
TALER_EC_REFRESH_MELT_INSUFFICIENT_FUNDS,
+                                       "coin_pub",
+                                       GNUNET_JSON_from_data_auto (coin_pub),
+                                       "original_value",
+                                       TALER_JSON_from_amount (&coin_value),
+                                       "residual_value",
+                                       TALER_JSON_from_amount (residual),
+                                       "requested_value",
+                                       TALER_JSON_from_amount (requested),
+                                       "history",
+                                       history);
+}
+
+
+/**
+ * Send a response to a "/refresh/melt" request.
+ *
+ * @param connection the connection to send the response to
+ * @param session_hash hash of the refresh session
+ * @param noreveal_index which index will the client not have to reveal
+ * @return a MHD status code
+ */
+static int
+reply_refresh_melt_success (struct MHD_Connection *connection,
+                           const struct GNUNET_HashCode *session_hash,
+                           uint16_t noreveal_index)
+{
+  struct TALER_RefreshMeltConfirmationPS body;
+  struct TALER_ExchangePublicKeyP pub;
+  struct TALER_ExchangeSignatureP sig;
+  json_t *sig_json;
+
+  body.purpose.size = htonl (sizeof (struct TALER_RefreshMeltConfirmationPS));
+  body.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_MELT);
+  body.session_hash = *session_hash;
+  body.noreveal_index = htons (noreveal_index);
+  body.reserved = htons (0);
+  TEH_KS_sign (&body.purpose,
+               &pub,
+               &sig);
+  sig_json = GNUNET_JSON_from_data_auto (&sig);
+  GNUNET_assert (NULL != sig_json);
+  return TEH_RESPONSE_reply_json_pack (connection,
+                                       MHD_HTTP_OK,
+                                       "{s:i, s:o, s:o}",
+                                       "noreveal_index", (int) noreveal_index,
+                                       "exchange_sig", sig_json,
+                                       "exchange_pub", 
GNUNET_JSON_from_data_auto (&pub));
+}
+
+
+/**
+ * Context for the /refresh/melt operation.
+ */
+struct RefreshMeltContext
+{
+
+  /**
+   * Key state that can be used to lookup keys.
+   */
+  struct TEH_KS_StateHandle *key_state;
+
+  /**
+   * Information about the denomination key of the coin being
+   * melted.
+   */ 
+  struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki;
+
+  /**
+   * Array of denominations of the fresh coins.
+   */
+  struct TALER_DenominationPublicKey *denom_pubs;
+
+  /**
+   * Number of new coins to be generated in the melt.
+   * Size of the @e denom_pubs array.
+   */
+  unsigned int num_newcoins;
+
+  /**
+   * Details about the coin to be melted.
+   */
+  struct TEH_DB_MeltDetails coin_melt_details;
+
+  /**
+   * Set to the session hash once the @e hash_context has finished.
+   */ 
+  struct GNUNET_HashCode session_hash;
+
+  /**
+   * Hash operation used to calculate the session hash.
+   */
+  struct GNUNET_HashContext *hash_context;
+
+  /**
+   * Committments to the blinded envelopes for the fresh coins.
+   */
+  struct TALER_EXCHANGEDB_RefreshCommitCoin *commit_coin[TALER_CNC_KAPPA];
+
+  /**
+   * Commmittments to the transfer public keys.
+   */
+  struct TALER_TransferPublicKeyP transfer_pub[TALER_CNC_KAPPA];
+
+  /**
+   * Initialized during #refresh_melt_transaction().
+   */
+  struct TALER_EXCHANGEDB_RefreshSession refresh_session;
+
+};
+
+
+/**
+ * Parse coin melt requests from a JSON object and write them to
+ * the database.
+ *
+ * @param connection the connection to send errors to
+ * @param session the database connection
+ * @param[in,out] rmc melt context
+ * @param[out] mhd_ret status code to return to MHD on hard error
+ * @return transaction status code
+ */
+static enum GNUNET_DB_QueryStatus 
+refresh_check_melt (struct MHD_Connection *connection,
+                    struct TALER_EXCHANGEDB_Session *session,
+                   struct RefreshMeltContext *rmc,
+                   int *mhd_ret)
+{
+  struct TALER_EXCHANGEDB_TransactionList *tl;
+  struct TALER_EXCHANGEDB_RefreshMelt *meltp = &rmc->refresh_session.melt;
+  struct TALER_Amount coin_value;
+  struct TALER_Amount coin_residual;
+  struct TALER_Amount spent;
+  enum GNUNET_DB_QueryStatus qs;
+
+  TALER_amount_ntoh (&coin_value,
+                     &rmc->dki->issue.properties.value);
+  /* fee for THIS transaction; the melt amount includes the fee! */
+  spent = rmc->coin_melt_details.melt_amount_with_fee;
+  /* add historic transaction costs of this coin */
+  qs = TEH_plugin->get_coin_transactions (TEH_plugin->cls,
+                                          session,
+                                          
&rmc->coin_melt_details.coin_info.coin_pub,
+                                         &tl);
+  if (0 > qs)
+  {
+    if (GNUNET_DB_STATUS_HARD_ERROR == qs)
+      *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+                                                      
TALER_EC_REFRESH_MELT_DB_FETCH_ERROR);
+    return qs;
+  }
+  if (GNUNET_OK !=
+      TEH_DB_calculate_transaction_list_totals (tl,
+                                               &spent,
+                                               &spent))
+  {
+    GNUNET_break (0);
+    TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
+                                            tl);
+    *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+                                                    
TALER_EC_REFRESH_MELT_COIN_HISTORY_COMPUTATION_FAILED);
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  }
+  /* Refuse to refresh when the coin's value is insufficient
+     for the cost of all transactions. */
+  if (TALER_amount_cmp (&coin_value,
+                        &spent) < 0)
+  {
+    GNUNET_assert (GNUNET_SYSERR !=
+                   TALER_amount_subtract (&coin_residual,
+                                          &spent,
+                                          
&rmc->coin_melt_details.melt_amount_with_fee));
+    *mhd_ret = reply_refresh_melt_insufficient_funds (connection,
+                                                     
&rmc->coin_melt_details.coin_info.coin_pub,
+                                                     coin_value,
+                                                     tl,
+                                                     
&rmc->coin_melt_details.melt_amount_with_fee,
+                                                     &coin_residual);
+    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);
+
+  meltp->coin = rmc->coin_melt_details.coin_info;
+  meltp->coin_sig = rmc->coin_melt_details.melt_sig;
+  meltp->session_hash = rmc->session_hash;
+  meltp->amount_with_fee = rmc->coin_melt_details.melt_amount_with_fee;
+  meltp->melt_fee = rmc->coin_melt_details.melt_fee;
+  return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
+}
+
+
+/**
+ * Execute a "/refresh/melt".  We have been given a list of valid
+ * coins and a request to melt them into the given @a
+ * refresh_session_pub.  Check that the coins all have the required
+ * value left and if so, store that they have been melted and confirm
+ * the melting operation to the client.
+ *
+ * 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 our `struct RefreshMeltContext`
+ * @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
+refresh_melt_transaction (void *cls,
+                         struct MHD_Connection *connection,
+                         struct TALER_EXCHANGEDB_Session *session,
+                         int *mhd_ret)
+{
+  struct RefreshMeltContext *rmc = cls;
+  enum GNUNET_DB_QueryStatus qs;
+
+  qs = TEH_plugin->get_refresh_session (TEH_plugin->cls,
+                                       session,
+                                       &rmc->session_hash,
+                                       &rmc->refresh_session);
+  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
+  {
+    *mhd_ret = reply_refresh_melt_success (connection,
+                                          &rmc->session_hash,
+                                          rmc->refresh_session.noreveal_index);
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  }
+  if (0 > qs)
+  {
+    if (GNUNET_DB_STATUS_HARD_ERROR == qs)
+      *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+                                                      
TALER_EC_REFRESH_MELT_DB_FETCH_ERROR);
+    return qs;
+  }
+
+  /* store 'global' session data */
+  rmc->refresh_session.num_newcoins = rmc->num_newcoins;
+  rmc->refresh_session.noreveal_index
+    = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_STRONG,
+                               TALER_CNC_KAPPA);
+
+  qs = refresh_check_melt (connection,
+                          session,
+                          rmc,
+                          mhd_ret);
+  if (0 > qs)
+    return qs;
+
+  if ( (0 >=
+       (qs = TEH_plugin->create_refresh_session (TEH_plugin->cls,
+                                                 session,
+                                                 &rmc->session_hash,
+                                                 &rmc->refresh_session))) ||
+       (0 >=
+       (qs = TEH_plugin->insert_refresh_order (TEH_plugin->cls,
+                                               session,
+                                               &rmc->session_hash,
+                                               rmc->num_newcoins,
+                                               rmc->denom_pubs))) ||
+       (0 >=
+       (qs = TEH_plugin->insert_refresh_commit_coins (TEH_plugin->cls,
+                                                      session,
+                                                      &rmc->session_hash,
+                                                      rmc->num_newcoins,
+                                                      
rmc->commit_coin[rmc->refresh_session.noreveal_index]))) ||
+       (0 >=
+       (qs = TEH_plugin->insert_refresh_transfer_public_key (TEH_plugin->cls,
+                                                             session,
+                                                             
&rmc->session_hash,
+                                                             
&rmc->transfer_pub[rmc->refresh_session.noreveal_index]))) )
+  {
+    if (GNUNET_DB_STATUS_SOFT_ERROR != qs)
+    {
+      *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+                                                      
TALER_EC_REFRESH_MELT_DB_STORE_SESSION_ERROR);
+      return GNUNET_DB_STATUS_HARD_ERROR;
+    }
+    return qs;
+  }
+  return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
+}
+
+
+/**
+ * Handle a "/refresh/melt" request after the main JSON parsing has
+ * happened.  We now need to validate the coins being melted and the
+ * session signature and then hand things of to execute the melt
+ * operation.
+ *
+ * @param connection the MHD connection to handle
+ * @param[out] mhd_ret set on failure to return value for MHD
+ * @param rmc information about the melt to process
+ * @return MHD result code
+ */
+static int
+refresh_melt_prepare (struct MHD_Connection *connection,
+                     int *mhd_ret,
+                     struct RefreshMeltContext *rmc)
+{
+  struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dk;
+  struct TALER_EXCHANGEDB_DenominationKeyInformationP *dki;
+  struct TALER_Amount cost;
+  struct TALER_Amount total_cost;
+  struct TALER_Amount value;
+  struct TALER_Amount fee_withdraw;
+  struct TALER_Amount fee_melt;
+  struct TALER_Amount total_melt;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "/refresh/melt request for session %s\n",
+              GNUNET_h2s (&rmc->session_hash));
+  
+  GNUNET_assert (GNUNET_OK ==
+                 TALER_amount_get_zero (TEH_exchange_currency_string,
+                                        &total_cost));
+  for (unsigned int i=0;i<rmc->num_newcoins;i++)
+  {
+    dk = TEH_KS_denomination_key_lookup (rmc->key_state,
+                                         &rmc->denom_pubs[i],
+                                         TEH_KS_DKU_WITHDRAW);
+    if (NULL == dk)
+    {
+      GNUNET_break_op (0);
+      *mhd_ret = TEH_RESPONSE_reply_arg_invalid (connection,
+                                                
TALER_EC_REFRESH_MELT_FRESH_DENOMINATION_KEY_NOT_FOUND,
+                                                "new_denoms");
+      return GNUNET_SYSERR;
+    }
+    dki = &dk->issue;
+    TALER_amount_ntoh (&value,
+                       &dki->properties.value);
+    TALER_amount_ntoh (&fee_withdraw,
+                       &dki->properties.fee_withdraw);
+    if ( (GNUNET_OK !=
+          TALER_amount_add (&cost,
+                            &value,
+                            &fee_withdraw)) ||
+         (GNUNET_OK !=
+          TALER_amount_add (&total_cost,
+                            &cost,
+                            &total_cost)) )
+    {
+      GNUNET_break_op (0);
+      *mhd_ret = TEH_RESPONSE_reply_internal_error (connection,
+                                                   
TALER_EC_REFRESH_MELT_COST_CALCULATION_OVERFLOW,
+                                                   "cost calculation failure");
+      return GNUNET_SYSERR;
+    }
+  }
+
+  dki = &rmc->dki->issue;
+  TALER_amount_ntoh (&fee_melt,
+                     &dki->properties.fee_refresh);
+  if (GNUNET_OK !=
+      TALER_amount_subtract (&total_melt,
+                             &rmc->coin_melt_details.melt_amount_with_fee,
+                             &fee_melt))
+  {
+    GNUNET_break_op (0);
+    *mhd_ret = TEH_RESPONSE_reply_external_error (connection,
+                                                 
TALER_EC_REFRESH_MELT_FEES_EXCEED_CONTRIBUTION,
+                                                 "Melt contribution below 
melting fee");
+    return GNUNET_SYSERR;
+  }
+  if (0 !=
+      TALER_amount_cmp (&total_cost,
+                        &total_melt))
+  {
+    GNUNET_break_op (0);
+    /* We require total value of coins being melted and
+       total value of coins being generated to match! */
+    *mhd_ret = TEH_RESPONSE_reply_json_pack (connection,
+                                            MHD_HTTP_BAD_REQUEST,
+                                            "{s:s, s:I}",
+                                            "error", "value mismatch",
+                                            "code", (json_int_t) 
TALER_EC_REFRESH_MELT_FEES_MISSMATCH);
+    return GNUNET_SYSERR;
+  }
+  return TEH_DB_run_transaction (connection,
+                                mhd_ret,
+                                &refresh_melt_transaction,
+                                rmc);
+}
+
+
+/**
+ * Extract public coin information from a JSON object.
+ *
+ * @param connection the connection to send error responses to
+ * @param coin_info the JSON object to extract the coin info from
+ * @param[out] r_melt_detail set to details about the coin's melting 
permission (if valid)
+ * @return #GNUNET_YES if coin public info in JSON was valid
+ *         #GNUNET_NO JSON was invalid, response was generated
+ *         #GNUNET_SYSERR on internal error
+ */
+static int
+get_coin_public_info (struct MHD_Connection *connection,
+                      const json_t *coin_info,
+                      struct TEH_DB_MeltDetails *r_melt_detail)
+{
+  int ret;
+  struct TALER_CoinSpendSignatureP melt_sig;
+  struct TALER_DenominationSignature sig;
+  struct TALER_DenominationPublicKey pk;
+  struct TALER_Amount amount;
+  struct GNUNET_JSON_Specification spec[] = {
+    GNUNET_JSON_spec_fixed_auto ("coin_pub", 
&r_melt_detail->coin_info.coin_pub),
+    TALER_JSON_spec_denomination_signature ("denom_sig", &sig),
+    TALER_JSON_spec_denomination_public_key ("denom_pub", &pk),
+    GNUNET_JSON_spec_fixed_auto ("confirm_sig", &melt_sig),
+    TALER_JSON_spec_amount ("value_with_fee", &amount),
+    GNUNET_JSON_spec_end ()
+  };
+
+  ret = TEH_PARSE_json_data (connection,
+                             coin_info,
+                             spec);
+  if (GNUNET_OK != ret)
+  {
+    GNUNET_break_op (0);
+    return ret;
+  }
+  /* check exchange signature on the coin */
+  r_melt_detail->coin_info.denom_sig = sig;
+  r_melt_detail->coin_info.denom_pub = pk;
+  if (GNUNET_OK !=
+      TALER_test_coin_valid (&r_melt_detail->coin_info))
+  {
+    GNUNET_break_op (0);
+    GNUNET_JSON_parse_free (spec);
+    r_melt_detail->coin_info.denom_sig.rsa_signature = NULL;
+    r_melt_detail->coin_info.denom_pub.rsa_public_key = NULL;
+    return (MHD_YES ==
+            TEH_RESPONSE_reply_signature_invalid (connection,
+                                                 
TALER_EC_REFRESH_MELT_DENOMINATION_SIGNATURE_INVALID,
+                                                  "denom_sig"))
+      ? GNUNET_NO : GNUNET_SYSERR;
+  }
+  r_melt_detail->melt_sig = melt_sig;
+  r_melt_detail->melt_amount_with_fee = amount;
+  return GNUNET_OK;
+}
+
+
+/**
+ * Release memory from the @a commit_coin array.
+ *
+ * @param commit_coin array to release
+ * @param kappa size of 1st dimension
+ * @param num_new_coins size of 2nd dimension
+ */
+static void
+free_commit_coins (struct TALER_EXCHANGEDB_RefreshCommitCoin **commit_coin,
+                   unsigned int kappa,
+                   unsigned int num_new_coins)
+{
+  for (unsigned int i=0;i<kappa;i++)
+  {
+    if (NULL == commit_coin[i])
+      break;
+    for (unsigned int j=0;j<num_new_coins;j++)
+      GNUNET_free_non_null (commit_coin[i][j].coin_ev);
+    GNUNET_free (commit_coin[i]);
+    commit_coin[i] = NULL;
+  }
+}
+
+
+/**
+ * Cleanup state kept in the @a rmc.
+ *
+ * @param rmc state to clean up; does not free @a rmc itself
+ */
+static void
+cleanup_rmc (struct RefreshMeltContext *rmc)
+{
+  free_commit_coins (rmc->commit_coin,
+                     TALER_CNC_KAPPA,
+                     rmc->num_newcoins);
+  if (NULL != rmc->coin_melt_details.coin_info.denom_pub.rsa_public_key)
+  {
+    GNUNET_CRYPTO_rsa_public_key_free 
(rmc->coin_melt_details.coin_info.denom_pub.rsa_public_key);
+    rmc->coin_melt_details.coin_info.denom_pub.rsa_public_key = NULL;
+  }
+  if (NULL != rmc->coin_melt_details.coin_info.denom_sig.rsa_signature)
+  {
+    GNUNET_CRYPTO_rsa_signature_free 
(rmc->coin_melt_details.coin_info.denom_sig.rsa_signature);
+    rmc->coin_melt_details.coin_info.denom_sig.rsa_signature = NULL;
+  }
+  if (NULL != rmc->denom_pubs)
+  {
+    for (unsigned int j=0;j<rmc->num_newcoins;j++)
+      if (NULL != rmc->denom_pubs[j].rsa_public_key)
+        GNUNET_CRYPTO_rsa_public_key_free (rmc->denom_pubs[j].rsa_public_key);
+    GNUNET_free (rmc->denom_pubs);
+    rmc->denom_pubs = NULL;
+  }
+  if (NULL != rmc->hash_context)
+  {
+    GNUNET_CRYPTO_hash_context_abort (rmc->hash_context);
+    rmc->hash_context = NULL;
+  }
+  if (NULL != rmc->key_state)
+  {
+    TEH_KS_release (rmc->key_state);
+    rmc->key_state = NULL;
+  }
+}
+
+
+/**
+ * Handle a "/refresh/melt" request after the first parsing has
+ * happened.  We now need to validate the coins being melted and the
+ * session signature and then hand things of to execute the melt
+ * operation.  This function parses the JSON arrays and then passes
+ * processing on to #handle_refresh_melt_binary().
+ *
+ * @param connection the MHD connection to handle
+ * @param new_denoms array of denomination keys
+ * @param melt_coin coin to melt
+ * @param transfer_pubs #TALER_CNC_KAPPA-dimensional array of transfer keys
+ * @param coin_evs #TALER_CNC_KAPPA-dimensional array of envelopes to sign
+ * @return MHD result code
+ */
+static int
+handle_refresh_melt_json (struct MHD_Connection *connection,
+                          const json_t *new_denoms,
+                          const json_t *melt_coin,
+                          const json_t *transfer_pubs,
+                          const json_t *coin_evs)
+{
+  int res;
+  int mhd_ret;
+  struct RefreshMeltContext rmc;
+
+  memset (&rmc,
+         0,
+         sizeof (rmc));
+  /* For the signature check, we hash most of the inputs together
+     (except for the signatures on the coins). */
+  rmc.hash_context = GNUNET_CRYPTO_hash_context_start ();
+  for (unsigned int i = 0; i < TALER_CNC_KAPPA; i++)
+  {
+    struct GNUNET_JSON_Specification trans_spec[] = {
+      GNUNET_JSON_spec_fixed_auto (NULL, &rmc.transfer_pub[i]),
+      GNUNET_JSON_spec_end ()
+    };
+
+    res = TEH_PARSE_json_array (connection,
+                                transfer_pubs,
+                                trans_spec,
+                                i, -1);
+    if (GNUNET_OK != res)
+    {
+      GNUNET_break_op (0);
+      mhd_ret = (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
+      cleanup_rmc (&rmc);
+      return mhd_ret;
+    }
+    GNUNET_CRYPTO_hash_context_read (rmc.hash_context,
+                                     &rmc.transfer_pub[i],
+                                     sizeof (struct TALER_TransferPublicKeyP));
+  }
+
+  rmc.num_newcoins = json_array_size (new_denoms);
+  rmc.denom_pubs = GNUNET_new_array (rmc.num_newcoins,
+                                    struct TALER_DenominationPublicKey);
+  for (unsigned int i=0;i<rmc.num_newcoins;i++)
+  {
+    char *buf;
+    size_t buf_size;
+    struct GNUNET_JSON_Specification spec[] = {
+      TALER_JSON_spec_denomination_public_key (NULL,
+                                               &rmc.denom_pubs[i]),
+      GNUNET_JSON_spec_end ()
+    };
+
+    res = TEH_PARSE_json_array (connection,
+                                new_denoms,
+                                spec,
+                                i,
+                               -1);
+    if (GNUNET_OK != res)
+    {
+      mhd_ret = (GNUNET_NO == res) ? MHD_YES : MHD_NO;
+      cleanup_rmc (&rmc);
+      return mhd_ret;
+    }
+    buf_size = GNUNET_CRYPTO_rsa_public_key_encode 
(rmc.denom_pubs[i].rsa_public_key,
+                                                    &buf);
+    GNUNET_CRYPTO_hash_context_read (rmc.hash_context,
+                                     buf,
+                                     buf_size);
+    GNUNET_free (buf);
+  }
+  
+  /* decode JSON data on coin to melt and check that this is a
+     valid coin */
+  {
+    struct TALER_AmountNBO melt_amount;
+
+    res = get_coin_public_info (connection,
+                                melt_coin,
+                                &rmc.coin_melt_details);
+    if (GNUNET_OK != res)
+    {
+      GNUNET_break_op (0);
+      mhd_ret = (GNUNET_NO == res) ? MHD_YES : MHD_NO;
+      cleanup_rmc (&rmc);
+      return mhd_ret;
+    }
+    TALER_amount_hton (&melt_amount,
+                       &rmc.coin_melt_details.melt_amount_with_fee);
+    GNUNET_CRYPTO_hash_context_read (rmc.hash_context,
+                                     &rmc.coin_melt_details.coin_info.coin_pub,
+                                     sizeof (struct 
TALER_CoinSpendPublicKeyP));
+    GNUNET_CRYPTO_hash_context_read (rmc.hash_context,
+                                     &melt_amount,
+                                     sizeof (struct TALER_AmountNBO));
+  }
+
+  /* parse JSON arrays into binary arrays and hash everything
+     together for the signature check */
+  for (unsigned int i = 0; i < TALER_CNC_KAPPA; i++)
+  {
+    rmc.commit_coin[i] = GNUNET_new_array (rmc.num_newcoins,
+                                          struct 
TALER_EXCHANGEDB_RefreshCommitCoin);
+    for (unsigned int j = 0; j < rmc.num_newcoins; j++)
+    {
+      struct TALER_EXCHANGEDB_RefreshCommitCoin *rcc = &rmc.commit_coin[i][j];
+      struct GNUNET_JSON_Specification coin_spec[] = {
+        GNUNET_JSON_spec_varsize (NULL,
+                                  (void **) &rcc->coin_ev,
+                                  &rcc->coin_ev_size),
+        GNUNET_JSON_spec_end ()
+      };
+
+      res = TEH_PARSE_json_array (connection,
+                                  coin_evs,
+                                  coin_spec,
+                                  i,
+                                 j,
+                                 -1);
+      if (GNUNET_OK != res)
+      {
+        GNUNET_break_op (0);
+        mhd_ret = (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
+        cleanup_rmc (&rmc);
+       return mhd_ret;
+      }
+
+      GNUNET_CRYPTO_hash_context_read (rmc.hash_context,
+                                       rcc->coin_ev,
+                                       rcc->coin_ev_size);
+    }
+  }
+
+  GNUNET_CRYPTO_hash_context_finish (rmc.hash_context,
+                                     &rmc.session_hash);
+  rmc.hash_context = NULL;
+
+  rmc.key_state = TEH_KS_acquire ();
+  rmc.dki = TEH_KS_denomination_key_lookup (rmc.key_state,
+                                           
&rmc.coin_melt_details.coin_info.denom_pub,
+                                           TEH_KS_DKU_DEPOSIT);
+  if (NULL == rmc.dki)
+  {
+    TEH_KS_release (rmc.key_state);
+    TALER_LOG_WARNING ("Unknown denomination key in /refresh/melt request\n");
+    return TEH_RESPONSE_reply_arg_unknown (connection,
+                                          
TALER_EC_REFRESH_MELT_DENOMINATION_KEY_NOT_FOUND,
+                                           "denom_pub");
+  }
+
+  /* verify signature of coin for melt operation */
+  {
+    struct TALER_RefreshMeltCoinAffirmationPS body;
+    struct TALER_Amount fee_refresh;
+
+    TALER_amount_ntoh (&fee_refresh,
+                      &rmc.dki->issue.properties.fee_refresh);
+    rmc.coin_melt_details.melt_fee = fee_refresh;
+    body.purpose.size = htonl (sizeof (struct 
TALER_RefreshMeltCoinAffirmationPS));
+    body.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_MELT);
+    body.session_hash = rmc.session_hash;
+    TALER_amount_hton (&body.amount_with_fee,
+                      &rmc.coin_melt_details.melt_amount_with_fee);
+    TALER_amount_hton (&body.melt_fee,
+                      &fee_refresh);
+    body.coin_pub = rmc.coin_melt_details.coin_info.coin_pub;
+    if (TALER_amount_cmp (&fee_refresh,
+                         &rmc.coin_melt_details.melt_amount_with_fee) > 0)
+    {
+      GNUNET_break_op (0);
+      cleanup_rmc (&rmc);
+      return TEH_RESPONSE_reply_external_error (connection,
+                                               
TALER_EC_REFRESH_MELT_AMOUNT_INSUFFICIENT,
+                                               "melt amount smaller than 
melting fee");
+    }
+    
+    if (GNUNET_OK !=
+       GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_MELT,
+                                   &body.purpose,
+                                   
&rmc.coin_melt_details.melt_sig.eddsa_signature,
+                                   
&rmc.coin_melt_details.coin_info.coin_pub.eddsa_pub))
+    {
+      GNUNET_break_op (0);
+      cleanup_rmc (&rmc);
+      return TEH_RESPONSE_reply_signature_invalid (connection,
+                                                  
TALER_EC_REFRESH_MELT_COIN_SIGNATURE_INVALID,
+                                                  "confirm_sig");
+    }
+  }
+    
+  /* prepare commit */
+  if (GNUNET_OK !=
+      refresh_melt_prepare (connection,
+                           &mhd_ret,
+                           &rmc))
+  {
+    cleanup_rmc (&rmc);
+    return mhd_ret;
+  }
+  mhd_ret = reply_refresh_melt_success (connection,
+                                       &rmc.session_hash,
+                                       rmc.refresh_session.noreveal_index);
+  cleanup_rmc (&rmc);
+  return mhd_ret;
+}
+
+
+/**
+ * Handle a "/refresh/melt" request.  Parses the request into the JSON
+ * components and then hands things of to #handle_refresh_melt_json()
+ * to validate the melted coins, the signature and execute the melt
+ * using TEH_DB_execute_refresh_melt().
+ *
+ * @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_REFRESH_handler_refresh_melt (struct TEH_RequestHandler *rh,
+                                  struct MHD_Connection *connection,
+                                  void **connection_cls,
+                                  const char *upload_data,
+                                  size_t *upload_data_size)
+{
+  json_t *root;
+  json_t *new_denoms;
+  json_t *melt_coin;
+  json_t *coin_evs;
+  json_t *transfer_pubs;
+  int res;
+  struct GNUNET_JSON_Specification spec[] = {
+    GNUNET_JSON_spec_json ("new_denoms", &new_denoms),
+    GNUNET_JSON_spec_json ("melt_coin", &melt_coin),
+    GNUNET_JSON_spec_json ("coin_evs", &coin_evs),
+    GNUNET_JSON_spec_json ("transfer_pubs", &transfer_pubs),
+    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;
+
+  /* Determine dimensionality of the request (kappa, #old and #new coins) */
+  if (TALER_CNC_KAPPA != json_array_size (coin_evs))
+  {
+    GNUNET_break_op (0);
+    GNUNET_JSON_parse_free (spec);
+    return TEH_RESPONSE_reply_arg_invalid (connection,
+                                          
TALER_EC_REFRESH_MELT_CNC_COIN_ARRAY_SIZE_INVALID,
+                                           "coin_evs");
+  }
+  if (TALER_CNC_KAPPA != json_array_size (transfer_pubs))
+  {
+    GNUNET_break_op (0);
+    GNUNET_JSON_parse_free (spec);
+    return TEH_RESPONSE_reply_arg_invalid (connection,
+                                          
TALER_EC_REFRESH_MELT_CNC_TRANSFER_ARRAY_SIZE_INVALID,
+                                           "transfer_pubs");
+  }
+  res = handle_refresh_melt_json (connection,
+                                  new_denoms,
+                                  melt_coin,
+                                  transfer_pubs,
+                                  coin_evs);
+  GNUNET_JSON_parse_free (spec);
+  return res;
+}
+
+
+/* end of taler-exchange-httpd_refresh_melt.c */
diff --git a/src/exchange/taler-exchange-httpd_refund.h 
b/src/exchange/taler-exchange-httpd_refresh_melt.h
similarity index 60%
copy from src/exchange/taler-exchange-httpd_refund.h
copy to src/exchange/taler-exchange-httpd_refresh_melt.h
index 1fa52b1..a938abf 100644
--- a/src/exchange/taler-exchange-httpd_refund.h
+++ b/src/exchange/taler-exchange-httpd_refresh_melt.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,14 +14,14 @@
   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_refresh_melt.h
+ * @brief Handle /refresh/melt 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_REFRESH_MELT_H
+#define TALER_EXCHANGE_HTTPD_REFRESH_MELT_H
 
 #include <gnunet/gnunet_util_lib.h>
 #include <microhttpd.h>
@@ -29,11 +29,10 @@
 
 
 /**
- * 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 "/refresh/melt" request.  Parses the request into the JSON
+ * components and then hands things of to #handle_refresh_melt_json()
+ * to validate the melted coins, the signature and execute the melt
+ * using TEH_DB_execute_refresh_melt().
  *
  * @param rh context of the handler
  * @param connection the MHD connection to handle
@@ -41,12 +40,13 @@
  * @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_REFUND_handler_refund (struct TEH_RequestHandler *rh,
-                          struct MHD_Connection *connection,
-                          void **connection_cls,
-                          const char *upload_data,
-                          size_t *upload_data_size);
+TEH_REFRESH_handler_refresh_melt (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_refresh_reveal.c 
b/src/exchange/taler-exchange-httpd_refresh_reveal.c
new file mode 100644
index 0000000..cfb2b68
--- /dev/null
+++ b/src/exchange/taler-exchange-httpd_refresh_reveal.c
@@ -0,0 +1,769 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2014-2017 Inria & 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_refresh_reveal.c
+ * @brief Handle /refresh/reveal requests
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <jansson.h>
+#include <microhttpd.h>
+#include "taler-exchange-httpd_parsing.h"
+#include "taler-exchange-httpd_mhd.h"
+#include "taler-exchange-httpd_refresh_reveal.h"
+#include "taler-exchange-httpd_responses.h"
+#include "taler-exchange-httpd_keystate.h"
+
+
+/**
+ * Send a response for "/refresh/reveal".
+ *
+ * @param connection the connection to send the response to
+ * @param num_newcoins number of new coins for which we reveal data
+ * @param sigs array of @a num_newcoins signatures revealed
+ * @return a MHD result code
+ */
+static int
+reply_refresh_reveal_success (struct MHD_Connection *connection,
+                             unsigned int num_newcoins,
+                             const struct TALER_DenominationSignature *sigs)
+{
+  json_t *root;
+  json_t *obj;
+  json_t *list;
+  int ret;
+
+  list = json_array ();
+  for (unsigned int newcoin_index = 0;
+       newcoin_index < num_newcoins;
+       newcoin_index++)
+  {
+    obj = json_object ();
+    json_object_set_new (obj,
+                        "ev_sig",
+                        GNUNET_JSON_from_rsa_signature 
(sigs[newcoin_index].rsa_signature));
+    GNUNET_assert (0 ==
+                   json_array_append_new (list,
+                                          obj));
+  }
+  root = json_object ();
+  json_object_set_new (root,
+                       "ev_sigs",
+                       list);
+  ret = TEH_RESPONSE_reply_json (connection,
+                                 root,
+                                 MHD_HTTP_OK);
+  json_decref (root);
+  return ret;
+}
+
+
+/**
+ * Send a response for a failed "/refresh/reveal", where the
+ * revealed value(s) do not match the original commitment.
+ *
+ * @param connection the connection to send the response to
+ * @param session info about session
+ * @param commit_coins array of @a num_newcoins committed envelopes at offset 
@a gamma
+ * @param denom_pubs array of @a num_newcoins denomination keys for the new 
coins
+ * @param gamma_tp transfer public key at offset @a gamma
+ * @return a MHD result code
+ */
+static int
+reply_refresh_reveal_missmatch (struct MHD_Connection *connection,
+                               const struct TALER_EXCHANGEDB_RefreshSession 
*session,
+                               const struct TALER_EXCHANGEDB_RefreshCommitCoin 
*commit_coins,
+                               const struct TALER_DenominationPublicKey 
*denom_pubs,
+                               const struct TALER_TransferPublicKeyP *gamma_tp)
+{
+  json_t *info_new;
+  json_t *info_commit_k;
+
+  info_new = json_array ();
+  info_commit_k = json_array ();
+  for (unsigned int i=0;i<session->num_newcoins;i++)
+  {
+    const struct TALER_EXCHANGEDB_RefreshCommitCoin *cc;
+    json_t *cc_json;
+
+    GNUNET_assert (0 ==
+                   json_array_append_new (info_new,
+                                          GNUNET_JSON_from_rsa_public_key 
(denom_pubs[i].rsa_public_key)));
+
+    cc = &commit_coins[i];
+    cc_json = json_pack ("{s:o}",
+                         "coin_ev",
+                         GNUNET_JSON_from_data (cc->coin_ev,
+                                                cc->coin_ev_size));
+    GNUNET_assert (0 ==
+                   json_array_append_new (info_commit_k,
+                                          cc_json));
+  }
+  return TEH_RESPONSE_reply_json_pack (connection,
+                                       MHD_HTTP_CONFLICT,
+                                       "{s:s, s:I, s:o, s:o, s:o, s:o, s:o, 
s:o, s:o, s:i}",
+                                       "error", "commitment violation",
+                                      "code", (json_int_t) 
TALER_EC_REFRESH_REVEAL_COMMITMENT_VIOLATION,
+                                       "coin_sig", GNUNET_JSON_from_data_auto 
(&session->melt.coin_sig),
+                                       "coin_pub", GNUNET_JSON_from_data_auto 
(&session->melt.coin.coin_pub),
+                                       "melt_amount_with_fee", 
TALER_JSON_from_amount (&session->melt.amount_with_fee),
+                                       "melt_fee", TALER_JSON_from_amount 
(&session->melt.melt_fee),
+                                       "newcoin_infos", info_new,
+                                       "commit_infos", info_commit_k,
+                                       "gamma_tp", GNUNET_JSON_from_data_auto 
(gamma_tp),
+                                       "gamma", (int) session->noreveal_index);
+}
+
+
+/**
+ * Check if the given @a transfer_privs correspond to an honest
+ * commitment for the given session.
+ * Checks that the transfer private keys match their commitments.
+ * Then derives the shared secret for each #TALER_CNC_KAPPA, and check that 
they match.
+ *
+ * @param connection the MHD connection to handle
+ * @param session database connection to use
+ * @param session_hash hash of session to query
+ * @param off commitment offset to check
+ * @param transfer_priv private transfer key
+ * @param melt information about the melted coin
+ * @param num_newcoins number of newcoins being generated
+ * @param denom_pubs array of @a num_newcoins keys for the new coins
+ * @param hash_context hash context to update by hashing in the data
+ *                     from this offset
+ * @return #GNUNET_OK if the committment was honest,
+ *         #GNUNET_NO if there was a problem and we generated an error message
+ *         #GNUNET_SYSERR if we could not even generate an error message
+ */
+static int
+check_commitment (struct MHD_Connection *connection,
+                  struct TALER_EXCHANGEDB_Session *session,
+                  const struct GNUNET_HashCode *session_hash,
+                  unsigned int off,
+                  const struct TALER_TransferPrivateKeyP *transfer_priv,
+                  const struct TALER_EXCHANGEDB_RefreshMelt *melt,
+                  unsigned int num_newcoins,
+                  const struct TALER_DenominationPublicKey *denom_pubs,
+                  struct GNUNET_HashContext *hash_context)
+{
+  struct TALER_TransferSecretP transfer_secret;
+
+  TALER_link_reveal_transfer_secret (transfer_priv,
+                                     &melt->coin.coin_pub,
+                                     &transfer_secret);
+
+  /* Check that the commitments for all new coins were correct */
+  for (unsigned int j = 0; j < num_newcoins; j++)
+  {
+    struct TALER_FreshCoinP fc;
+    struct TALER_CoinSpendPublicKeyP coin_pub;
+    struct GNUNET_HashCode h_msg;
+    char *buf;
+    size_t buf_len;
+
+    TALER_setup_fresh_coin (&transfer_secret,
+                            j,
+                            &fc);
+    GNUNET_CRYPTO_eddsa_key_get_public (&fc.coin_priv.eddsa_priv,
+                                        &coin_pub.eddsa_pub);
+    GNUNET_CRYPTO_hash (&coin_pub,
+                        sizeof (struct TALER_CoinSpendPublicKeyP),
+                        &h_msg);
+    if (GNUNET_YES !=
+        GNUNET_CRYPTO_rsa_blind (&h_msg,
+                                 &fc.blinding_key.bks,
+                                 denom_pubs[j].rsa_public_key,
+                                 &buf,
+                                 &buf_len))
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "Blind failed (bad denomination key!?)\n");
+      return (MHD_YES ==
+             TEH_RESPONSE_reply_internal_error (connection,
+                                                
TALER_EC_REFRESH_REVEAL_BLINDING_ERROR,
+                                                "Blinding error"))
+        ? GNUNET_NO : GNUNET_SYSERR;
+    }
+    GNUNET_CRYPTO_hash_context_read (hash_context,
+                                     buf,
+                                     buf_len);
+    GNUNET_free (buf);
+  }
+  return GNUNET_OK;
+}
+
+
+/**
+ * State for a /refresh/reveal operation.
+ */
+struct RevealContext
+{
+
+  /**
+   * Hash of the refresh session.
+   */
+  const struct GNUNET_HashCode *session_hash;
+
+  /**
+   * Database session used to execute the transaction.
+   */
+  struct TALER_EXCHANGEDB_Session *session;
+
+  /**
+   * Session state from the database.
+   */
+  struct TALER_EXCHANGEDB_RefreshSession refresh_session;
+
+  /**
+   * Array of denomination public keys used for the refresh.
+   */
+  struct TALER_DenominationPublicKey *denom_pubs;
+
+  /**
+   * Envelopes with the signatures to be returned.
+   */
+  struct TALER_DenominationSignature *ev_sigs;
+
+  /**
+   * Commitment data from the DB giving data about original
+   * commitments, in particular the blinded envelopes (for
+   * index gamma).
+   */
+  struct TALER_EXCHANGEDB_RefreshCommitCoin *commit_coins;
+
+  /**
+   * Transfer public key associated with the gamma value
+   * selected by the exchange.
+   */
+  struct TALER_TransferPublicKeyP gamma_tp;
+
+  /**
+   * Transfer private keys revealed to us.
+   */
+  struct TALER_TransferPrivateKeyP transfer_privs[TALER_CNC_KAPPA - 1];
+
+};
+
+
+/**
+ * Exchange a coin as part of a refresh operation.  Obtains the
+ * envelope from the database and performs the signing operation.
+ *
+ * @param connection the MHD connection to handle
+ * @param session database connection to use
+ * @param session_hash hash of session to query
+ * @param key_state key state to lookup denomination pubs
+ * @param denom_pub denomination key for the coin to create
+ * @param commit_coin the coin that was committed
+ * @param coin_off number of the coin
+ * @param[out] ev_sig set to signature over the coin upon success
+ * @return database transaction status
+ */
+static enum GNUNET_DB_QueryStatus
+refresh_exchange_coin (struct MHD_Connection *connection,
+                       struct TALER_EXCHANGEDB_Session *session,
+                       const struct GNUNET_HashCode *session_hash,
+                       struct TEH_KS_StateHandle *key_state,
+                       const struct TALER_DenominationPublicKey *denom_pub,
+                       const struct TALER_EXCHANGEDB_RefreshCommitCoin 
*commit_coin,
+                       unsigned int coin_off,
+                      struct TALER_DenominationSignature *ev_sig)
+{
+  struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki;
+  enum GNUNET_DB_QueryStatus qs;
+
+  dki = TEH_KS_denomination_key_lookup (key_state,
+                                        denom_pub,
+                                       TEH_KS_DKU_WITHDRAW);
+  if (NULL == dki)
+  {
+    GNUNET_break (0);
+    ev_sig->rsa_signature = NULL;
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  }
+  qs = TEH_plugin->get_refresh_out (TEH_plugin->cls,
+                                   session,
+                                   session_hash,
+                                   coin_off,
+                                   ev_sig);
+  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "Returning cached reply for /refresh/reveal signature\n");
+    return qs;
+  }
+  if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != qs)
+    return qs;
+
+  ev_sig->rsa_signature
+    = GNUNET_CRYPTO_rsa_sign_blinded (dki->denom_priv.rsa_private_key,
+                                      commit_coin->coin_ev,
+                                      commit_coin->coin_ev_size);
+  if (NULL == ev_sig->rsa_signature)
+  {
+    GNUNET_break (0);
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  }
+  qs = TEH_plugin->insert_refresh_out (TEH_plugin->cls,
+                                      session,
+                                      session_hash,
+                                      coin_off,
+                                      ev_sig);
+  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
+  {
+    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+    if (NULL != ev_sig->rsa_signature)
+    {
+      GNUNET_CRYPTO_rsa_signature_free (ev_sig->rsa_signature);
+      ev_sig->rsa_signature = NULL;
+    }
+  }
+  return qs;
+}
+
+
+/**
+ * Cleanup state of the transaction stored in @a rc.
+ *
+ * @param rc context to clean up
+ */
+static void
+cleanup_rc (struct RevealContext *rc)
+{
+  if (NULL != rc->denom_pubs)
+  {
+    for (unsigned int i=0;i<rc->refresh_session.num_newcoins;i++)
+      if (NULL != rc->denom_pubs[i].rsa_public_key)
+       GNUNET_CRYPTO_rsa_public_key_free (rc->denom_pubs[i].rsa_public_key);
+    GNUNET_free (rc->denom_pubs);
+    rc->denom_pubs = NULL;
+  }
+  if (NULL != rc->commit_coins)
+  {
+    for (unsigned int j=0;j<rc->refresh_session.num_newcoins;j++)
+      GNUNET_free_non_null (rc->commit_coins[j].coin_ev);
+    GNUNET_free (rc->commit_coins);
+    rc->commit_coins = NULL;
+  }
+  if (NULL != rc->ev_sigs)
+  {
+    for (unsigned int j=0;j<rc->refresh_session.num_newcoins;j++)
+      if (NULL != rc->ev_sigs[j].rsa_signature)
+       GNUNET_CRYPTO_rsa_signature_free (rc->ev_sigs[j].rsa_signature);
+    GNUNET_free (rc->ev_sigs);
+    rc->ev_sigs = NULL;
+  }
+  if (NULL != rc->refresh_session.melt.coin.denom_sig.rsa_signature)
+  {
+    GNUNET_CRYPTO_rsa_signature_free 
(rc->refresh_session.melt.coin.denom_sig.rsa_signature);
+    rc->refresh_session.melt.coin.denom_sig.rsa_signature = NULL;
+  }
+  if (NULL != rc->refresh_session.melt.coin.denom_pub.rsa_public_key)
+  {
+    GNUNET_CRYPTO_rsa_public_key_free 
(rc->refresh_session.melt.coin.denom_pub.rsa_public_key);
+    rc->refresh_session.melt.coin.denom_pub.rsa_public_key = NULL;
+  }
+}
+
+
+/**
+ * Execute a "/refresh/reveal".  The client is revealing to us the
+ * transfer keys for @a #TALER_CNC_KAPPA-1 sets of coins.  Verify that the
+ * revealed transfer keys would allow linkage to the blinded coins,
+ * and if so, return the signed coins for corresponding to the set of
+ * coins that was not chosen.
+ *
+ * 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 closure of type `struct RevealContext`
+ * @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
+refresh_reveal_transaction (void *cls,
+                           struct MHD_Connection *connection,
+                           struct TALER_EXCHANGEDB_Session *session,
+                           int *mhd_ret)
+{
+  struct RevealContext *rc = cls;
+  unsigned int off;
+  struct GNUNET_HashContext *hash_context;
+  struct GNUNET_HashCode sh_check;
+  enum GNUNET_DB_QueryStatus qs;
+
+  rc->session = session;
+  qs = TEH_plugin->get_refresh_session (TEH_plugin->cls,
+                                       session,
+                                       rc->session_hash,
+                                       &rc->refresh_session);
+  if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
+  {
+    *mhd_ret = TEH_RESPONSE_reply_arg_invalid (connection,
+                                              
TALER_EC_REFRESH_REVEAL_SESSION_UNKNOWN,
+                                              "session_hash");
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  }
+  if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+    return qs;
+  if ( (GNUNET_DB_STATUS_HARD_ERROR == qs) ||
+       (rc->refresh_session.noreveal_index >= TALER_CNC_KAPPA) )
+  {
+    GNUNET_break (0);
+    cleanup_rc (rc);
+    *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+                                                    
TALER_EC_REFRESH_REVEAL_DB_FETCH_SESSION_ERROR);
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  }
+  rc->denom_pubs = GNUNET_new_array (rc->refresh_session.num_newcoins,
+                                    struct TALER_DenominationPublicKey);
+  qs = TEH_plugin->get_refresh_order (TEH_plugin->cls,
+                                     session,
+                                     rc->session_hash,
+                                     rc->refresh_session.num_newcoins,
+                                     rc->denom_pubs);
+  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
+  {
+    cleanup_rc (rc);
+    if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+      return qs;
+    GNUNET_break (0);
+    *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+                                                    
TALER_EC_REFRESH_REVEAL_DB_FETCH_ORDER_ERROR);
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  }
+
+  hash_context = GNUNET_CRYPTO_hash_context_start ();
+  /* first, iterate over transfer public keys for hash_context */
+  off = 0;
+  for (unsigned int i=0;i<TALER_CNC_KAPPA;i++)
+  {
+    if (i == rc->refresh_session.noreveal_index)
+    {
+      off = 1;
+      /* obtain gamma_tp from db */
+      qs = TEH_plugin->get_refresh_transfer_public_key (TEH_plugin->cls,
+                                                       session,
+                                                       rc->session_hash,
+                                                       &rc->gamma_tp);
+      if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
+      {
+        GNUNET_CRYPTO_hash_context_abort (hash_context);
+       cleanup_rc (rc);
+       if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+         return qs;
+        GNUNET_break (0);
+        *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+                                                        
TALER_EC_REFRESH_REVEAL_DB_FETCH_TRANSFER_ERROR);
+       return GNUNET_DB_STATUS_HARD_ERROR;
+      }
+      GNUNET_CRYPTO_hash_context_read (hash_context,
+                                       &rc->gamma_tp,
+                                       sizeof (struct 
TALER_TransferPublicKeyP));
+    }
+    else
+    {
+      /* compute tp from private key */
+      struct TALER_TransferPublicKeyP tp;
+
+      GNUNET_CRYPTO_ecdhe_key_get_public (&rc->transfer_privs[i - 
off].ecdhe_priv,
+                                          &tp.ecdhe_pub);
+      GNUNET_CRYPTO_hash_context_read (hash_context,
+                                       &tp,
+                                       sizeof (struct 
TALER_TransferPublicKeyP));
+    }
+  }
+
+  /* next, add all of the hashes from the denomination keys to the
+     hash_context */
+  for (unsigned int i=0;i<rc->refresh_session.num_newcoins;i++)
+  {
+    char *buf;
+    size_t buf_size;
+
+    buf_size = GNUNET_CRYPTO_rsa_public_key_encode 
(rc->denom_pubs[i].rsa_public_key,
+                                                   &buf);
+    GNUNET_CRYPTO_hash_context_read (hash_context,
+                                    buf,
+                                    buf_size);
+    GNUNET_free (buf);
+  }
+
+  /* next, add public key of coin and amount being refreshed */
+  {
+    struct TALER_AmountNBO melt_amountn;
+
+    GNUNET_CRYPTO_hash_context_read (hash_context,
+                                     &rc->refresh_session.melt.coin.coin_pub,
+                                     sizeof (struct 
TALER_CoinSpendPublicKeyP));
+    TALER_amount_hton (&melt_amountn,
+                       &rc->refresh_session.melt.amount_with_fee);
+    GNUNET_CRYPTO_hash_context_read (hash_context,
+                                     &melt_amountn,
+                                     sizeof (struct TALER_AmountNBO));
+  }
+
+  rc->commit_coins = GNUNET_new_array (rc->refresh_session.num_newcoins,
+                                      struct 
TALER_EXCHANGEDB_RefreshCommitCoin);
+  off = 0;
+  for (unsigned int i=0;i<TALER_CNC_KAPPA;i++)
+  {
+    int res;
+    
+    if (i == rc->refresh_session.noreveal_index)
+    {
+      off = 1;
+      /* obtain commit_coins for the selected gamma value from DB */
+      qs = TEH_plugin->get_refresh_commit_coins (TEH_plugin->cls,
+                                                session,
+                                                rc->session_hash,
+                                                
rc->refresh_session.num_newcoins,
+                                                rc->commit_coins);
+      if (0 >= qs)
+      {        
+       cleanup_rc (rc);
+        GNUNET_CRYPTO_hash_context_abort (hash_context);
+       if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+         return qs;
+        GNUNET_break (0);
+        *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+                                                        
TALER_EC_REFRESH_REVEAL_DB_FETCH_COMMIT_ERROR);
+       return GNUNET_DB_STATUS_HARD_ERROR;
+      }
+      /* add envelopes to hash_context */
+      for (unsigned int j=0;j<rc->refresh_session.num_newcoins;j++)
+      {
+        GNUNET_CRYPTO_hash_context_read (hash_context,
+                                         rc->commit_coins[j].coin_ev,
+                                         rc->commit_coins[j].coin_ev_size);
+      }
+      continue;
+    }
+    if (GNUNET_OK !=
+        (res = check_commitment (connection,
+                                 session,
+                                 rc->session_hash,
+                                 i,
+                                 &rc->transfer_privs[i - off],
+                                 &rc->refresh_session.melt,
+                                 rc->refresh_session.num_newcoins,
+                                 rc->denom_pubs,
+                                 hash_context)))
+    {
+      GNUNET_break_op (0);
+      cleanup_rc (rc);
+      GNUNET_CRYPTO_hash_context_abort (hash_context);
+      *mhd_ret = (GNUNET_NO == res) ? MHD_YES : MHD_NO;
+      return GNUNET_DB_STATUS_HARD_ERROR;
+    }
+  }
+
+  /* Check session hash matches */
+  GNUNET_CRYPTO_hash_context_finish (hash_context,
+                                     &sh_check);
+  if (0 != memcmp (&sh_check,
+                   rc->session_hash,
+                   sizeof (struct GNUNET_HashCode)))
+  {
+    GNUNET_break_op (0);
+    *mhd_ret = reply_refresh_reveal_missmatch (connection,
+                                              &rc->refresh_session,
+                                              rc->commit_coins,
+                                              rc->denom_pubs,
+                                              &rc->gamma_tp);
+    cleanup_rc (rc);
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  }
+  
+  /* Client request OK, sign coins */
+  rc->ev_sigs = GNUNET_new_array (rc->refresh_session.num_newcoins,
+                                 struct TALER_DenominationSignature);
+  {
+    struct TEH_KS_StateHandle *key_state;
+
+    key_state = TEH_KS_acquire ();
+    for (unsigned int j=0;j<rc->refresh_session.num_newcoins;j++)
+    {
+      qs = refresh_exchange_coin (connection,
+                                 session,
+                                 rc->session_hash,
+                                 key_state,
+                                 &rc->denom_pubs[j],
+                                 &rc->commit_coins[j],
+                                 j,
+                                 &rc->ev_sigs[j]);
+      if ( (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) ||
+          (NULL == rc->ev_sigs[j].rsa_signature) )
+      {
+       *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+                                                        
TALER_EC_REFRESH_REVEAL_SIGNING_ERROR);
+       qs = GNUNET_DB_STATUS_HARD_ERROR;
+       break;
+      }
+    }
+    TEH_KS_release (key_state);
+  }
+  if (0 >= qs)
+  {
+    cleanup_rc (rc);
+    return qs;
+  }
+  return qs;
+}
+
+
+/**
+ * Handle a "/refresh/reveal" request.   Parses the given JSON
+ * transfer private keys and if successful, passes everything to
+ * #TEH_DB_execute_refresh_reveal() which will verify that the
+ * revealed information is valid then returns the signed refreshed
+ * coins.
+ *
+ * @param connection the MHD connection to handle
+ * @param session_hash hash identifying the melting session
+ * @param tp_json private transfer keys in JSON format
+ * @return MHD result code
+  */
+static int
+handle_refresh_reveal_json (struct MHD_Connection *connection,
+                            const struct GNUNET_HashCode *session_hash,
+                            const json_t *tp_json)
+{
+  struct RevealContext rc;
+  int mhd_ret;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "reveal request for session %s\n",
+              GNUNET_h2s (session_hash));
+  memset (&rc,
+         0,
+         sizeof (rc));
+  rc.session_hash = session_hash;
+  for (unsigned int i = 0; i < TALER_CNC_KAPPA - 1; i++)
+  {
+    struct GNUNET_JSON_Specification tp_spec[] = {
+      GNUNET_JSON_spec_fixed_auto (NULL, &rc.transfer_privs[i]),
+      GNUNET_JSON_spec_end ()
+    };
+    int res;
+
+    res = TEH_PARSE_json_array (connection,
+                                tp_json,
+                                tp_spec,
+                                i,
+                               -1);
+    GNUNET_break_op (GNUNET_OK == res);
+    if (GNUNET_OK != res)
+      return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
+  }
+  if (GNUNET_OK !=
+      TEH_DB_run_transaction (connection,
+                             &mhd_ret,
+                             &refresh_reveal_transaction,
+                             &rc))
+  {
+    cleanup_rc (&rc);
+    return mhd_ret;
+  }
+  mhd_ret = reply_refresh_reveal_success (connection,
+                                         rc.refresh_session.num_newcoins,
+                                         rc.ev_sigs);
+  cleanup_rc (&rc);
+  return mhd_ret;
+}
+
+
+/**
+ * Handle a "/refresh/reveal" request. This time, the client reveals
+ * the private transfer keys except for the cut-and-choose value
+ * returned from "/refresh/melt".  This function parses the revealed
+ * keys and secrets and ultimately passes everything to
+ * #TEH_DB_execute_refresh_reveal() which will verify that the
+ * revealed information is valid then returns the signed refreshed
+ * coins.
+ *
+ * @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_REFRESH_handler_refresh_reveal (struct TEH_RequestHandler *rh,
+                                    struct MHD_Connection *connection,
+                                    void **connection_cls,
+                                    const char *upload_data,
+                                    size_t *upload_data_size)
+{
+  struct GNUNET_HashCode session_hash;
+  int res;
+  json_t *root;
+  json_t *transfer_privs;
+  struct GNUNET_JSON_Specification spec[] = {
+    GNUNET_JSON_spec_fixed_auto ("session_hash", &session_hash),
+    GNUNET_JSON_spec_json ("transfer_privs", &transfer_privs),
+    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)
+  {
+    GNUNET_break_op (0);
+    return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
+  }
+  /* Determine dimensionality of the request (kappa and #old coins) */
+  /* Note we do +1 as 1 row (cut-and-choose!) is missing! */
+  if (TALER_CNC_KAPPA != json_array_size (transfer_privs) + 1)
+  {
+    GNUNET_JSON_parse_free (spec);
+    GNUNET_break_op (0);
+    return TEH_RESPONSE_reply_arg_invalid (connection,
+                                          
TALER_EC_REFRESH_REVEAL_CNC_TRANSFER_ARRAY_SIZE_INVALID,
+                                           "transfer_privs");
+  }
+  res = handle_refresh_reveal_json (connection,
+                                    &session_hash,
+                                    transfer_privs);
+  GNUNET_JSON_parse_free (spec);
+  return res;
+}
+
+
+/* end of taler-exchange-httpd_refresh_reveal.c */
diff --git a/src/exchange/taler-exchange-httpd_refund.h 
b/src/exchange/taler-exchange-httpd_refresh_reveal.h
similarity index 55%
copy from src/exchange/taler-exchange-httpd_refund.h
copy to src/exchange/taler-exchange-httpd_refresh_reveal.h
index 1fa52b1..cc5ac3f 100644
--- a/src/exchange/taler-exchange-httpd_refund.h
+++ b/src/exchange/taler-exchange-httpd_refresh_reveal.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,14 +14,14 @@
   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_refresh_reveal.h
+ * @brief Handle /refresh/reveal 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_REFRESH_REVEAL_H
+#define TALER_EXCHANGE_HTTPD_REFRESH_REVEAL_H
 
 #include <gnunet/gnunet_util_lib.h>
 #include <microhttpd.h>
@@ -29,11 +29,13 @@
 
 
 /**
- * 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 "/refresh/reveal" request.  This time, the client reveals
+ * the private transfer keys except for the cut-and-choose value
+ * returned from "/refresh/commit".  This function parses the revealed
+ * keys and secrets and ultimately passes everything to
+ * #TEH_DB_execute_refresh_reveal() which will verify that the
+ * revealed information is valid then returns the signed refreshed
+ * coins.
  *
  * @param rh context of the handler
  * @param connection the MHD connection to handle
@@ -43,10 +45,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_REFRESH_handler_refresh_reveal (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_refund.c 
b/src/exchange/taler-exchange-httpd_refund.c
index a5787ee..9846c73 100644
--- a/src/exchange/taler-exchange-httpd_refund.c
+++ b/src/exchange/taler-exchange-httpd_refund.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,350 @@
 
 
 /**
+ * Generate successful refund confirmation message.
+ *
+ * @param connection connection to the client
+ * @param refund details about the successful refund
+ * @return MHD result code
+ */
+static int
+reply_refund_success (struct MHD_Connection *connection,
+                     const struct TALER_EXCHANGEDB_Refund *refund)
+{
+  struct TALER_RefundConfirmationPS rc;
+  struct TALER_ExchangePublicKeyP pub;
+  struct TALER_ExchangeSignatureP sig;
+
+  rc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_REFUND);
+  rc.purpose.size = htonl (sizeof (struct TALER_RefundConfirmationPS));
+  rc.h_contract_terms = refund->h_contract_terms;
+  rc.coin_pub = refund->coin.coin_pub;
+  rc.merchant = refund->merchant_pub;
+  rc.rtransaction_id = GNUNET_htonll (refund->rtransaction_id);
+  TALER_amount_hton (&rc.refund_amount,
+                     &refund->refund_amount);
+  TALER_amount_hton (&rc.refund_fee,
+                     &refund->refund_fee);
+  TEH_KS_sign (&rc.purpose,
+               &pub,
+               &sig);
+  return TEH_RESPONSE_reply_json_pack (connection,
+                                       MHD_HTTP_OK,
+                                       "{s:s, s:o, s:o}",
+                                       "status", "REFUND_OK",
+                                       "sig", GNUNET_JSON_from_data_auto 
(&sig),
+                                       "pub", GNUNET_JSON_from_data_auto 
(&pub));
+}
+
+
+/**
+ * Generate generic refund failure message. All the details
+ * are in the @a response_code.  The body can be empty.
+ *
+ * @param connection connection to the client
+ * @param response_code response code to generate
+ * @param ec taler error code to include
+ * @return MHD result code
+ */
+static int
+reply_refund_failure (struct MHD_Connection *connection,
+                     unsigned int response_code,
+                     enum TALER_ErrorCode ec)
+{
+  return TEH_RESPONSE_reply_json_pack (connection,
+                                       response_code,
+                                       "{s:s, s:I}",
+                                       "status", "refund failure",
+                                      "code", (json_int_t) ec);
+}
+
+
+/**
+ * Generate refund conflict failure message. Returns the
+ * transaction list @a tl with the details about the conflict.
+ *
+ * @param connection connection to the client
+ * @param tl transaction list showing the conflict
+ * @return MHD result code
+ */
+static int
+reply_refund_conflict (struct MHD_Connection *connection,
+                      const struct TALER_EXCHANGEDB_TransactionList *tl)
+{
+  return TEH_RESPONSE_reply_json_pack (connection,
+                                       MHD_HTTP_CONFLICT,
+                                       "{s:s, s:I, s:o}",
+                                       "status", "conflicting refund",
+                                      "code", (json_int_t) 
TALER_EC_REFUND_CONFLICT,
+                                       "history", 
TEH_RESPONSE_compile_transaction_history (tl));
+}
+
+
+/**
+ * Execute a "/refund" transaction.  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.
+ *
+ * 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 closure with a `const struct TALER_EXCHANGEDB_Refund *`
+ * @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
+refund_transaction (void *cls,
+                   struct MHD_Connection *connection,
+                   struct TALER_EXCHANGEDB_Session *session,
+                   int *mhd_ret)
+{
+  const struct TALER_EXCHANGEDB_Refund *refund = cls;
+  struct TALER_EXCHANGEDB_TransactionList *tl;
+  const struct TALER_EXCHANGEDB_Deposit *dep;
+  const struct TALER_EXCHANGEDB_Refund *ref;
+  struct TEH_KS_StateHandle *mks;
+  struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki;
+  struct TALER_Amount expect_fee;
+  enum GNUNET_DB_QueryStatus qs;
+  int deposit_found;
+  int refund_found;
+  int fee_cmp;
+
+  dep = NULL;
+  ref = NULL;
+  tl = NULL;
+  qs = TEH_plugin->get_coin_transactions (TEH_plugin->cls,
+                                          session,
+                                          &refund->coin.coin_pub,
+                                         &tl);
+  if (0 > qs)
+  {
+    if (GNUNET_DB_STATUS_HARD_ERROR == qs)
+      *mhd_ret = reply_refund_failure (connection,
+                                      MHD_HTTP_NOT_FOUND,
+                                      TALER_EC_REFUND_COIN_NOT_FOUND);
+    return qs;
+  }
+  deposit_found = GNUNET_NO;
+  refund_found = GNUNET_NO;
+  for (struct TALER_EXCHANGEDB_TransactionList *tlp = tl;
+       NULL != tlp;
+       tlp = tlp->next)
+  {
+    switch (tlp->type)
+    {
+    case TALER_EXCHANGEDB_TT_DEPOSIT:
+      if (GNUNET_NO == deposit_found)
+      {
+        if ( (0 == memcmp (&tlp->details.deposit->merchant_pub,
+                           &refund->merchant_pub,
+                           sizeof (struct TALER_MerchantPublicKeyP))) &&
+             (0 == memcmp (&tlp->details.deposit->h_contract_terms,
+                           &refund->h_contract_terms,
+                           sizeof (struct GNUNET_HashCode))) )
+        {
+          dep = tlp->details.deposit;
+          deposit_found = GNUNET_YES;
+          break;
+        }
+      }
+      break;
+    case TALER_EXCHANGEDB_TT_REFRESH_MELT:
+      /* Melts cannot be refunded, ignore here */
+      break;
+    case TALER_EXCHANGEDB_TT_REFUND:
+      if (GNUNET_NO == refund_found)
+      {
+        /* First, check if existing refund request is identical */
+        if ( (0 == memcmp (&tlp->details.refund->merchant_pub,
+                           &refund->merchant_pub,
+                           sizeof (struct TALER_MerchantPublicKeyP))) &&
+             (0 == memcmp (&tlp->details.refund->h_contract_terms,
+                           &refund->h_contract_terms,
+                           sizeof (struct GNUNET_HashCode))) &&
+             (tlp->details.refund->rtransaction_id == refund->rtransaction_id) 
)
+        {
+          ref = tlp->details.refund;
+          refund_found = GNUNET_YES;
+          break;
+        }
+        /* Second, check if existing refund request conflicts */
+        if ( (0 == memcmp (&tlp->details.refund->merchant_pub,
+                           &refund->merchant_pub,
+                           sizeof (struct TALER_MerchantPublicKeyP))) &&
+             (0 == memcmp (&tlp->details.refund->h_contract_terms,
+                           &refund->h_contract_terms,
+                           sizeof (struct GNUNET_HashCode))) &&
+             (tlp->details.refund->rtransaction_id != refund->rtransaction_id) 
)
+        {
+          GNUNET_break_op (0); /* conflicting refund found */
+          refund_found = GNUNET_SYSERR;
+          /* NOTE: Alternatively we could total up all existing
+             refunds and check if the sum still permits the
+             refund requested (thus allowing multiple, partial
+             refunds). Fow now, we keep it simple. */
+          break;
+        }
+      }
+      break;
+    case TALER_EXCHANGEDB_TT_PAYBACK:
+      /* Paybacks cannot be refunded, ignore here */
+      break;
+    }
+  }
+  /* handle if deposit was NOT found */
+  if (GNUNET_NO == deposit_found)
+  {
+    TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
+                                            tl);
+    *mhd_ret = TEH_RESPONSE_reply_transaction_unknown (connection,
+                                                      
TALER_EC_REFUND_DEPOSIT_NOT_FOUND);
+    return GNUNET_DB_STATUS_HARD_ERROR;    
+  }
+  /* handle if conflicting refund found */
+  if (GNUNET_SYSERR == refund_found)
+  {
+    *mhd_ret = reply_refund_conflict (connection,
+                                     tl);
+    TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
+                                            tl);
+    return GNUNET_DB_STATUS_HARD_ERROR; 
+  }
+  /* handle if identical refund found */
+  if (GNUNET_YES == refund_found)
+  {
+    /* /refund already done, simply re-transmit confirmation */
+    *mhd_ret = reply_refund_success (connection,
+                                    ref);
+    TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
+                                            tl);
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  }
+
+  /* check currency is compatible */
+  if ( (GNUNET_YES !=
+        TALER_amount_cmp_currency (&refund->refund_amount,
+                                   &dep->amount_with_fee)) ||
+       (GNUNET_YES !=
+        TALER_amount_cmp_currency (&refund->refund_fee,
+                                   &dep->deposit_fee)) )
+  {
+    GNUNET_break_op (0); /* currency missmatch */
+    *mhd_ret = reply_refund_failure (connection,
+                                    MHD_HTTP_PRECONDITION_FAILED,
+                                    TALER_EC_REFUND_CURRENCY_MISSMATCH);
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  }
+
+  /* check if we already send the money for the /deposit */
+  // FIXME: DB API...
+  qs = TEH_plugin->test_deposit_done (TEH_plugin->cls,
+                                     session,
+                                     dep);
+  if (GNUNET_DB_STATUS_HARD_ERROR == qs)
+  {
+    /* Internal error, we first had the deposit in the history,
+       but now it is gone? */
+    GNUNET_break (0);
+    TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
+                                            tl);
+    *mhd_ret = TEH_RESPONSE_reply_internal_error (connection,
+                                                 
TALER_EC_REFUND_DB_INCONSISTENT,
+                                                 "database inconsistent");
+    return qs;
+  }
+  if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+    return qs; /* go and retry */
+  
+  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
+  {
+    /* money was already transferred to merchant, can no longer refund */
+    TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
+                                            tl);
+    *mhd_ret = reply_refund_failure (connection,
+                                    MHD_HTTP_GONE,
+                                    TALER_EC_REFUND_MERCHANT_ALREADY_PAID);
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  }
+
+  /* check refund amount is sufficiently low */
+  if (1 == TALER_amount_cmp (&refund->refund_amount,
+                             &dep->amount_with_fee) )
+  {
+    GNUNET_break_op (0); /* cannot refund more than original value */
+    TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
+                                            tl);
+    *mhd_ret = reply_refund_failure (connection,
+                                    MHD_HTTP_PRECONDITION_FAILED,
+                                    TALER_EC_REFUND_INSUFFICIENT_FUNDS);
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  }
+
+  // FIXME: do this outside of transaction function?
+  /* Check refund fee matches fee of denomination key! */
+  mks = TEH_KS_acquire ();
+  dki = TEH_KS_denomination_key_lookup (mks,
+                                        &dep->coin.denom_pub,
+                                       TEH_KS_DKU_DEPOSIT);
+  if (NULL == dki)
+  {
+    /* DKI not found, but we do have a coin with this DK in our database;
+       not good... */
+    GNUNET_break (0);
+    TEH_KS_release (mks);
+    TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
+                                            tl);
+    *mhd_ret = TEH_RESPONSE_reply_internal_error (connection,
+                                                 
TALER_EC_REFUND_DENOMINATION_KEY_NOT_FOUND,
+                                                 "denomination key not found");
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  }
+  TALER_amount_ntoh (&expect_fee,
+                     &dki->issue.properties.fee_refund);
+  fee_cmp = TALER_amount_cmp (&refund->refund_fee,
+                              &expect_fee);
+  TEH_KS_release (mks);
+
+  if (-1 == fee_cmp)
+  {
+    TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
+                                            tl);
+    *mhd_ret = TEH_RESPONSE_reply_arg_invalid (connection,
+                                              TALER_EC_REFUND_FEE_TOO_LOW,
+                                              "refund_fee");
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  }
+  if (1 == fee_cmp)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                "Refund fee proposed by merchant is higher than necessary.\n");
+  }
+  TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
+                                          tl);
+
+  /* Finally, store new refund data */
+  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");
+    *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+                                                    
TALER_EC_REFUND_STORE_DB_ERROR);
+    return qs;
+  }
+  /* Success or soft failure */
+  return qs;
+}
+
+
+/**
  * We have parsed the JSON information about the refund, do some basic
  * sanity checks (especially that the signature on the coin is valid)
  * and then execute the refund.  Note that we need the DB to check
@@ -51,6 +395,7 @@ verify_and_execute_refund (struct MHD_Connection *connection,
                           const struct TALER_EXCHANGEDB_Refund *refund)
 {
   struct TALER_RefundRequestPS rr;
+  int mhd_ret;
 
   rr.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_REFUND);
   rr.purpose.size = htonl (sizeof (struct TALER_RefundRequestPS));
@@ -90,8 +435,14 @@ verify_and_execute_refund (struct MHD_Connection 
*connection,
                                                 
TALER_EC_REFUND_MERCHANT_SIGNATURE_INVALID,
                                                  "merchant_sig");
   }
-  return TEH_DB_execute_refund (connection,
-                               refund);
+  if (GNUNET_OK !=
+      TEH_DB_run_transaction (connection,
+                             &mhd_ret,
+                             &refund_transaction,
+                             (void *) refund))
+    return mhd_ret;
+  return reply_refund_success (connection,
+                              refund);
 }
 
 
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..c31c248 100644
--- a/src/exchange/taler-exchange-httpd_responses.c
+++ b/src/exchange/taler-exchange-httpd_responses.c
@@ -15,7 +15,7 @@
 */
 /**
  * @file taler-exchange-httpd_responses.c
- * @brief API for generating the various replies of the exchange; these
+ * @brief API for generating genric replies of the exchange; these
  *        functions are called TEH_RESPONSE_reply_ and they generate
  *        and queue MHD response objects for a given connection.
  * @author Florian Dold
@@ -462,67 +462,13 @@ 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
  * @return json representation of the @a rh
  */
-static json_t *
-compile_transaction_history (const struct TALER_EXCHANGEDB_TransactionList *tl)
+json_t *
+TEH_RESPONSE_compile_transaction_history (const struct 
TALER_EXCHANGEDB_TransactionList *tl)
 {
   json_t *history;
 
@@ -717,7 +663,7 @@ TEH_RESPONSE_reply_coin_insufficient_funds (struct 
MHD_Connection *connection,
 {
   json_t *history;
 
-  history = compile_transaction_history (tl);
+  history = TEH_RESPONSE_compile_transaction_history (tl);
   if (NULL == history)
     return TEH_RESPONSE_reply_internal_error (connection,
                                               
TALER_EC_COIN_HISTORY_DB_ERROR_INSUFFICIENT_FUNDS,
@@ -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;
@@ -933,416 +879,6 @@ compile_reserve_history (const struct 
TALER_EXCHANGEDB_ReserveHistory *rh,
 
 
 /**
- * Generate refund conflict failure message. Returns the
- * transaction list @a tl with the details about the conflict.
- *
- * @param connection connection to the client
- * @param tl transaction list showing the conflict
- * @return MHD result code
- */
-int
-TEH_RESPONSE_reply_refund_conflict (struct MHD_Connection *connection,
-                                    const struct 
TALER_EXCHANGEDB_TransactionList *tl)
-{
-  return TEH_RESPONSE_reply_json_pack (connection,
-                                       MHD_HTTP_CONFLICT,
-                                       "{s:s, s:I, s:o}",
-                                       "status", "conflicting refund",
-                                      "code", (json_int_t) 
TALER_EC_REFUND_CONFLICT,
-                                       "history", compile_transaction_history 
(tl));
-}
-
-
-/**
- * Generate generic refund failure message. All the details
- * are in the @a response_code.  The body can be empty.
- *
- * @param connection connection to the client
- * @param response_code response code to generate
- * @param ec taler error code to include
- * @return MHD result code
- */
-int
-TEH_RESPONSE_reply_refund_failure (struct MHD_Connection *connection,
-                                   unsigned int response_code,
-                                  enum TALER_ErrorCode ec)
-{
-  return TEH_RESPONSE_reply_json_pack (connection,
-                                       response_code,
-                                       "{s:s, s:I}",
-                                       "status", "refund failure",
-                                      "code", (json_int_t) ec);
-}
-
-
-/**
- * Generate successful refund confirmation message.
- *
- * @param connection connection to the client
- * @param refund details about the successful refund
- * @return MHD result code
- */
-int
-TEH_RESPONSE_reply_refund_success (struct MHD_Connection *connection,
-                                   const struct TALER_EXCHANGEDB_Refund 
*refund)
-{
-  struct TALER_RefundConfirmationPS rc;
-  struct TALER_ExchangePublicKeyP pub;
-  struct TALER_ExchangeSignatureP sig;
-
-  rc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_REFUND);
-  rc.purpose.size = htonl (sizeof (struct TALER_RefundConfirmationPS));
-  rc.h_contract_terms = refund->h_contract_terms;
-  rc.coin_pub = refund->coin.coin_pub;
-  rc.merchant = refund->merchant_pub;
-  rc.rtransaction_id = GNUNET_htonll (refund->rtransaction_id);
-  TALER_amount_hton (&rc.refund_amount,
-                     &refund->refund_amount);
-  TALER_amount_hton (&rc.refund_fee,
-                     &refund->refund_fee);
-  TEH_KS_sign (&rc.purpose,
-               &pub,
-               &sig);
-  return TEH_RESPONSE_reply_json_pack (connection,
-                                       MHD_HTTP_OK,
-                                       "{s:s, s:o, s:o}",
-                                       "status", "REFUND_OK",
-                                       "sig", GNUNET_JSON_from_data_auto 
(&sig),
-                                       "pub", GNUNET_JSON_from_data_auto 
(&pub));
-}
-
-
-/**
- * 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
- * contribution of the coin for the melt.  Thus, the exchange
- * refuses the melt operation.
- *
- * @param connection the connection to send the response to
- * @param coin_pub public key of the coin
- * @param coin_value original value of the coin
- * @param tl transaction history for the coin
- * @param requested how much this coin was supposed to contribute, including 
fee
- * @param residual remaining value of the coin (after subtracting @a tl)
- * @return a MHD result code
- */
-int
-TEH_RESPONSE_reply_refresh_melt_insufficient_funds (struct MHD_Connection 
*connection,
-                                                    const struct 
TALER_CoinSpendPublicKeyP *coin_pub,
-                                                    struct TALER_Amount 
coin_value,
-                                                    struct 
TALER_EXCHANGEDB_TransactionList *tl,
-                                                    struct TALER_Amount 
requested,
-                                                    struct TALER_Amount 
residual)
-{
-  json_t *history;
-
-  history = compile_transaction_history (tl);
-  if (NULL == history)
-    return TEH_RESPONSE_reply_internal_db_error (connection,
-                                                
TALER_EC_REFRESH_MELT_HISTORY_DB_ERROR_INSUFFICIENT_FUNDS);
-  return TEH_RESPONSE_reply_json_pack (connection,
-                                       MHD_HTTP_FORBIDDEN,
-                                       "{s:s, s:I, s:o, s:o, s:o, s:o, s:o}",
-                                       "error",
-                                      "insufficient funds",
-                                      "code",
-                                      (json_int_t) 
TALER_EC_REFRESH_MELT_INSUFFICIENT_FUNDS,
-                                       "coin_pub",
-                                       GNUNET_JSON_from_data_auto (coin_pub),
-                                       "original_value",
-                                       TALER_JSON_from_amount (&coin_value),
-                                       "residual_value",
-                                       TALER_JSON_from_amount (&residual),
-                                       "requested_value",
-                                       TALER_JSON_from_amount (&requested),
-                                       "history",
-                                       history);
-}
-
-
-/**
- * Send a response to a "/refresh/melt" request.
- *
- * @param connection the connection to send the response to
- * @param session_hash hash of the refresh session
- * @param noreveal_index which index will the client not have to reveal
- * @return a MHD status code
- */
-int
-TEH_RESPONSE_reply_refresh_melt_success (struct MHD_Connection *connection,
-                                         const struct GNUNET_HashCode 
*session_hash,
-                                         uint16_t noreveal_index)
-{
-  struct TALER_RefreshMeltConfirmationPS body;
-  struct TALER_ExchangePublicKeyP pub;
-  struct TALER_ExchangeSignatureP sig;
-  json_t *sig_json;
-
-  body.purpose.size = htonl (sizeof (struct TALER_RefreshMeltConfirmationPS));
-  body.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_MELT);
-  body.session_hash = *session_hash;
-  body.noreveal_index = htons (noreveal_index);
-  body.reserved = htons (0);
-  TEH_KS_sign (&body.purpose,
-               &pub,
-               &sig);
-  sig_json = GNUNET_JSON_from_data_auto (&sig);
-  GNUNET_assert (NULL != sig_json);
-  return TEH_RESPONSE_reply_json_pack (connection,
-                                       MHD_HTTP_OK,
-                                       "{s:i, s:o, s:o}",
-                                       "noreveal_index", (int) noreveal_index,
-                                       "exchange_sig", sig_json,
-                                       "exchange_pub", 
GNUNET_JSON_from_data_auto (&pub));
-}
-
-
-/**
- * Send a response for "/refresh/reveal".
- *
- * @param connection the connection to send the response to
- * @param num_newcoins number of new coins for which we reveal data
- * @param sigs array of @a num_newcoins signatures revealed
- * @return a MHD result code
- */
-int
-TEH_RESPONSE_reply_refresh_reveal_success (struct MHD_Connection *connection,
-                                           unsigned int num_newcoins,
-                                           const struct 
TALER_DenominationSignature *sigs)
-{
-  int newcoin_index;
-  json_t *root;
-  json_t *obj;
-  json_t *list;
-  int ret;
-
-  list = json_array ();
-  for (newcoin_index = 0; newcoin_index < num_newcoins; newcoin_index++)
-  {
-    obj = json_object ();
-    json_object_set_new (obj,
-                        "ev_sig",
-                        GNUNET_JSON_from_rsa_signature 
(sigs[newcoin_index].rsa_signature));
-    GNUNET_assert (0 ==
-                   json_array_append_new (list,
-                                          obj));
-  }
-  root = json_object ();
-  json_object_set_new (root,
-                       "ev_sigs",
-                       list);
-  ret = TEH_RESPONSE_reply_json (connection,
-                                 root,
-                                 MHD_HTTP_OK);
-  json_decref (root);
-  return ret;
-}
-
-
-/**
- * Send a response for a failed "/refresh/reveal", where the
- * revealed value(s) do not match the original commitment.
- *
- * @param connection the connection to send the response to
- * @param session info about session
- * @param commit_coins array of @a num_newcoins committed envelopes at offset 
@a gamma
- * @param denom_pubs array of @a num_newcoins denomination keys for the new 
coins
- * @param gamma_tp transfer public key at offset @a gamma
- * @return a MHD result code
- */
-int
-TEH_RESPONSE_reply_refresh_reveal_missmatch (struct MHD_Connection *connection,
-                                             const struct 
TALER_EXCHANGEDB_RefreshSession *session,
-                                             const struct 
TALER_EXCHANGEDB_RefreshCommitCoin *commit_coins,
-                                             const struct 
TALER_DenominationPublicKey *denom_pubs,
-                                             const struct 
TALER_TransferPublicKeyP *gamma_tp)
-{
-  json_t *info_new;
-  json_t *info_commit_k;
-  unsigned int i;
-
-  info_new = json_array ();
-  info_commit_k = json_array ();
-  for (i=0;i<session->num_newcoins;i++)
-  {
-    const struct TALER_EXCHANGEDB_RefreshCommitCoin *cc;
-    json_t *cc_json;
-
-    GNUNET_assert (0 ==
-                   json_array_append_new (info_new,
-                                          GNUNET_JSON_from_rsa_public_key 
(denom_pubs[i].rsa_public_key)));
-
-    cc = &commit_coins[i];
-    cc_json = json_pack ("{s:o}",
-                         "coin_ev",
-                         GNUNET_JSON_from_data (cc->coin_ev,
-                                                cc->coin_ev_size));
-    GNUNET_assert (0 ==
-                   json_array_append_new (info_commit_k,
-                                          cc_json));
-  }
-  return TEH_RESPONSE_reply_json_pack (connection,
-                                       MHD_HTTP_CONFLICT,
-                                       "{s:s, s:I, s:o, s:o, s:o, s:o, s:o, 
s:o, s:o, s:i}",
-                                       "error", "commitment violation",
-                                      "code", (json_int_t) 
TALER_EC_REFRESH_REVEAL_COMMITMENT_VIOLATION,
-                                       "coin_sig", GNUNET_JSON_from_data_auto 
(&session->melt.coin_sig),
-                                       "coin_pub", GNUNET_JSON_from_data_auto 
(&session->melt.coin.coin_pub),
-                                       "melt_amount_with_fee", 
TALER_JSON_from_amount (&session->melt.amount_with_fee),
-                                       "melt_fee", TALER_JSON_from_amount 
(&session->melt.melt_fee),
-                                       "newcoin_infos", info_new,
-                                       "commit_infos", info_commit_k,
-                                       "gamma_tp", GNUNET_JSON_from_data_auto 
(gamma_tp),
-                                       "gamma", (int) session->noreveal_index);
-}
-
-
-/**
- * Send a response for "/refresh/link".
- *
- * @param connection the connection to send the response to
- * @param num_sessions number of sessions the coin was used in
- * @param sessions array of @a num_session entries with
- *                  information for each session
- * @return a MHD result code
- */
-int
-TEH_RESPONSE_reply_refresh_link_success (struct MHD_Connection *connection,
-                                         unsigned int num_sessions,
-                                         const struct 
TEH_RESPONSE_LinkSessionInfo *sessions)
-{
-  json_t *root;
-  json_t *mlist;
-  int res;
-  unsigned int i;
-
-  mlist = json_array ();
-  for (i=0;i<num_sessions;i++)
-  {
-    const struct TALER_EXCHANGEDB_LinkDataList *pos;
-    json_t *list = json_array ();
-
-    for (pos = sessions[i].ldl; NULL != pos; pos = pos->next)
-    {
-      json_t *obj;
-
-      obj = json_object ();
-      json_object_set_new (obj,
-                           "denom_pub",
-                           GNUNET_JSON_from_rsa_public_key 
(pos->denom_pub.rsa_public_key));
-      json_object_set_new (obj,
-                           "ev_sig",
-                           GNUNET_JSON_from_rsa_signature 
(pos->ev_sig.rsa_signature));
-      GNUNET_assert (0 ==
-                     json_array_append_new (list,
-                                            obj));
-    }
-    root = json_object ();
-    json_object_set_new (root,
-                         "new_coins",
-                         list);
-    json_object_set_new (root,
-                         "transfer_pub",
-                         GNUNET_JSON_from_data_auto 
(&sessions[i].transfer_pub));
-    GNUNET_assert (0 ==
-                   json_array_append_new (mlist,
-                                          root));
-  }
-  res = TEH_RESPONSE_reply_json (connection,
-                                 mlist,
-                                 MHD_HTTP_OK);
-  json_decref (mlist);
-  return res;
-}
-
-
-/**
  * A merchant asked for details about a deposit, but
  * we do not know anything about the deposit. Generate the
  * 404 reply.
@@ -1363,217 +899,4 @@ TEH_RESPONSE_reply_transaction_unknown (struct 
MHD_Connection *connection,
 }
 
 
-/**
- * A merchant asked for details about a deposit, but
- * we did not execute the deposit yet. Generate a 202 reply.
- *
- * @param connection connection to the client
- * @param planned_exec_time planned execution time
- * @return MHD result code
- */
-int
-TEH_RESPONSE_reply_transfer_pending (struct MHD_Connection *connection,
-                                     struct GNUNET_TIME_Absolute 
planned_exec_time)
-{
-  return TEH_RESPONSE_reply_json_pack (connection,
-                                       MHD_HTTP_ACCEPTED,
-                                       "{s:o}",
-                                       "execution_time", 
GNUNET_JSON_from_time_abs (planned_exec_time));
-}
-
-
-/**
- * A merchant asked for details about a deposit.  Provide
- * them. Generates the 200 reply.
- *
- * @param connection connection to the client
- * @param h_contract_terms hash of the contract
- * @param h_wire hash of wire account details
- * @param coin_pub public key of the coin
- * @param coin_contribution how much did the coin we asked about
- *        contribute to the total transfer value? (deposit value minus fee)
- * @param wtid raw wire transfer identifier
- * @param exec_time execution time of the wire transfer
- * @return MHD result code
- */
-int
-TEH_RESPONSE_reply_track_transaction (struct MHD_Connection *connection,
-                                      const struct GNUNET_HashCode 
*h_contract_terms,
-                                      const struct GNUNET_HashCode *h_wire,
-                                      const struct TALER_CoinSpendPublicKeyP 
*coin_pub,
-                                      const struct TALER_Amount 
*coin_contribution,
-                                      const struct 
TALER_WireTransferIdentifierRawP *wtid,
-                                      struct GNUNET_TIME_Absolute exec_time)
-{
-  struct TALER_ConfirmWirePS cw;
-  struct TALER_ExchangePublicKeyP pub;
-  struct TALER_ExchangeSignatureP sig;
-
-  cw.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_WIRE);
-  cw.purpose.size = htonl (sizeof (struct TALER_ConfirmWirePS));
-  cw.h_wire = *h_wire;
-  cw.h_contract_terms = *h_contract_terms;
-  cw.wtid = *wtid;
-  cw.coin_pub = *coin_pub;
-  cw.execution_time = GNUNET_TIME_absolute_hton (exec_time);
-  TALER_amount_hton (&cw.coin_contribution,
-                     coin_contribution);
-  TEH_KS_sign (&cw.purpose,
-               &pub,
-               &sig);
-  return TEH_RESPONSE_reply_json_pack (connection,
-                                       MHD_HTTP_OK,
-                                       "{s:o, s:o, s:o, s:o, s:o}",
-                                       "wtid", GNUNET_JSON_from_data_auto 
(wtid),
-                                       "execution_time", 
GNUNET_JSON_from_time_abs (exec_time),
-                                       "coin_contribution", 
TALER_JSON_from_amount (coin_contribution),
-                                       "exchange_sig", 
GNUNET_JSON_from_data_auto (&sig),
-                                       "exchange_pub", 
GNUNET_JSON_from_data_auto (&pub));
-}
-
-
-/**
- * A merchant asked for transaction details about a wire transfer.
- * Provide them. Generates the 200 reply.
- *
- * @param connection connection to the client
- * @param total total amount that was transferred
- * @param merchant_pub public key of the merchant
- * @param h_wire destination account
- * @param wire_fee wire fee that was charged
- * @param exec_time execution time of the wire transfer
- * @param wdd_head linked list with details about the combined deposits
- * @return MHD result code
- */
-int
-TEH_RESPONSE_reply_track_transfer_details (struct MHD_Connection *connection,
-                                           const struct TALER_Amount *total,
-                                           const struct 
TALER_MerchantPublicKeyP *merchant_pub,
-                                           const struct GNUNET_HashCode 
*h_wire,
-                                           const struct TALER_Amount *wire_fee,
-                                           struct GNUNET_TIME_Absolute 
exec_time,
-                                           const struct 
TEH_TrackTransferDetail *wdd_head)
-{
-  const struct TEH_TrackTransferDetail *wdd_pos;
-  json_t *deposits;
-  struct TALER_WireDepositDetailP dd;
-  struct GNUNET_HashContext *hash_context;
-  struct TALER_WireDepositDataPS wdp;
-  struct TALER_ExchangePublicKeyP pub;
-  struct TALER_ExchangeSignatureP sig;
-
-  GNUNET_TIME_round_abs (&exec_time);
-  deposits = json_array ();
-  hash_context = GNUNET_CRYPTO_hash_context_start ();
-  for (wdd_pos = wdd_head; NULL != wdd_pos; wdd_pos = wdd_pos->next)
-  {
-    dd.h_contract_terms = wdd_pos->h_contract_terms;
-    dd.execution_time = GNUNET_TIME_absolute_hton (exec_time);
-    dd.coin_pub = wdd_pos->coin_pub;
-    TALER_amount_hton (&dd.deposit_value,
-                       &wdd_pos->deposit_value);
-    TALER_amount_hton (&dd.deposit_fee,
-                       &wdd_pos->deposit_fee);
-    GNUNET_CRYPTO_hash_context_read (hash_context,
-                                     &dd,
-                                     sizeof (struct TALER_WireDepositDetailP));
-    GNUNET_assert (0 ==
-                   json_array_append_new (deposits,
-                                          json_pack ("{s:o, s:o, s:o, s:o}",
-                                                     "h_contract_terms", 
GNUNET_JSON_from_data_auto (&wdd_pos->h_contract_terms),
-                                                     "coin_pub", 
GNUNET_JSON_from_data_auto (&wdd_pos->coin_pub),
-                                                     "deposit_value", 
TALER_JSON_from_amount (&wdd_pos->deposit_value),
-                                                     "deposit_fee", 
TALER_JSON_from_amount (&wdd_pos->deposit_fee))));
-  }
-  wdp.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_WIRE_DEPOSIT);
-  wdp.purpose.size = htonl (sizeof (struct TALER_WireDepositDataPS));
-  TALER_amount_hton (&wdp.total,
-                     total);
-  TALER_amount_hton (&wdp.wire_fee,
-                     wire_fee);
-  wdp.merchant_pub = *merchant_pub;
-  wdp.h_wire = *h_wire;
-  GNUNET_CRYPTO_hash_context_finish (hash_context,
-                                     &wdp.h_details);
-  TEH_KS_sign (&wdp.purpose,
-               &pub,
-               &sig);
-  return TEH_RESPONSE_reply_json_pack (connection,
-                                       MHD_HTTP_OK,
-                                       "{s:o, s:o, s:o, s:o, s:o, s:o, s:o, 
s:o}",
-                                       "total", TALER_JSON_from_amount (total),
-                                       "wire_fee", TALER_JSON_from_amount 
(wire_fee),
-                                       "merchant_pub", 
GNUNET_JSON_from_data_auto (merchant_pub),
-                                       "H_wire", GNUNET_JSON_from_data_auto 
(h_wire),
-                                       "execution_time", 
GNUNET_JSON_from_time_abs (exec_time),
-                                       "deposits", deposits,
-                                       "exchange_sig", 
GNUNET_JSON_from_data_auto (&sig),
-                                       "exchange_pub", 
GNUNET_JSON_from_data_auto (&pub));
-}
-
-
-
-/**
- * A wallet asked for /payback, but we do not know anything about the
- * original withdraw operation specified. Generates a 404 reply.
- *
- * @param connection connection to the client
- * @param ec Taler error code
- * @return MHD result code
- */
-int
-TEH_RESPONSE_reply_payback_unknown (struct MHD_Connection *connection,
-                                    enum TALER_ErrorCode ec)
-{
-  return TEH_RESPONSE_reply_json_pack (connection,
-                                       MHD_HTTP_NOT_FOUND,
-                                       "{s:s, s:I}",
-                                       "error", "blinded coin unknown",
-                                      "code", (json_int_t) ec);
-}
-
-
-/**
- * A wallet asked for /payback, return the successful response.
- *
- * @param connection connection to the client
- * @param coin_pub coin for which we are processing the payback request
- * @param reserve_pub public key of the reserve that will receive the payback
- * @param amount the amount we will wire back
- * @param timestamp when did the exchange receive the /payback request
- * @return MHD result code
- */
-int
-TEH_RESPONSE_reply_payback_success (struct MHD_Connection *connection,
-                                    const struct TALER_CoinSpendPublicKeyP 
*coin_pub,
-                                    const struct TALER_ReservePublicKeyP 
*reserve_pub,
-                                    const struct TALER_Amount *amount,
-                                    struct GNUNET_TIME_Absolute timestamp)
-{
-  struct TALER_PaybackConfirmationPS pc;
-  struct TALER_ExchangePublicKeyP pub;
-  struct TALER_ExchangeSignatureP sig;
-
-  pc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_PAYBACK);
-  pc.purpose.size = htonl (sizeof (struct TALER_PaybackConfirmationPS));
-  pc.timestamp = GNUNET_TIME_absolute_hton (timestamp);
-  TALER_amount_hton (&pc.payback_amount,
-                     amount);
-  pc.coin_pub = *coin_pub;
-  pc.reserve_pub = *reserve_pub;
-  TEH_KS_sign (&pc.purpose,
-               &pub,
-               &sig);
-  return TEH_RESPONSE_reply_json_pack (connection,
-                                       MHD_HTTP_OK,
-                                       "{s:o, s:o, s:o, s:o, s:o}",
-                                       "reserve_pub", 
GNUNET_JSON_from_data_auto (reserve_pub),
-                                       "timestamp", GNUNET_JSON_from_time_abs 
(timestamp),
-                                       "amount", TALER_JSON_from_amount 
(amount),
-                                       "exchange_sig", 
GNUNET_JSON_from_data_auto (&sig),
-                                       "exchange_pub", 
GNUNET_JSON_from_data_auto (&pub));
-}
-
-
-
 /* end of taler-exchange-httpd_responses.c */
diff --git a/src/exchange/taler-exchange-httpd_responses.h 
b/src/exchange/taler-exchange-httpd_responses.h
index 6a33b65..1e504fd 100644
--- a/src/exchange/taler-exchange-httpd_responses.h
+++ b/src/exchange/taler-exchange-httpd_responses.h
@@ -16,7 +16,7 @@
 
 /**
  * @file taler-exchange-httpd_responses.h
- * @brief API for generating the various replies of the exchange; these
+ * @brief API for generating generic replies of the exchange; these
  *        functions are called TEH_RESPONSE_reply_ and they generate
  *        and queue MHD response objects for a given connection.
  * @author Florian Dold
@@ -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);
 
 
 /**
@@ -290,46 +274,6 @@ TEH_RESPONSE_reply_coin_insufficient_funds (struct 
MHD_Connection *connection,
 
 
 /**
- * Generate refund conflict failure message. Returns the
- * transaction list @a tl with the details about the conflict.
- *
- * @param connection connection to the client
- * @param tl transaction list showing the conflict
- * @return MHD result code
- */
-int
-TEH_RESPONSE_reply_refund_conflict (struct MHD_Connection *connection,
-                                    const struct 
TALER_EXCHANGEDB_TransactionList *tl);
-
-
-/**
- * Generate generic refund failure message. All the details
- * are in the @a response_code.  The body can be empty.
- *
- * @param connection connection to the client
- * @param response_code response code to generate
- * @param ec error code uniquely identifying the error
- * @return MHD result code
- */
-int
-TEH_RESPONSE_reply_refund_failure (struct MHD_Connection *connection,
-                                  unsigned int response_code,
-                                  enum TALER_ErrorCode ec);
-
-
-/**
- * Generate successful refund confirmation message.
- *
- * @param connection connection to the client
- * @param refund details about the successful refund
- * @return MHD result code
- */
-int
-TEH_RESPONSE_reply_refund_success (struct MHD_Connection *connection,
-                                   const struct TALER_EXCHANGEDB_Refund 
*refund);
-
-
-/**
  * A merchant asked for details about a deposit, but
  * we do not know anything about the deposit. Generate the
  * 404 reply.
@@ -344,273 +288,13 @@ TEH_RESPONSE_reply_transaction_unknown (struct 
MHD_Connection *connection,
 
 
 /**
- * A merchant asked for details about a deposit, but
- * we did not execute the deposit yet. Generate a 202 reply.
- *
- * @param connection connection to the client
- * @param planned_exec_time planned execution time
- * @return MHD result code
- */
-int
-TEH_RESPONSE_reply_transfer_pending (struct MHD_Connection *connection,
-                                    struct GNUNET_TIME_Absolute 
planned_exec_time);
-
-
-/**
- * A merchant asked for details about a deposit.  Provide
- * them. Generates the 200 reply.
+ * Compile the transaction history of a coin into a JSON object.
  *
- * @param connection connection to the client
- * @param h_contract_terms hash of the proposal data
- * @param h_wire hash of wire account details
- * @param coin_pub public key of the coin
- * @param coin_contribution contribution of this coin to the total amount 
transferred
- * @param wtid raw wire transfer identifier
- * @param exec_time execution time of the wire transfer
- * @return MHD result code
- */
-int
-TEH_RESPONSE_reply_track_transaction (struct MHD_Connection *connection,
-                                      const struct GNUNET_HashCode 
*h_contract_terms,
-                                      const struct GNUNET_HashCode *h_wire,
-                                      const struct TALER_CoinSpendPublicKeyP 
*coin_pub,
-                                      const struct TALER_Amount 
*coin_contribution,
-                                      const struct 
TALER_WireTransferIdentifierRawP *wtid,
-                                      struct GNUNET_TIME_Absolute exec_time);
-
-
-/**
- * Detail for /wire/deposit response.
+ * @param tl transaction history to JSON-ify
+ * @return json representation of the @a rh
  */
-struct TEH_TrackTransferDetail
-{
-
-  /**
-   * We keep deposit details in a DLL.
-   */
-  struct TEH_TrackTransferDetail *next;
-
-  /**
-   * We keep deposit details in a DLL.
-   */
-  struct TEH_TrackTransferDetail *prev;
-
-  /**
-   * Hash of the proposal data.
-   */
-  struct GNUNET_HashCode h_contract_terms;
-
-  /**
-   * Coin's public key.
-   */
-  struct TALER_CoinSpendPublicKeyP coin_pub;
-
-  /**
-   * Total value of the coin.
-   */
-  struct TALER_Amount deposit_value;
-
-  /**
-   * Fees charged by the exchange for the deposit.
-   */
-  struct TALER_Amount deposit_fee;
-};
-
-
-/**
- * A merchant asked for transaction details about a wire transfer.
- * Provide them. Generates the 200 reply.
- *
- * @param connection connection to the client
- * @param total total amount that was transferred
- * @param merchant_pub public key of the merchant
- * @param h_wire destination account
- * @param wire_fee wire fee that was charged
- * @param exec_time execution time of the wire transfer
- * @param wdd_head linked list with details about the combined deposits
- * @return MHD result code
- */
-int
-TEH_RESPONSE_reply_track_transfer_details (struct MHD_Connection *connection,
-                                           const struct TALER_Amount *total,
-                                           const struct 
TALER_MerchantPublicKeyP *merchant_pub,
-                                           const struct GNUNET_HashCode 
*h_wire,
-                                           const struct TALER_Amount *wire_fee,
-                                           struct GNUNET_TIME_Absolute 
exec_time,
-                                           const struct 
TEH_TrackTransferDetail *wdd_head);
-
-
-/**
- * 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
- * @param session_hash hash of the refresh session
- * @param noreveal_index which index will the client not have to reveal
- * @return a MHD status code
- */
-int
-TEH_RESPONSE_reply_refresh_melt_success (struct MHD_Connection *connection,
-                                         const struct GNUNET_HashCode 
*session_hash,
-                                         uint16_t noreveal_index);
-
-
-/**
- * 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
- * contribution of the coin for the melt.  Thus, the exchange
- * refuses the melt operation.
- *
- * @param connection the connection to send the response to
- * @param coin_pub public key of the coin
- * @param coin_value original value of the coin
- * @param tl transaction history for the coin
- * @param requested how much this coin was supposed to contribute
- * @param residual remaining value of the coin (after subtracting @a tl)
- * @return a MHD result code
- */
-int
-TEH_RESPONSE_reply_refresh_melt_insufficient_funds (struct MHD_Connection 
*connection,
-                                                    const struct 
TALER_CoinSpendPublicKeyP *coin_pub,
-                                                    struct TALER_Amount 
coin_value,
-                                                    struct 
TALER_EXCHANGEDB_TransactionList *tl,
-                                                    struct TALER_Amount 
requested,
-                                                    struct TALER_Amount 
residual);
-
-
-/**
- * Send a response for "/refresh/reveal".
- *
- * @param connection the connection to send the response to
- * @param num_newcoins number of new coins for which we reveal data
- * @param sigs array of @a num_newcoins signatures revealed
- * @return a MHD result code
- */
-int
-TEH_RESPONSE_reply_refresh_reveal_success (struct MHD_Connection *connection,
-                                           unsigned int num_newcoins,
-                                           const struct 
TALER_DenominationSignature *sigs);
-
-
-/**
- * Send a response for a failed "/refresh/reveal", where the
- * revealed value(s) do not match the original commitment.
- *
- * @param connection the connection to send the response to
- * @param session info about session
- * @param commit_coins array of @a num_newcoins committed envelopes at offset 
@a gamma
- * @param denom_pubs array of @a num_newcoins denomination keys for the new 
coins
- * @param gamma_tp transfer public key at offset @a gamma
- * @return a MHD result code
- */
-int
-TEH_RESPONSE_reply_refresh_reveal_missmatch (struct MHD_Connection *connection,
-                                             const struct 
TALER_EXCHANGEDB_RefreshSession *session,
-                                             const struct 
TALER_EXCHANGEDB_RefreshCommitCoin *commit_coins,
-                                             const struct 
TALER_DenominationPublicKey *denom_pubs,
-                                             const struct 
TALER_TransferPublicKeyP *gamma_tp);
-
-
-/**
- * @brief Information for each session a coin was melted into.
- */
-struct TEH_RESPONSE_LinkSessionInfo
-{
-  /**
-   * Transfer public key of the coin.
-   */
-  struct TALER_TransferPublicKeyP transfer_pub;
-
-  /**
-   * Linked data of coins being created in the session.
-   */
-  struct TALER_EXCHANGEDB_LinkDataList *ldl;
-
-};
-
-
-/**
- * Send a response for "/refresh/link".
- *
- * @param connection the connection to send the response to
- * @param num_sessions number of sessions the coin was used in
- * @param sessions array of @a num_session entries with
- *                  information for each session
- * @return a MHD result code
- */
-int
-TEH_RESPONSE_reply_refresh_link_success (struct MHD_Connection *connection,
-                                         unsigned int num_sessions,
-                                         const struct 
TEH_RESPONSE_LinkSessionInfo *sessions);
-
-
-/**
- * A wallet asked for /payback, but we do not know anything about the
- * original withdraw operation specified. Generates a 404 reply.
- *
- * @param connection connection to the client
- * @param ec Taler error code
- * @return MHD result code
- */
-int
-TEH_RESPONSE_reply_payback_unknown (struct MHD_Connection *connection,
-                                    enum TALER_ErrorCode ec);
-
-
-/**
- * A wallet asked for /payback, return the successful response.
- *
- * @param connection connection to the client
- * @param coin_pub coin for which we are processing the payback request
- * @param reserve_pub public key of the reserve that will receive the payback
- * @param amount the amount we will wire back
- * @param timestamp when did the exchange receive the /payback request
- * @return MHD result code
- */
-int
-TEH_RESPONSE_reply_payback_success (struct MHD_Connection *connection,
-                                    const struct TALER_CoinSpendPublicKeyP 
*coin_pub,
-                                    const struct TALER_ReservePublicKeyP 
*reserve_pub,
-                                    const struct TALER_Amount *amount,
-                                    struct GNUNET_TIME_Absolute timestamp);
+json_t *
+TEH_RESPONSE_compile_transaction_history (const struct 
TALER_EXCHANGEDB_TransactionList *tl);
 
 
 #endif
diff --git a/src/exchange/taler-exchange-httpd_track_transaction.c 
b/src/exchange/taler-exchange-httpd_track_transaction.c
new file mode 100644
index 0000000..7c1bd6a
--- /dev/null
+++ b/src/exchange/taler-exchange-httpd_track_transaction.c
@@ -0,0 +1,368 @@
+/*
+  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_track_transaction.c
+ * @brief Handle wire transfer tracking-related requests
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <jansson.h>
+#include <microhttpd.h>
+#include <pthread.h>
+#include "taler_signatures.h"
+#include "taler-exchange-httpd_parsing.h"
+#include "taler-exchange-httpd_keystate.h"
+#include "taler-exchange-httpd_track_transaction.h"
+#include "taler-exchange-httpd_responses.h"
+
+
+/**
+ * A merchant asked for details about a deposit, but
+ * we did not execute the deposit yet. Generate a 202 reply.
+ *
+ * @param connection connection to the client
+ * @param planned_exec_time planned execution time
+ * @return MHD result code
+ */
+static int
+reply_transfer_pending (struct MHD_Connection *connection,
+                       struct GNUNET_TIME_Absolute planned_exec_time)
+{
+  return TEH_RESPONSE_reply_json_pack (connection,
+                                       MHD_HTTP_ACCEPTED,
+                                       "{s:o}",
+                                       "execution_time", 
GNUNET_JSON_from_time_abs (planned_exec_time));
+}
+
+
+/**
+ * A merchant asked for details about a deposit.  Provide
+ * them. Generates the 200 reply.
+ *
+ * @param connection connection to the client
+ * @param h_contract_terms hash of the contract
+ * @param h_wire hash of wire account details
+ * @param coin_pub public key of the coin
+ * @param coin_contribution how much did the coin we asked about
+ *        contribute to the total transfer value? (deposit value minus fee)
+ * @param wtid raw wire transfer identifier
+ * @param exec_time execution time of the wire transfer
+ * @return MHD result code
+ */
+static int
+reply_track_transaction (struct MHD_Connection *connection,
+                        const struct GNUNET_HashCode *h_contract_terms,
+                        const struct GNUNET_HashCode *h_wire,
+                        const struct TALER_CoinSpendPublicKeyP *coin_pub,
+                        const struct TALER_Amount *coin_contribution,
+                        const struct TALER_WireTransferIdentifierRawP *wtid,
+                        struct GNUNET_TIME_Absolute exec_time)
+{
+  struct TALER_ConfirmWirePS cw;
+  struct TALER_ExchangePublicKeyP pub;
+  struct TALER_ExchangeSignatureP sig;
+
+  cw.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_WIRE);
+  cw.purpose.size = htonl (sizeof (struct TALER_ConfirmWirePS));
+  cw.h_wire = *h_wire;
+  cw.h_contract_terms = *h_contract_terms;
+  cw.wtid = *wtid;
+  cw.coin_pub = *coin_pub;
+  cw.execution_time = GNUNET_TIME_absolute_hton (exec_time);
+  TALER_amount_hton (&cw.coin_contribution,
+                     coin_contribution);
+  TEH_KS_sign (&cw.purpose,
+               &pub,
+               &sig);
+  return TEH_RESPONSE_reply_json_pack (connection,
+                                       MHD_HTTP_OK,
+                                       "{s:o, s:o, s:o, s:o, s:o}",
+                                       "wtid", GNUNET_JSON_from_data_auto 
(wtid),
+                                       "execution_time", 
GNUNET_JSON_from_time_abs (exec_time),
+                                       "coin_contribution", 
TALER_JSON_from_amount (coin_contribution),
+                                       "exchange_sig", 
GNUNET_JSON_from_data_auto (&sig),
+                                       "exchange_pub", 
GNUNET_JSON_from_data_auto (&pub));
+}
+
+
+/**
+ * Closure for #handle_wtid_data.
+ */
+struct DepositWtidContext
+{
+
+  /**
+   * Deposit details.
+   */
+  const struct TALER_DepositTrackPS *tps;
+
+  /**
+   * Public key of the merchant.
+   */
+  const struct TALER_MerchantPublicKeyP *merchant_pub;
+  
+  /**
+   * Set by #handle_wtid data to the wire transfer ID.
+   */ 
+  struct TALER_WireTransferIdentifierRawP wtid;
+  
+  /**
+   * Set by #handle_wtid data to the coin's contribution to the wire transfer.
+   */ 
+  struct TALER_Amount coin_contribution;
+  
+  /**
+   * Set by #handle_wtid data to the fee charged to the coin.
+   */ 
+  struct TALER_Amount coin_fee;
+
+  /**
+   * Set by #handle_wtid data to the wire transfer execution time.
+   */ 
+  struct GNUNET_TIME_Absolute execution_time;
+
+  /**
+   * Set by #handle_wtid to the coin contribution to the transaction
+   * (that is, @e coin_contribution minus @e coin_fee).
+   */
+  struct TALER_Amount coin_delta;
+
+  /**
+   * Set to #GNUNET_YES by #handle_wtid if the wire transfer is still pending
+   * (and the above were not set).
+   * Set to #GNUNET_SYSERR if there was a serious error.
+   */
+  int pending;
+};
+
+
+/**
+ * Function called with the results of the lookup of the
+ * wire transfer identifier information.
+ *
+ * @param cls our context for transmission
+ * @param wtid raw wire transfer identifier, NULL
+ *         if the transaction was not yet done
+ * @param coin_contribution how much did the coin we asked about
+ *        contribute to the total transfer value? (deposit value including fee)
+ * @param coin_fee how much did the exchange charge for the deposit fee
+ * @param execution_time when was the transaction done, or
+ *         when we expect it to be done (if @a wtid was NULL);
+ *         #GNUNET_TIME_UNIT_FOREVER_ABS if the /deposit is unknown
+ *         to the exchange
+ */
+static void
+handle_wtid_data (void *cls,
+                 const struct TALER_WireTransferIdentifierRawP *wtid,
+                  const struct TALER_Amount *coin_contribution,
+                  const struct TALER_Amount *coin_fee,
+                 struct GNUNET_TIME_Absolute execution_time)
+{
+  struct DepositWtidContext *ctx = cls;
+
+  if (NULL == wtid)
+  {
+    ctx->pending = GNUNET_YES;
+    ctx->execution_time = execution_time;
+    return;
+  }
+  if (GNUNET_SYSERR ==
+      TALER_amount_subtract (&ctx->coin_delta,
+                            coin_contribution,
+                            coin_fee))
+  {
+    GNUNET_break (0);
+    ctx->pending = GNUNET_SYSERR;
+    return;
+  }
+  ctx->wtid = *wtid;
+  ctx->execution_time = execution_time;
+  ctx->coin_contribution = *coin_contribution;
+  ctx->coin_fee = *coin_fee;
+}
+
+
+/**
+ * Execute a "/track/transaction".  Returns the transfer information
+ * associated with the given deposit.
+ *
+ * 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 closure of type `struct DepositWtidContext *`
+ * @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
+track_transaction_transaction (void *cls,
+                              struct MHD_Connection *connection,
+                              struct TALER_EXCHANGEDB_Session *session,
+                              int *mhd_ret)
+{
+  struct DepositWtidContext *ctx = cls;
+  enum GNUNET_DB_QueryStatus qs;
+
+  qs = TEH_plugin->wire_lookup_deposit_wtid (TEH_plugin->cls,
+                                            session,
+                                            &ctx->tps->h_contract_terms,
+                                            &ctx->tps->h_wire,
+                                            &ctx->tps->coin_pub,
+                                            ctx->merchant_pub,
+                                            &handle_wtid_data,
+                                            ctx);
+  if (0 > qs)
+  {
+    if (GNUNET_DB_STATUS_HARD_ERROR == qs)
+    {
+      GNUNET_break (0);
+      *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+                                                      
TALER_EC_TRACK_TRANSACTION_DB_FETCH_FAILED);
+    }
+    return qs;
+  }
+  if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
+  {
+    *mhd_ret = TEH_RESPONSE_reply_transaction_unknown (connection,
+                                                      
TALER_EC_TRACK_TRANSACTION_NOT_FOUND);
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  }
+  return qs;
+}
+
+
+/**
+ * Check the merchant signature, and if it is valid,
+ * return the wire transfer identifier.
+ *
+ * @param connection the MHD connection to handle
+ * @param tps signed request to execute
+ * @param merchant_pub public key from the merchant
+ * @param merchant_sig signature from the merchant (to be checked)
+ * @return MHD result code
+ */
+static int
+check_and_handle_track_transaction_request (struct MHD_Connection *connection,
+                                            const struct TALER_DepositTrackPS 
*tps,
+                                            const struct 
TALER_MerchantPublicKeyP *merchant_pub,
+                                            const struct 
TALER_MerchantSignatureP *merchant_sig)
+{
+  struct DepositWtidContext ctx;
+  int mhd_ret;
+
+  if (GNUNET_OK !=
+      GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MERCHANT_TRACK_TRANSACTION,
+                                 &tps->purpose,
+                                 &merchant_sig->eddsa_sig,
+                                 &merchant_pub->eddsa_pub))
+  {
+    GNUNET_break_op (0);
+    return TEH_RESPONSE_reply_signature_invalid (connection,
+                                                
TALER_EC_TRACK_TRANSACTION_MERCHANT_SIGNATURE_INVALID,
+                                                "merchant_sig");
+  }
+  ctx.pending = GNUNET_NO;
+  ctx.tps = tps;
+  ctx.merchant_pub = merchant_pub;
+  
+  if (GNUNET_OK !=
+      TEH_DB_run_transaction (connection,
+                             &mhd_ret,
+                             &track_transaction_transaction,
+                             &ctx))
+    return mhd_ret;
+  if (GNUNET_YES == ctx.pending)
+    return reply_transfer_pending (connection,
+                                  ctx.execution_time);
+  if (GNUNET_SYSERR == ctx.pending)
+    return TEH_RESPONSE_reply_internal_db_error (connection,
+                                                
TALER_EC_TRACK_TRANSACTION_DB_FEE_INCONSISTENT);
+  return reply_track_transaction (connection,
+                                 &tps->h_contract_terms,
+                                 &tps->h_wire,
+                                 &tps->coin_pub,
+                                 &ctx.coin_delta,
+                                 &ctx.wtid,
+                                 ctx.execution_time);
+}
+
+
+/**
+ * Handle a "/track/transaction" request.
+ *
+ * @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_TRACKING_handler_track_transaction (struct TEH_RequestHandler *rh,
+                                        struct MHD_Connection *connection,
+                                        void **connection_cls,
+                                        const char *upload_data,
+                                        size_t *upload_data_size)
+{
+  int res;
+  json_t *json;
+  struct TALER_DepositTrackPS tps;
+  struct TALER_MerchantSignatureP merchant_sig;
+  struct GNUNET_JSON_Specification spec[] = {
+    GNUNET_JSON_spec_fixed_auto ("H_wire", &tps.h_wire),
+    GNUNET_JSON_spec_fixed_auto ("h_contract_terms", &tps.h_contract_terms),
+    GNUNET_JSON_spec_fixed_auto ("coin_pub", &tps.coin_pub),
+    GNUNET_JSON_spec_fixed_auto ("merchant_pub", &tps.merchant),
+    GNUNET_JSON_spec_fixed_auto ("merchant_sig", &merchant_sig),
+    GNUNET_JSON_spec_end ()
+  };
+
+  res = TEH_PARSE_post_json (connection,
+                             connection_cls,
+                             upload_data,
+                             upload_data_size,
+                             &json);
+  if (GNUNET_SYSERR == res)
+    return MHD_NO;
+  if ( (GNUNET_NO == res) || (NULL == json) )
+    return MHD_YES;
+  res = TEH_PARSE_json_data (connection,
+                             json,
+                             spec);
+  if (GNUNET_OK != res)
+  {
+    json_decref (json);
+    return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
+  }
+  tps.purpose.size = htonl (sizeof (struct TALER_DepositTrackPS));
+  tps.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_TRACK_TRANSACTION);
+  res = check_and_handle_track_transaction_request (connection,
+                                                    &tps,
+                                                    &tps.merchant,
+                                                    &merchant_sig);
+  GNUNET_JSON_parse_free (spec);
+  json_decref (json);
+  return res;
+}
+
+
+/* end of taler-exchange-httpd_track_transaction.c */
diff --git a/src/exchange/taler-exchange-httpd_tracking.h 
b/src/exchange/taler-exchange-httpd_track_transaction.h
similarity index 65%
copy from src/exchange/taler-exchange-httpd_tracking.h
copy to src/exchange/taler-exchange-httpd_track_transaction.h
index fe22304..f1cd366 100644
--- a/src/exchange/taler-exchange-httpd_tracking.h
+++ b/src/exchange/taler-exchange-httpd_track_transaction.h
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  Copyright (C) 2014, 2015 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,12 +14,12 @@
   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
 */
 /**
- * @file taler-exchange-httpd_tracking.h
+ * @file taler-exchange-httpd_track_transaction.h
  * @brief Handle wire transfer tracking-related requests
  * @author Christian Grothoff
  */
-#ifndef TALER_EXCHANGE_HTTPD_TRACKING_H
-#define TALER_EXCHANGE_HTTPD_TRACKING_H
+#ifndef TALER_EXCHANGE_HTTPD_TRACK_TRANSACTION_H
+#define TALER_EXCHANGE_HTTPD_TRACK_TRANSACTION_H
 
 #include <gnunet/gnunet_util_lib.h>
 #include <microhttpd.h>
@@ -27,24 +27,6 @@
 
 
 /**
- * Handle a "/track/transfer" request.
- *
- * @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_TRACKING_handler_track_transfer (struct TEH_RequestHandler *rh,
-                                     struct MHD_Connection *connection,
-                                     void **connection_cls,
-                                     const char *upload_data,
-                                     size_t *upload_data_size);
-
-
-/**
  * Handle a "/track/transaction" request.
  *
  * @param rh context of the handler
diff --git a/src/exchange/taler-exchange-httpd_track_transfer.c 
b/src/exchange/taler-exchange-httpd_track_transfer.c
new file mode 100644
index 0000000..57b621e
--- /dev/null
+++ b/src/exchange/taler-exchange-httpd_track_transfer.c
@@ -0,0 +1,486 @@
+/*
+  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_track_transfer.c
+ * @brief Handle wire transfer /track/transfer requests
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <jansson.h>
+#include <microhttpd.h>
+#include <pthread.h>
+#include "taler_signatures.h"
+#include "taler-exchange-httpd_parsing.h"
+#include "taler-exchange-httpd_keystate.h"
+#include "taler-exchange-httpd_track_transfer.h"
+#include "taler-exchange-httpd_responses.h"
+
+
+/**
+ * Detail for /wire/deposit response.
+ */
+struct TEH_TrackTransferDetail
+{
+
+  /**
+   * We keep deposit details in a DLL.
+   */
+  struct TEH_TrackTransferDetail *next;
+
+  /**
+   * We keep deposit details in a DLL.
+   */
+  struct TEH_TrackTransferDetail *prev;
+
+  /**
+   * Hash of the proposal data.
+   */
+  struct GNUNET_HashCode h_contract_terms;
+
+  /**
+   * Coin's public key.
+   */
+  struct TALER_CoinSpendPublicKeyP coin_pub;
+
+  /**
+   * Total value of the coin.
+   */
+  struct TALER_Amount deposit_value;
+
+  /**
+   * Fees charged by the exchange for the deposit.
+   */
+  struct TALER_Amount deposit_fee;
+};
+
+
+/**
+ * A merchant asked for transaction details about a wire transfer.
+ * Provide them. Generates the 200 reply.
+ *
+ * @param connection connection to the client
+ * @param total total amount that was transferred
+ * @param merchant_pub public key of the merchant
+ * @param h_wire destination account
+ * @param wire_fee wire fee that was charged
+ * @param exec_time execution time of the wire transfer
+ * @param wdd_head linked list with details about the combined deposits
+ * @return MHD result code
+ */
+static int
+reply_track_transfer_details (struct MHD_Connection *connection,
+                             const struct TALER_Amount *total,
+                             const struct TALER_MerchantPublicKeyP 
*merchant_pub,
+                             const struct GNUNET_HashCode *h_wire,
+                             const struct TALER_Amount *wire_fee,
+                             struct GNUNET_TIME_Absolute exec_time,
+                             const struct TEH_TrackTransferDetail *wdd_head)
+{
+  const struct TEH_TrackTransferDetail *wdd_pos;
+  json_t *deposits;
+  struct TALER_WireDepositDetailP dd;
+  struct GNUNET_HashContext *hash_context;
+  struct TALER_WireDepositDataPS wdp;
+  struct TALER_ExchangePublicKeyP pub;
+  struct TALER_ExchangeSignatureP sig;
+
+  GNUNET_TIME_round_abs (&exec_time);
+  deposits = json_array ();
+  hash_context = GNUNET_CRYPTO_hash_context_start ();
+  for (wdd_pos = wdd_head; NULL != wdd_pos; wdd_pos = wdd_pos->next)
+  {
+    dd.h_contract_terms = wdd_pos->h_contract_terms;
+    dd.execution_time = GNUNET_TIME_absolute_hton (exec_time);
+    dd.coin_pub = wdd_pos->coin_pub;
+    TALER_amount_hton (&dd.deposit_value,
+                       &wdd_pos->deposit_value);
+    TALER_amount_hton (&dd.deposit_fee,
+                       &wdd_pos->deposit_fee);
+    GNUNET_CRYPTO_hash_context_read (hash_context,
+                                     &dd,
+                                     sizeof (struct TALER_WireDepositDetailP));
+    GNUNET_assert (0 ==
+                   json_array_append_new (deposits,
+                                          json_pack ("{s:o, s:o, s:o, s:o}",
+                                                     "h_contract_terms", 
GNUNET_JSON_from_data_auto (&wdd_pos->h_contract_terms),
+                                                     "coin_pub", 
GNUNET_JSON_from_data_auto (&wdd_pos->coin_pub),
+                                                     "deposit_value", 
TALER_JSON_from_amount (&wdd_pos->deposit_value),
+                                                     "deposit_fee", 
TALER_JSON_from_amount (&wdd_pos->deposit_fee))));
+  }
+  wdp.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_WIRE_DEPOSIT);
+  wdp.purpose.size = htonl (sizeof (struct TALER_WireDepositDataPS));
+  TALER_amount_hton (&wdp.total,
+                     total);
+  TALER_amount_hton (&wdp.wire_fee,
+                     wire_fee);
+  wdp.merchant_pub = *merchant_pub;
+  wdp.h_wire = *h_wire;
+  GNUNET_CRYPTO_hash_context_finish (hash_context,
+                                     &wdp.h_details);
+  TEH_KS_sign (&wdp.purpose,
+               &pub,
+               &sig);
+  return TEH_RESPONSE_reply_json_pack (connection,
+                                       MHD_HTTP_OK,
+                                       "{s:o, s:o, s:o, s:o, s:o, s:o, s:o, 
s:o}",
+                                       "total", TALER_JSON_from_amount (total),
+                                       "wire_fee", TALER_JSON_from_amount 
(wire_fee),
+                                       "merchant_pub", 
GNUNET_JSON_from_data_auto (merchant_pub),
+                                       "H_wire", GNUNET_JSON_from_data_auto 
(h_wire),
+                                       "execution_time", 
GNUNET_JSON_from_time_abs (exec_time),
+                                       "deposits", deposits,
+                                       "exchange_sig", 
GNUNET_JSON_from_data_auto (&sig),
+                                       "exchange_pub", 
GNUNET_JSON_from_data_auto (&pub));
+}
+
+
+/**
+ * Closure for #handle_transaction_data.
+ */
+struct WtidTransactionContext
+{
+
+  /**
+   * Identifier of the wire transfer to track.
+   */
+  struct TALER_WireTransferIdentifierRawP wtid;
+
+  /**
+   * Total amount of the wire transfer, as calculated by
+   * summing up the individual amounts. To be rounded down
+   * to calculate the real transfer amount at the end.
+   * Only valid if @e is_valid is #GNUNET_YES.
+   */
+  struct TALER_Amount total;
+
+  /**
+   * Public key of the merchant, only valid if @e is_valid
+   * is #GNUNET_YES.
+   */
+  struct TALER_MerchantPublicKeyP merchant_pub;
+
+  /**
+   * Which method was used to wire the funds?
+   */
+  char *wire_method;
+
+  /**
+   * Hash of the wire details of the merchant (identical for all
+   * deposits), only valid if @e is_valid is #GNUNET_YES.
+   */
+  struct GNUNET_HashCode h_wire;
+
+  /**
+   * Wire fee applicable at @e exec_time.
+   */
+  struct TALER_Amount wire_fee;
+
+  /**
+   * Execution time of the wire transfer
+   */
+  struct GNUNET_TIME_Absolute exec_time;
+
+  /**
+   * Head of DLL with details for /wire/deposit response.
+   */
+  struct TEH_TrackTransferDetail *wdd_head;
+
+  /**
+   * Head of DLL with details for /wire/deposit response.
+   */
+  struct TEH_TrackTransferDetail *wdd_tail;
+
+  /**
+   * JSON array with details about the individual deposits.
+   */
+  json_t *deposits;
+
+  /**
+   * Initially #GNUNET_NO, if we found no deposits so far.  Set to
+   * #GNUNET_YES if we got transaction data, and the database replies
+   * remained consistent with respect to @e merchant_pub and @e h_wire
+   * (as they should).  Set to #GNUNET_SYSERR if we encountered an
+   * internal error.
+   */
+  int is_valid;
+
+};
+
+
+/**
+ * Function called with the results of the lookup of the
+ * transaction data for the given wire transfer identifier.
+ *
+ * @param cls our context for transmission
+ * @param rowid which row in the DB is the information from (for diagnostics)
+ * @param merchant_pub public key of the merchant (should be same for all 
callbacks with the same @e cls)
+ * @param wire_method which wire plugin was used
+ * @param h_wire hash of wire transfer details of the merchant (should be same 
for all callbacks with the same @e cls)
+ * @param exec_time execution time of the wire transfer (should be same for 
all callbacks with the same @e cls)
+ * @param h_contract_terms which proposal was this payment about
+ * @param coin_pub which public key was this payment about
+ * @param deposit_value amount contributed by this coin in total
+ * @param deposit_fee deposit fee charged by exchange for this coin
+ */
+static void
+handle_transaction_data (void *cls,
+                         uint64_t rowid,
+                         const struct TALER_MerchantPublicKeyP *merchant_pub,
+                         const char *wire_method,
+                         const struct GNUNET_HashCode *h_wire,
+                         struct GNUNET_TIME_Absolute exec_time,
+                         const struct GNUNET_HashCode *h_contract_terms,
+                         const struct TALER_CoinSpendPublicKeyP *coin_pub,
+                         const struct TALER_Amount *deposit_value,
+                         const struct TALER_Amount *deposit_fee)
+{
+  struct WtidTransactionContext *ctx = cls;
+  struct TALER_Amount delta;
+  struct TEH_TrackTransferDetail *wdd;
+
+  if (GNUNET_SYSERR == ctx->is_valid)
+    return;
+  if (GNUNET_NO == ctx->is_valid)
+  {
+    ctx->merchant_pub = *merchant_pub;
+    ctx->h_wire = *h_wire;
+    ctx->exec_time = exec_time;
+    ctx->wire_method = GNUNET_strdup (wire_method);
+    ctx->is_valid = GNUNET_YES;
+    if (GNUNET_OK !=
+        TALER_amount_subtract (&ctx->total,
+                               deposit_value,
+                               deposit_fee))
+    {
+      GNUNET_break (0);
+      ctx->is_valid = GNUNET_SYSERR;
+      return;
+    }
+  }
+  else
+  {
+    if ( (0 != memcmp (&ctx->merchant_pub,
+                       merchant_pub,
+                       sizeof (struct TALER_MerchantPublicKeyP))) ||
+         (0 != strcmp (wire_method,
+                       ctx->wire_method)) ||
+         (0 != memcmp (&ctx->h_wire,
+                       h_wire,
+                       sizeof (struct GNUNET_HashCode))) )
+    {
+      GNUNET_break (0);
+      ctx->is_valid = GNUNET_SYSERR;
+      return;
+    }
+    if (GNUNET_OK !=
+        TALER_amount_subtract (&delta,
+                               deposit_value,
+                               deposit_fee))
+    {
+      GNUNET_break (0);
+      ctx->is_valid = GNUNET_SYSERR;
+      return;
+    }
+    if (GNUNET_OK !=
+        TALER_amount_add (&ctx->total,
+                          &ctx->total,
+                          &delta))
+    {
+      GNUNET_break (0);
+      ctx->is_valid = GNUNET_SYSERR;
+      return;
+    }
+  }
+  wdd = GNUNET_new (struct TEH_TrackTransferDetail);
+  wdd->deposit_value = *deposit_value;
+  wdd->deposit_fee = *deposit_fee;
+  wdd->h_contract_terms = *h_contract_terms;
+  wdd->coin_pub = *coin_pub;
+  GNUNET_CONTAINER_DLL_insert (ctx->wdd_head,
+                               ctx->wdd_tail,
+                               wdd);
+}
+
+
+/**
+ * Execute a "/track/transfer".  Returns the transaction information
+ * associated with the given wire transfer identifier.
+ * 
+ * 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 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
+ */
+static enum GNUNET_DB_QueryStatus
+track_transfer_transaction (void *cls,
+                           struct MHD_Connection *connection,
+                           struct TALER_EXCHANGEDB_Session *session,
+                           int *mhd_ret)
+{
+  struct WtidTransactionContext *ctx = cls;
+  enum GNUNET_DB_QueryStatus qs;
+  struct GNUNET_TIME_Absolute wire_fee_start_date;
+  struct GNUNET_TIME_Absolute wire_fee_end_date;
+  struct TALER_MasterSignatureP wire_fee_master_sig;
+
+  ctx->is_valid = GNUNET_NO;
+  ctx->wdd_head = NULL;
+  ctx->wdd_tail = NULL;
+  ctx->wire_method = NULL;
+  qs = TEH_plugin->lookup_wire_transfer (TEH_plugin->cls,
+                                        session,
+                                        &ctx->wtid,
+                                        &handle_transaction_data,
+                                        ctx);
+  if (0 > qs)
+  {
+    if (GNUNET_DB_STATUS_HARD_ERROR == qs)
+    {
+      GNUNET_break (0);
+      *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+                                                      
TALER_EC_TRACK_TRANSFER_DB_FETCH_FAILED);
+    }
+    return qs;
+  }
+  if (GNUNET_SYSERR == ctx->is_valid)
+  {
+    GNUNET_break (0);
+    *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+                                                    
TALER_EC_TRACK_TRANSFER_DB_INCONSISTENT);
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  }
+  if (GNUNET_NO == ctx->is_valid)
+  {
+    *mhd_ret = TEH_RESPONSE_reply_arg_unknown (connection,
+                                              
TALER_EC_TRACK_TRANSFER_WTID_NOT_FOUND,
+                                              "wtid");
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  }
+  qs = TEH_plugin->get_wire_fee (TEH_plugin->cls,
+                                session,
+                                ctx->wire_method,
+                                ctx->exec_time,
+                                &wire_fee_start_date,
+                                &wire_fee_end_date,
+                                &ctx->wire_fee,
+                                &wire_fee_master_sig);
+  if (0 >= qs)
+  {
+    if ( (GNUNET_DB_STATUS_HARD_ERROR == qs) ||
+        (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS) )
+    {
+      GNUNET_break (0);
+      *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+                                                      
TALER_EC_TRACK_TRANSFER_WIRE_FEE_NOT_FOUND);
+    }
+    return qs;
+  }
+  if (GNUNET_OK !=
+      TALER_amount_subtract (&ctx->total,
+                             &ctx->total,
+                             &ctx->wire_fee))
+  {
+    GNUNET_break (0);
+    *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+                                                    
TALER_EC_TRACK_TRANSFER_WIRE_FEE_INCONSISTENT);
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  }
+  return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
+}
+
+
+/**
+ * Free data structure reachable from @a ctx, but not @a ctx itself.
+ *
+ * @param ctx context to free
+ */
+static void
+free_ctx (struct WtidTransactionContext *ctx)
+{
+  struct TEH_TrackTransferDetail *wdd;
+
+  while (NULL != (wdd = ctx->wdd_head))
+  {
+    GNUNET_CONTAINER_DLL_remove (ctx->wdd_head,
+                                 ctx->wdd_tail,
+                                 wdd);
+    GNUNET_free (wdd);
+  }
+  GNUNET_free_non_null (ctx->wire_method);
+}
+
+
+/**
+ * Handle a "/track/transfer" request.
+ *
+ * @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_TRACKING_handler_track_transfer (struct TEH_RequestHandler *rh,
+                                     struct MHD_Connection *connection,
+                                     void **connection_cls,
+                                     const char *upload_data,
+                                     size_t *upload_data_size)
+{
+  struct WtidTransactionContext ctx;
+  int res;
+  int mhd_ret;
+
+  memset (&ctx, 0, sizeof (ctx));
+  res = TEH_PARSE_mhd_request_arg_data (connection,
+                                        "wtid",
+                                        &ctx.wtid,
+                                        sizeof (struct 
TALER_WireTransferIdentifierRawP));
+  if (GNUNET_SYSERR == res)
+    return MHD_NO; /* internal error */
+  if (GNUNET_NO == res)
+    return MHD_YES; /* parse error */
+  if (GNUNET_OK !=
+      TEH_DB_run_transaction (connection,
+                             &mhd_ret,
+                             &track_transfer_transaction,
+                             &ctx))
+  {
+    free_ctx (&ctx);
+    return mhd_ret;
+  }
+  mhd_ret = reply_track_transfer_details (connection,
+                                         &ctx.total,
+                                         &ctx.merchant_pub,
+                                         &ctx.h_wire,
+                                         &ctx.wire_fee,
+                                         ctx.exec_time,
+                                         ctx.wdd_head);
+  free_ctx (&ctx);
+  return mhd_ret;
+}
+
+
+/* end of taler-exchange-httpd_track_transfer.c */
diff --git a/src/exchange/taler-exchange-httpd_tracking.h 
b/src/exchange/taler-exchange-httpd_track_transfer.h
similarity index 64%
rename from src/exchange/taler-exchange-httpd_tracking.h
rename to src/exchange/taler-exchange-httpd_track_transfer.h
index fe22304..2242b25 100644
--- a/src/exchange/taler-exchange-httpd_tracking.h
+++ b/src/exchange/taler-exchange-httpd_track_transfer.h
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  Copyright (C) 2014, 2015 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,12 +14,12 @@
   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
 */
 /**
- * @file taler-exchange-httpd_tracking.h
+ * @file taler-exchange-httpd_track_transfer.h
  * @brief Handle wire transfer tracking-related requests
  * @author Christian Grothoff
  */
-#ifndef TALER_EXCHANGE_HTTPD_TRACKING_H
-#define TALER_EXCHANGE_HTTPD_TRACKING_H
+#ifndef TALER_EXCHANGE_HTTPD_TRACK_TRANSFER_H
+#define TALER_EXCHANGE_HTTPD_TRACK_TRANSFER_H
 
 #include <gnunet/gnunet_util_lib.h>
 #include <microhttpd.h>
@@ -43,23 +43,4 @@ TEH_TRACKING_handler_track_transfer (struct 
TEH_RequestHandler *rh,
                                      const char *upload_data,
                                      size_t *upload_data_size);
 
-
-/**
- * Handle a "/track/transaction" request.
- *
- * @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_TRACKING_handler_track_transaction (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_tracking.c 
b/src/exchange/taler-exchange-httpd_tracking.c
deleted file mode 100644
index a973549..0000000
--- a/src/exchange/taler-exchange-httpd_tracking.c
+++ /dev/null
@@ -1,159 +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_tracking.c
- * @brief Handle wire transfer tracking-related requests
- * @author Christian Grothoff
- */
-#include "platform.h"
-#include <gnunet/gnunet_util_lib.h>
-#include <jansson.h>
-#include <microhttpd.h>
-#include <pthread.h>
-#include "taler_signatures.h"
-#include "taler-exchange-httpd_parsing.h"
-#include "taler-exchange-httpd_tracking.h"
-#include "taler-exchange-httpd_responses.h"
-
-
-/**
- * Handle a "/track/transfer" request.
- *
- * @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_TRACKING_handler_track_transfer (struct TEH_RequestHandler *rh,
-                                     struct MHD_Connection *connection,
-                                     void **connection_cls,
-                                     const char *upload_data,
-                                     size_t *upload_data_size)
-{
-  struct TALER_WireTransferIdentifierRawP wtid;
-  int res;
-
-  res = TEH_PARSE_mhd_request_arg_data (connection,
-                                        "wtid",
-                                        &wtid,
-                                        sizeof (struct 
TALER_WireTransferIdentifierRawP));
-  if (GNUNET_SYSERR == res)
-    return MHD_NO; /* internal error */
-  if (GNUNET_NO == res)
-    return MHD_YES; /* parse error */
-  return TEH_DB_execute_track_transfer (connection,
-                                        &wtid);
-}
-
-
-/**
- * Check the merchant signature, and if it is valid,
- * return the wire transfer identifier.
- *
- * @param connection the MHD connection to handle
- * @param tps signed request to execute
- * @param merchant_pub public key from the merchant
- * @param merchant_sig signature from the merchant (to be checked)
- * @return MHD result code
- */
-static int
-check_and_handle_track_transaction_request (struct MHD_Connection *connection,
-                                            const struct TALER_DepositTrackPS 
*tps,
-                                            struct TALER_MerchantPublicKeyP 
*merchant_pub,
-                                            struct TALER_MerchantSignatureP 
*merchant_sig)
-{
-  if (GNUNET_OK !=
-      GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MERCHANT_TRACK_TRANSACTION,
-                                 &tps->purpose,
-                                 &merchant_sig->eddsa_sig,
-                                 &merchant_pub->eddsa_pub))
-  {
-    GNUNET_break_op (0);
-    return TEH_RESPONSE_reply_signature_invalid (connection,
-                                                
TALER_EC_TRACK_TRANSACTION_MERCHANT_SIGNATURE_INVALID,
-                                                "merchant_sig");
-  }
-  return TEH_DB_execute_track_transaction (connection,
-                                           &tps->h_contract_terms,
-                                           &tps->h_wire,
-                                           &tps->coin_pub,
-                                           merchant_pub);
-}
-
-
-/**
- * Handle a "/track/transaction" request.
- *
- * @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_TRACKING_handler_track_transaction (struct TEH_RequestHandler *rh,
-                                        struct MHD_Connection *connection,
-                                        void **connection_cls,
-                                        const char *upload_data,
-                                        size_t *upload_data_size)
-{
-  int res;
-  json_t *json;
-  struct TALER_DepositTrackPS tps;
-  struct TALER_MerchantSignatureP merchant_sig;
-  struct GNUNET_JSON_Specification spec[] = {
-    GNUNET_JSON_spec_fixed_auto ("H_wire", &tps.h_wire),
-    GNUNET_JSON_spec_fixed_auto ("h_contract_terms", &tps.h_contract_terms),
-    GNUNET_JSON_spec_fixed_auto ("coin_pub", &tps.coin_pub),
-    GNUNET_JSON_spec_fixed_auto ("merchant_pub", &tps.merchant),
-    GNUNET_JSON_spec_fixed_auto ("merchant_sig", &merchant_sig),
-    GNUNET_JSON_spec_end ()
-  };
-
-  res = TEH_PARSE_post_json (connection,
-                             connection_cls,
-                             upload_data,
-                             upload_data_size,
-                             &json);
-  if (GNUNET_SYSERR == res)
-    return MHD_NO;
-  if ( (GNUNET_NO == res) || (NULL == json) )
-    return MHD_YES;
-  res = TEH_PARSE_json_data (connection,
-                             json,
-                             spec);
-  if (GNUNET_OK != res)
-  {
-    json_decref (json);
-    return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
-  }
-  tps.purpose.size = htonl (sizeof (struct TALER_DepositTrackPS));
-  tps.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_TRACK_TRANSACTION);
-  res = check_and_handle_track_transaction_request (connection,
-                                                    &tps,
-                                                    &tps.merchant,
-                                                    &merchant_sig);
-  GNUNET_JSON_parse_free (spec);
-  json_decref (json);
-  return res;
-}
-
-
-/* end of taler-exchange-httpd_tracking.c */
diff --git a/src/exchange/taler-exchange-wirewatch.c 
b/src/exchange/taler-exchange-wirewatch.c
index aee3083..312f8ac 100644
--- a/src/exchange/taler-exchange-wirewatch.c
+++ b/src/exchange/taler-exchange-wirewatch.c
@@ -95,6 +95,11 @@ static char *type;
 static int delay;
 
 /**
+ * Are we run in testing mode and should only do one pass?
+ */
+static int test_mode;
+
+/**
  * Next task to run, if any.
  */
 static struct GNUNET_SCHEDULER_Task *task;
@@ -218,22 +223,28 @@ history_cb (void *cls,
            const struct TALER_WIRE_TransferDetails *details)
 {
   struct TALER_EXCHANGEDB_Session *session = cls;
-  int ret;
+  enum GNUNET_DB_QueryStatus qs;
 
   if (TALER_BANK_DIRECTION_NONE == dir)
   {
     hh = NULL;
 
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
                 "End of list. Committing progress!\n");
-    ret = db_plugin->commit (db_plugin->cls,
-                            session);
-    if (GNUNET_OK == ret)
+    qs = db_plugin->commit (db_plugin->cls,
+                           session);
+    if (0 <= qs)
     {
       GNUNET_free_non_null (start_off);
       start_off = last_row_off;
       start_off_size = last_row_off_size;
     }
+    if ( (GNUNET_YES == delay) &&
+         (test_mode) )
+    {
+      GNUNET_SCHEDULER_shutdown ();
+      return GNUNET_OK;
+    }
     if (GNUNET_YES == delay)
       task = GNUNET_SCHEDULER_add_delayed (DELAY,
                                           &find_transfers,
@@ -243,22 +254,29 @@ history_cb (void *cls,
                                       NULL);
     return GNUNET_OK; /* will be ignored anyway */
   }
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
               "Adding wire transfer over %s with subject `%s'\n",
               TALER_amount2s (&details->amount),
               TALER_B2S (&details->reserve_pub));
-  ret = db_plugin->reserves_in_insert (db_plugin->cls,
-                                      session,
-                                      &details->reserve_pub,
-                                      &details->amount,
-                                      details->execution_date,
-                                      details->account_details,
-                                       row_off,
-                                       row_off_size);
-  if (GNUNET_OK != ret)
+  qs = db_plugin->reserves_in_insert (db_plugin->cls,
+                                     session,
+                                     &details->reserve_pub,
+                                     &details->amount,
+                                     details->execution_date,
+                                     details->account_details,
+                                     row_off,
+                                     row_off_size);
+  if (GNUNET_DB_STATUS_HARD_ERROR == qs)
   {
     GNUNET_break (0);
     db_plugin->rollback (db_plugin->cls,
+                        session);
+    GNUNET_SCHEDULER_shutdown ();
+    return GNUNET_SYSERR;
+  }
+  if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+  {
+    db_plugin->rollback (db_plugin->cls,
                          session);
     /* try again */
     task = GNUNET_SCHEDULER_add_now (&find_transfers,
@@ -287,9 +305,10 @@ static void
 find_transfers (void *cls)
 {
   struct TALER_EXCHANGEDB_Session *session;
-  int ret;
+  enum GNUNET_DB_QueryStatus qs;
 
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+  task = NULL;
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
               "Checking for incoming wire transfers\n");
 
   if (NULL == (session = db_plugin->get_session (db_plugin->cls)))
@@ -310,11 +329,11 @@ find_transfers (void *cls)
     GNUNET_SCHEDULER_shutdown ();
     return;
   }
-  ret = db_plugin->get_latest_reserve_in_reference (db_plugin->cls,
-                                                    session,
-                                                    &start_off,
-                                                    &start_off_size);
-  if (GNUNET_SYSERR == ret)
+  qs = db_plugin->get_latest_reserve_in_reference (db_plugin->cls,
+                                                  session,
+                                                  &start_off,
+                                                  &start_off_size);
+  if (GNUNET_DB_STATUS_HARD_ERROR == qs)
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                 "Failed to obtain starting point for montoring from 
database!\n");
@@ -322,6 +341,15 @@ find_transfers (void *cls)
     GNUNET_SCHEDULER_shutdown ();
     return;
   }
+  if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+  {
+    /* try again */
+    db_plugin->rollback (db_plugin->cls,
+                         session);
+    task = GNUNET_SCHEDULER_add_now (&find_transfers,
+                                    NULL);
+    return;
+  }
   delay = GNUNET_YES;
   hh = wire_plugin->get_history (wire_plugin->cls,
                                 TALER_BANK_DIRECTION_CREDIT,
@@ -389,7 +417,10 @@ main (int argc,
                                 "PLUGINNAME",
                                 "which wire plugin to use",
                                 &type),
-    GNUNET_GETOPT_option_version (VERSION "-" VCS_VERSION),
+    GNUNET_GETOPT_option_flag ('T',
+                              "test",
+                              "run in test mode and exit when idle",
+                              &test_mode),
     GNUNET_GETOPT_OPTION_END
   };
 
diff --git a/src/exchange/test-taler-exchange-aggregator-postgres.conf 
b/src/exchange/test-taler-exchange-wirewatch-postgres.conf
similarity index 94%
copy from src/exchange/test-taler-exchange-aggregator-postgres.conf
copy to src/exchange/test-taler-exchange-wirewatch-postgres.conf
index f609c0a..2e846d8 100644
--- a/src/exchange/test-taler-exchange-aggregator-postgres.conf
+++ b/src/exchange/test-taler-exchange-wirewatch-postgres.conf
@@ -26,7 +26,9 @@ BASE_URL = "https://exchange.taler.net/";
 # as there is no way for wallets to query this value.  Thus,
 # it is only configurable for testing, and should be treated
 # as constant in production.
-IDLE_RESERVE_EXPIRATION_TIME = 4 weeks
+#
+# This is THE test that requires a short reserve expiration time!
+IDLE_RESERVE_EXPIRATION_TIME = 5 s
 
 [exchangedb-postgres]
 
diff --git a/src/exchange/test_taler_exchange_aggregator.c 
b/src/exchange/test_taler_exchange_aggregator.c
index 57cb9d3..7f9ea41 100644
--- a/src/exchange/test_taler_exchange_aggregator.c
+++ b/src/exchange/test_taler_exchange_aggregator.c
@@ -118,7 +118,7 @@ struct Command
       /**
        * Subject of the transfer, set by the command.
        */
-      struct TALER_WireTransferIdentifierRawP wtid;
+      char *subject;
 
     } expect_transaction;
 
@@ -450,7 +450,7 @@ do_deposit (struct Command *cmd)
         plugin->insert_deposit (plugin->cls,
                                 session,
                                 &deposit)) ||
-       (GNUNET_OK !=
+       (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
         plugin->commit (plugin->cls,
                         session)) )
     ret = GNUNET_SYSERR;
@@ -565,7 +565,7 @@ interpreter (void *cls)
                                   
cmd->details.expect_transaction.debit_account,
                                   
cmd->details.expect_transaction.credit_account,
                                   
cmd->details.expect_transaction.exchange_base_url,
-                                  &cmd->details.expect_transaction.wtid))
+                                  &cmd->details.expect_transaction.subject))
         {
           fail (cmd);
           return;
@@ -1143,12 +1143,12 @@ run (void *cls)
   if ( (GNUNET_OK !=
         plugin->start (plugin->cls,
                        session)) ||
-       (GNUNET_OK !=
+       (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
         plugin->insert_denomination_info (plugin->cls,
                                           session,
                                           &dpk,
                                           &issue)) ||
-       (GNUNET_OK !=
+       (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
         plugin->commit (plugin->cls,
                         session)) )
     {
diff --git a/src/exchange/test_taler_exchange_wirewatch.c 
b/src/exchange/test_taler_exchange_wirewatch.c
new file mode 100644
index 0000000..69502d9
--- /dev/null
+++ b/src/exchange/test_taler_exchange_wirewatch.c
@@ -0,0 +1,848 @@
+/*
+  This file is part of TALER
+  (C) 2016, 2017 Inria and GNUnet e.V.
+
+  TALER is free software; you can redistribute it and/or modify it under the
+  terms of the GNU 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 General Public License for more details.
+
+  You should have received a copy of the GNU General Public License along with
+  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file exchange/test_taler_exchange_wirewatch.c
+ * @brief Tests for taler-exchange-wirewatch and taler-exchange-aggregator 
logic;
+ *        Performs an invalid wire transfer to the exchange, and then checks 
that
+ *        wirewatch immediately sends the money back.
+ *        Then performs a valid wire transfer, waits for the reserve to expire,
+ *        and then checks that the aggregator sends the money back.
+ * @author Christian Grothoff <address@hidden>
+ */
+#include "platform.h"
+#include "taler_util.h"
+#include <gnunet/gnunet_json_lib.h>
+#include "taler_json_lib.h"
+#include <microhttpd.h>
+#include "taler_fakebank_lib.h"
+
+
+/**
+ * Commands for the interpreter.
+ */
+enum OpCode {
+
+  /**
+   * Terminate testcase with 'skipped' result.
+   */
+  OPCODE_TERMINATE_SKIP,
+
+  /**
+   * Run taler-exchange-aggregator.
+   */
+  OPCODE_RUN_AGGREGATOR,
+
+  /**
+   * Expect that we have exhaustively gone over all transactions.
+   */
+  OPCODE_RUN_WIREWATCH,
+
+  /**
+   * Send money from bank to exchange.
+   */
+  OPCODE_RUN_TRANSFER,
+
+  /**
+   * Wait a certain amount of time.
+   */
+  OPCODE_WAIT,
+
+  /**
+   * Expect that we have received the specified transfer.
+   */
+  OPCODE_EXPECT_TRANSFER,
+
+  /**
+   * Expect that we have 'expected' all wire transfers.
+   */
+  OPCODE_EXPECT_TRANSFERS_EMPTY,
+
+  /**
+   * Finish testcase with success.
+   */
+  OPCODE_TERMINATE_SUCCESS
+};
+
+
+/**
+ * Command state for the interpreter.
+ */
+struct Command
+{
+
+  /**
+   * What instruction should we run?
+   */
+  enum OpCode opcode;
+
+  /**
+   * Human-readable label for the command.
+   */
+  const char *label;
+
+  union {
+
+    /**
+     * If @e opcode is #OPCODE_EXPECT_TRANSFER, this
+     * specifies which transaction we expected.  Note that
+     * the WTID will be set, not checked!
+     */
+    struct {
+
+      /**
+       * Amount to be transferred.
+       */
+      const char *amount;
+
+      /**
+       * Account to debit.
+       */
+      uint64_t debit_account;
+
+      /**
+       * Account to credit.
+       */
+      uint64_t credit_account;
+
+      /**
+       * Expected base URL for the exchange.
+       */
+      const char *exchange_base_url;
+
+      /**
+       * Subject of the transfer, set by the command.
+       */
+      char *subject;
+
+    } expect_transfer;
+
+
+    /**
+     * If @e opcode is #OPCODE_RUN_TRANSFER, this
+     * specifies which transaction the bank should do.
+     */
+    struct {
+
+      /**
+       * Amount to be transferred.
+       */
+      const char *amount;
+
+      /**
+       * Account to debit.
+       */
+      uint64_t debit_account;
+
+      /**
+       * Account to credit.
+       */
+      uint64_t credit_account;
+
+      /**
+       * Subject of the transfer, set by the command.
+       */
+      const char *subject;
+
+      /**
+       * Serial ID of the wire transfer as assigned by the bank.
+       */
+      uint64_t serial_id;
+
+    } run_transfer;
+
+    struct {
+
+      /**
+       * The handle for the aggregator process that we are testing.
+       */
+      struct GNUNET_OS_Process *aggregator_proc;
+
+      /**
+       * ID of task called whenever we get a SIGCHILD.
+       */
+      struct GNUNET_SCHEDULER_Task *child_death_task;
+
+    } aggregator;
+
+    struct {
+
+      /**
+       * The handle for the wirewatch process that we are testing.
+       */
+      struct GNUNET_OS_Process *wirewatch_proc;
+
+      /**
+       * ID of task called whenever we get a SIGCHILD.
+       */
+      struct GNUNET_SCHEDULER_Task *child_death_task;
+
+    } wirewatch;
+
+    /**
+     * How long should we wait if the opcode is #OPCODE_WAIT.
+     */
+    struct GNUNET_TIME_Relative wait_delay;
+
+  } details;
+
+};
+
+
+/**
+ * State of the interpreter.
+ */
+struct State
+{
+  /**
+   * Array of commands to run.
+   */
+  struct Command* commands;
+
+  /**
+   * Offset of the next command to be run.
+   */
+  unsigned int ioff;
+};
+
+
+/**
+ * Pipe used to communicate child death via signal.
+ */
+static struct GNUNET_DISK_PipeHandle *sigpipe;
+
+/**
+ * ID of task called whenever we time out.
+ */
+static struct GNUNET_SCHEDULER_Task *timeout_task;
+
+/**
+ * Return value from main().
+ */
+static int result;
+
+/**
+ * Name of the configuration file to use.
+ */
+static char *config_filename;
+
+/**
+ * Task running the interpreter().
+ */
+static struct GNUNET_SCHEDULER_Task *int_task;
+
+/**
+ * Handle for our fake bank.
+ */
+static struct TALER_FAKEBANK_Handle *fb;
+
+
+/**
+ * Interprets the commands from the test program.
+ *
+ * @param cls the `struct State` of the interpreter
+ */
+static void
+interpreter (void *cls);
+
+
+/**
+ * Advance the IP and run the next command.
+ *
+ * @param state interpreter to advance.
+ */
+static void
+next_command (struct State *state)
+{
+  GNUNET_assert (NULL == int_task);
+  state->ioff++;
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Advancing to command %s\n",
+              state->commands[state->ioff].label);
+  int_task = GNUNET_SCHEDULER_add_now (&interpreter,
+                                       state);
+}
+
+
+/**
+ * Fail the testcase at the current command.
+ */
+static void
+fail (struct Command *cmd)
+{
+  GNUNET_assert (NULL == int_task);
+  fprintf (stderr,
+           "Testcase failed at command `%s'\n",
+           cmd->label);
+  result = 2;
+  GNUNET_SCHEDULER_shutdown ();
+}
+
+
+/**
+ * Task triggered whenever we are to shutdown.
+ *
+ * @param cls closure, NULL if we need to self-restart
+ */
+static void
+timeout_action (void *cls)
+{
+  timeout_task = NULL;
+  GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+              "Test failed: timeout\n");
+  result = 2;
+  GNUNET_SCHEDULER_shutdown ();
+}
+
+
+/**
+ * Task triggered whenever we are to shutdown.
+ *
+ * @param cls our `struct State`
+ */
+static void
+shutdown_action (void *cls)
+{
+  struct State *state = cls;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Running shutdown\n");
+  if (NULL != timeout_task)
+  {
+    GNUNET_SCHEDULER_cancel (timeout_task);
+    timeout_task = NULL;
+  }
+  if (NULL != int_task)
+  {
+    GNUNET_SCHEDULER_cancel (int_task);
+    int_task = NULL;
+  }
+  if (NULL != fb)
+  {
+    TALER_FAKEBANK_stop (fb);
+    fb = NULL;
+  }
+  for (unsigned int i=0;i<=state->ioff;i++)
+  {
+    struct Command *cmd = &state->commands[i];
+
+    switch (cmd->opcode)
+    {
+    case OPCODE_TERMINATE_SKIP:
+      break;
+    case OPCODE_RUN_AGGREGATOR:
+      if (NULL != cmd->details.aggregator.child_death_task)
+      {
+        GNUNET_SCHEDULER_cancel (cmd->details.aggregator.child_death_task);
+        cmd->details.aggregator.child_death_task = NULL;
+      }
+      if (NULL != cmd->details.aggregator.aggregator_proc)
+      {
+        GNUNET_break (0 == GNUNET_OS_process_kill 
(cmd->details.aggregator.aggregator_proc,
+                                                   SIGKILL));
+        GNUNET_OS_process_wait (cmd->details.aggregator.aggregator_proc);
+        GNUNET_OS_process_destroy (cmd->details.aggregator.aggregator_proc);
+        cmd->details.aggregator.aggregator_proc = NULL;
+      }
+      break;
+    case OPCODE_RUN_WIREWATCH:
+      if (NULL != cmd->details.wirewatch.child_death_task)
+      {
+        GNUNET_SCHEDULER_cancel (cmd->details.wirewatch.child_death_task);
+        cmd->details.wirewatch.child_death_task = NULL;
+      }
+      if (NULL != cmd->details.wirewatch.wirewatch_proc)
+      {
+        GNUNET_break (0 == GNUNET_OS_process_kill 
(cmd->details.wirewatch.wirewatch_proc,
+                                                   SIGKILL));
+        GNUNET_OS_process_wait (cmd->details.wirewatch.wirewatch_proc);
+        GNUNET_OS_process_destroy (cmd->details.wirewatch.wirewatch_proc);
+        cmd->details.wirewatch.wirewatch_proc = NULL;
+      }
+      break;
+    case OPCODE_RUN_TRANSFER:
+      break;
+    case OPCODE_WAIT:
+      break;
+    case OPCODE_EXPECT_TRANSFER:
+      GNUNET_free_non_null (cmd->details.expect_transfer.subject);
+      cmd->details.expect_transfer.subject = NULL;
+      break;
+    case OPCODE_EXPECT_TRANSFERS_EMPTY:
+      break;
+    case OPCODE_TERMINATE_SUCCESS:
+      break;
+    }
+  }
+}
+
+
+/**
+ * Task triggered whenever we receive a SIGCHLD (child
+ * process died).
+ *
+ * @param cls our `struct State`
+ */
+static void
+maint_child_death (void *cls)
+{
+  struct State *state = cls;
+  const struct GNUNET_DISK_FileHandle *pr;
+  struct Command *cmd = &state->commands[state->ioff];
+  char c[16];
+
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Child process died for command %s\n",
+              cmd->label);
+  pr = GNUNET_DISK_pipe_handle (sigpipe,
+                                GNUNET_DISK_PIPE_END_READ);
+  GNUNET_break (0 < GNUNET_DISK_file_read (pr,
+                                           &c,
+                                           sizeof (c)));
+  switch (cmd->opcode)
+  {
+  case OPCODE_RUN_AGGREGATOR:
+    GNUNET_assert (NULL != cmd->details.aggregator.child_death_task);
+    cmd->details.aggregator.child_death_task = NULL;
+    GNUNET_OS_process_wait (cmd->details.aggregator.aggregator_proc);
+    GNUNET_OS_process_destroy (cmd->details.aggregator.aggregator_proc);
+    cmd->details.aggregator.aggregator_proc = NULL;
+    break;
+  case OPCODE_RUN_WIREWATCH:
+    GNUNET_assert (NULL != cmd->details.wirewatch.child_death_task);
+    cmd->details.wirewatch.child_death_task = NULL;
+    GNUNET_OS_process_wait (cmd->details.wirewatch.wirewatch_proc);
+    GNUNET_OS_process_destroy (cmd->details.wirewatch.wirewatch_proc);
+    cmd->details.wirewatch.wirewatch_proc = NULL;
+    break;
+  default:
+    fail (cmd);
+    return;
+  }
+  next_command (state);
+}
+
+
+/**
+ * Interprets the commands from the test program.
+ *
+ * @param cls the `struct State` of the interpreter
+ */
+static void
+interpreter (void *cls)
+{
+  struct State *state = cls;
+  struct Command *cmd = &state->commands[state->ioff];
+
+  GNUNET_assert (NULL != int_task);
+  int_task = NULL;
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Running command %u (%s)\n",
+              state->ioff,
+              cmd->label);
+  switch (cmd->opcode)
+  {
+  case OPCODE_TERMINATE_SKIP:
+    /* return skip: test not finished, but did not fail either */
+    result = 77;
+    GNUNET_SCHEDULER_shutdown ();
+    return;
+  case OPCODE_RUN_AGGREGATOR:
+    cmd->details.aggregator.child_death_task =
+      GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
+                                      GNUNET_DISK_pipe_handle (sigpipe,
+                                                               
GNUNET_DISK_PIPE_END_READ),
+                                      &maint_child_death,
+                                      state);
+    cmd->details.aggregator.aggregator_proc
+      = GNUNET_OS_start_process (GNUNET_NO,
+                                 GNUNET_OS_INHERIT_STD_ALL,
+                                 NULL, NULL, NULL,
+                                 "taler-exchange-aggregator",
+                                 "taler-exchange-aggregator",
+                                 "-c", config_filename,
+                                 "-t", /* enable temporary tables */
+                                 NULL);
+    if (NULL == cmd->details.aggregator.aggregator_proc)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "Failed to start taler-exchange-aggregator. Check $PATH.\n");
+      GNUNET_break (0);
+      fail (cmd);
+      return;
+    }
+    return;
+  case OPCODE_RUN_WIREWATCH:
+    cmd->details.wirewatch.child_death_task =
+      GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
+                                      GNUNET_DISK_pipe_handle (sigpipe,
+                                                               
GNUNET_DISK_PIPE_END_READ),
+                                      &maint_child_death,
+                                      state);
+    cmd->details.wirewatch.wirewatch_proc
+      = GNUNET_OS_start_process (GNUNET_NO,
+                                 GNUNET_OS_INHERIT_STD_ALL,
+                                 NULL, NULL, NULL,
+                                 "taler-exchange-wirewatch",
+                                 "taler-exchange-wirewatch",
+                                 "-c", config_filename,
+                                 "-t", "test",
+                                 "-T", /* run in test mode, exit instead of 
looping */
+                                 NULL);
+    if (NULL == cmd->details.wirewatch.wirewatch_proc)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "Failed to start taler-exchange-wirewatch. Check $PATH.\n");
+      GNUNET_break (0);
+      fail (cmd);
+      return;
+    }
+    return;
+  case OPCODE_RUN_TRANSFER:
+    {
+      struct TALER_Amount amount;
+
+      if (GNUNET_OK !=
+          TALER_string_to_amount (cmd->details.run_transfer.amount,
+                                  &amount))
+      {
+        GNUNET_break (0);
+        fail (cmd);
+        return;
+      }
+      GNUNET_assert (NULL != cmd->details.run_transfer.subject);
+      cmd->details.run_transfer.serial_id
+        = TALER_FAKEBANK_make_transfer (fb,
+                                        
cmd->details.run_transfer.debit_account,
+                                        
cmd->details.run_transfer.credit_account,
+                                        &amount,
+                                        cmd->details.run_transfer.subject,
+                                        "https://exchange.taler.net/";);
+      next_command (state);
+      return;
+    }
+  case OPCODE_WAIT:
+    state->ioff++;
+    GNUNET_assert (NULL == int_task);
+    int_task = GNUNET_SCHEDULER_add_delayed (cmd->details.wait_delay,
+                                             &interpreter,
+                                             state);
+    return;
+  case OPCODE_EXPECT_TRANSFER:
+    {
+      struct TALER_Amount want_amount;
+
+      if (GNUNET_OK !=
+          TALER_string_to_amount (cmd->details.expect_transfer.amount,
+                                  &want_amount))
+      {
+        GNUNET_break (0);
+        fail (cmd);
+        return;
+      }
+      if (GNUNET_OK !=
+          TALER_FAKEBANK_check (fb,
+                                &want_amount,
+                                cmd->details.expect_transfer.debit_account,
+                                cmd->details.expect_transfer.credit_account,
+                                cmd->details.expect_transfer.exchange_base_url,
+                                &cmd->details.expect_transfer.subject))
+      {
+        fail (cmd);
+        return;
+      }
+      next_command (state);
+      return;
+    }
+  case OPCODE_EXPECT_TRANSFERS_EMPTY:
+    if (GNUNET_OK != TALER_FAKEBANK_check_empty (fb))
+    {
+      fail (cmd);
+      return;
+    }
+    next_command (state);
+    return;
+  case OPCODE_TERMINATE_SUCCESS:
+    result = 0;
+    GNUNET_SCHEDULER_shutdown ();
+    return;
+  }
+}
+
+
+/**
+ * Main function that will be run by the scheduler.
+ *
+ * @param cls closure with configuration
+ */
+static void
+run (void *cls)
+{
+  static struct Command commands[] = {
+    /* test running with empty DB */
+    {
+      .opcode = OPCODE_EXPECT_TRANSFERS_EMPTY,
+      .label = "expect-empty-transactions-on-start"
+    },
+    {
+      .opcode = OPCODE_RUN_AGGREGATOR,
+      .label = "run-aggregator-on-empty"
+    },
+    {
+      .opcode = OPCODE_RUN_WIREWATCH,
+      .label = "run-wirewatch-on-empty"
+    },
+    {
+      .opcode = OPCODE_EXPECT_TRANSFERS_EMPTY,
+      .label = "expect-empty-transactions-after-dry-run"
+    },
+    /* fill exchange's reserve at bank */
+    {
+      .opcode = OPCODE_RUN_TRANSFER,
+      .label = "run-transfer-good-to-exchange",
+      .details.run_transfer.debit_account = 4,
+      .details.run_transfer.credit_account = 3,
+      .details.run_transfer.subject = 
"SRB8VQHNTNJWSSG7BXT24Z063ZSXN7T0MHCQCBAFC1V17BZH10D0",
+      .details.run_transfer.amount = "EUR:5.00"
+    },
+    /* creates reserve */
+    {
+      .opcode = OPCODE_RUN_WIREWATCH,
+      .label = "run-wirewatch-on-good-transfer"
+    },
+    /* clear first transfer from DLL */
+    {
+      .opcode = OPCODE_EXPECT_TRANSFER,
+      .label = "clear-good-transfer-to-exchange",
+      .details.expect_transfer.debit_account = 4,
+      .details.expect_transfer.credit_account = 3,
+      .details.expect_transfer.exchange_base_url = 
"https://exchange.taler.net/";,
+      .details.expect_transfer.amount = "EUR:5.00"
+    },
+    /* should do NOTHING, it is too early... */
+    {
+      .opcode = OPCODE_RUN_AGGREGATOR,
+      .label = "run-aggregator-non-expired-reserve"
+    },
+    /* check nothing happened */
+    {
+      .opcode = OPCODE_EXPECT_TRANSFERS_EMPTY,
+      .label = "expect-empty-transactions-1"
+    },
+    /* Configuration says reserves expire after 5s! */
+    {
+      .opcode = OPCODE_WAIT,
+      .label = "wait (5s)",
+      .details.wait_delay = { 1000LL * 1000 * 6 } /* 6s */
+    },
+    /* This time the reserve expired, so the money should go back... */
+    {
+      .opcode = OPCODE_RUN_AGGREGATOR,
+      .label = "run-aggregator-non-expired-reserve"
+    },
+    /* Check exchange sent money back, minus closing fee of EUR:0.01  */
+    {
+      .opcode = OPCODE_EXPECT_TRANSFER,
+      .label = "check-reserve-expiration-transfer",
+      .details.expect_transfer.debit_account = 3,
+      .details.expect_transfer.credit_account = 4,
+      .details.expect_transfer.exchange_base_url = 
"https://exchange.taler.net/";,
+      .details.expect_transfer.amount = "EUR:4.99"
+    },
+    /* check nothing else happened */
+    {
+      .opcode = OPCODE_EXPECT_TRANSFERS_EMPTY,
+      .label = "expect-empty-transactions-1"
+    },
+    /* This cannot work unless #5077 is implemented. */
+#if TEST_5077
+    {
+      .opcode = OPCODE_RUN_TRANSFER,
+      .label = "run-transfer-bad-to-exchange",
+      .details.run_transfer.debit_account = 4,
+      .details.run_transfer.credit_account = 3,
+      .details.run_transfer.subject = "random junk",
+      .details.run_transfer.amount = "EUR:5.00"
+    },
+    {
+      .opcode = OPCODE_RUN_WIREWATCH,
+      .label = "run-wirewatch-on-bad-transfer"
+    },
+    {
+      .opcode = OPCODE_EXPECT_TRANSFER,
+      .label = "expect-bad-transfer-to-exchange",
+      .details.expect_transfer.debit_account = 4,
+      .details.expect_transfer.credit_account = 3,
+      .details.expect_transfer.exchange_base_url = 
"https://exchange.taler.net/";,
+      .details.expect_transfer.amount = "EUR:5.00"
+    },
+    {
+      .opcode = OPCODE_EXPECT_TRANSFER,
+      .label = "expect-rewire-transfer-from-exchange",
+      .details.expect_transfer.debit_account = 3,
+      .details.expect_transfer.credit_account = 4,
+      .details.expect_transfer.exchange_base_url = 
"https://exchange.taler.net/";,
+      .details.expect_transfer.amount = "EUR:5.00"
+    },
+    {
+      .opcode = OPCODE_EXPECT_TRANSFERS_EMPTY,
+      .label = "expect-empty-transactions-1"
+    },
+#endif
+
+    {
+      .opcode = OPCODE_TERMINATE_SUCCESS,
+      .label = "testcase-complete-terminating-with-success"
+    }
+  };
+  static struct State state = {
+    .commands = commands
+  };
+
+  GNUNET_SCHEDULER_add_shutdown (&shutdown_action,
+                                 &state);
+  timeout_task = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES,
+                                               &timeout_action,
+                                               &state);
+  result = 1; /* test failed for undefined reason */
+  fb = TALER_FAKEBANK_start (8082);
+  if (NULL == fb)
+  {
+    GNUNET_SCHEDULER_shutdown ();
+    result = 77;
+    return;
+  }
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Launching interpreter\n");
+  int_task = GNUNET_SCHEDULER_add_now (&interpreter,
+                                       &state);
+}
+
+
+/**
+ * Signal handler called for SIGCHLD.  Triggers the
+ * respective handler by writing to the trigger pipe.
+ */
+static void
+sighandler_child_death ()
+{
+  static char c;
+  int old_errno = errno;       /* back-up errno */
+
+  GNUNET_break (1 ==
+               GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle
+                                       (sigpipe, GNUNET_DISK_PIPE_END_WRITE),
+                                       &c, sizeof (c)));
+  errno = old_errno;           /* restore errno */
+}
+
+
+int
+main (int argc,
+      char *const argv[])
+{
+  const char *plugin_name;
+  char *testname;
+  struct GNUNET_OS_Process *proc;
+  struct GNUNET_CONFIGURATION_Handle *cfg;
+  struct GNUNET_SIGNAL_Context *shc_chld;
+
+  result = -1;
+  if (NULL == (plugin_name = strrchr (argv[0], (int) '-')))
+  {
+    GNUNET_break (0);
+    return -1;
+  }
+  plugin_name++;
+  (void) GNUNET_asprintf (&testname,
+                          "test-taler-exchange-wirewatch-%s",
+                          plugin_name);
+  (void) GNUNET_asprintf (&config_filename,
+                          "%s.conf",
+                          testname);
+  /* these might get in the way */
+  unsetenv ("XDG_DATA_HOME");
+  unsetenv ("XDG_CONFIG_HOME");
+  GNUNET_log_setup ("test_taler_exchange_wirewatch",
+                    "WARNING",
+                    NULL);
+  proc = GNUNET_OS_start_process (GNUNET_NO,
+                                  GNUNET_OS_INHERIT_STD_ALL,
+                                  NULL, NULL, NULL,
+                                  "taler-exchange-keyup",
+                                  "taler-exchange-keyup",
+                                  "-c", config_filename,
+                                  NULL);
+  if (NULL == proc)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+               "Failed to run `taler-exchange-keyup`, is your PATH 
correct?\n");
+    return 77;
+  }
+  GNUNET_OS_process_wait (proc);
+  GNUNET_OS_process_destroy (proc);
+  proc = GNUNET_OS_start_process (GNUNET_NO,
+                                  GNUNET_OS_INHERIT_STD_ALL,
+                                  NULL, NULL, NULL,
+                                  "taler-exchange-dbinit",
+                                  "taler-exchange-dbinit",
+                                  "-c", config_filename,
+                                  "-r",
+                                  NULL);
+  if (NULL == proc)
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+               "Failed to run `taler-exchange-keyup`, is your PATH 
correct?\n");
+    return 77;
+  }
+  GNUNET_OS_process_wait (proc);
+  GNUNET_OS_process_destroy (proc);
+  if (GNUNET_OK !=
+      GNUNET_NETWORK_test_port_free (IPPROTO_TCP,
+                                    8082))
+  {
+    fprintf (stderr,
+             "Required port %u not available, skipping.\n",
+            8082);
+    return 77;
+  }
+  cfg = GNUNET_CONFIGURATION_create ();
+  if (GNUNET_OK !=
+      GNUNET_CONFIGURATION_parse (cfg,
+                                  config_filename))
+  {
+    GNUNET_break (0);
+    GNUNET_free (config_filename);
+    GNUNET_free (testname);
+    return 2;
+  }
+  sigpipe = GNUNET_DISK_pipe (GNUNET_NO, GNUNET_NO,
+                              GNUNET_NO, GNUNET_NO);
+  GNUNET_assert (NULL != sigpipe);
+  shc_chld =
+    GNUNET_SIGNAL_handler_install (GNUNET_SIGCHLD,
+                                   &sighandler_child_death);
+  GNUNET_SCHEDULER_run (&run,
+                        cfg);
+  GNUNET_SIGNAL_handler_uninstall (shc_chld);
+  GNUNET_DISK_pipe_close (sigpipe);
+  GNUNET_CONFIGURATION_destroy (cfg);
+  GNUNET_free (config_filename);
+  GNUNET_free (testname);
+  return result;
+}
+
+/* end of test_taler_exchange_wirewatch.c */
diff --git a/src/exchangedb/perf_taler_exchangedb_interpreter.c 
b/src/exchangedb/perf_taler_exchangedb_interpreter.c
index 5e4155c..7ec958c 100644
--- a/src/exchangedb/perf_taler_exchangedb_interpreter.c
+++ b/src/exchangedb/perf_taler_exchangedb_interpreter.c
@@ -1,6 +1,6 @@
 /*
    This file is part of TALER
-   Copyright (C) 2014, 2015 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 General Public License as published by the Free Software
@@ -1292,13 +1292,16 @@ interpret (struct 
PERF_TALER_EXCHANGEDB_interpreter_state *state)
         break;
 
       case PERF_TALER_EXCHANGEDB_CMD_START_TRANSACTION:
-        state->plugin->start (state->plugin->cls, state->session);
+        GNUNET_break (GNUNET_OK ==
+                      state->plugin->start (state->plugin->cls,
+                                            state->session));
         break;
 
       case PERF_TALER_EXCHANGEDB_CMD_COMMIT_TRANSACTION:
-        state->plugin->commit (state->plugin->cls, state->session);
+        GNUNET_break (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS ==
+                      state->plugin->commit (state->plugin->cls,
+                                             state->session));
         break;
-
       case PERF_TALER_EXCHANGEDB_CMD_ABORT_TRANSACTION:
         state->plugin->rollback (state->plugin->cls,
                                  state->session);
@@ -1331,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;
@@ -1347,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;
@@ -1355,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;
 
@@ -1419,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);
@@ -1443,7 +1449,7 @@ interpret (struct PERF_TALER_EXCHANGEDB_interpreter_state 
*state)
       case PERF_TALER_EXCHANGEDB_CMD_INSERT_DENOMINATION:
         {
           unsigned int denom_index;
-          int ret;
+          enum GNUNET_DB_QueryStatus ret;
           struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki ;
 
           denom_index = 
state->cmd[state->i].details.insert_denomination.index_denom;
@@ -1452,23 +1458,23 @@ interpret (struct 
PERF_TALER_EXCHANGEDB_interpreter_state *state)
                                                          state->session,
                                                          &dki->denom_pub,
                                                          &dki->issue);
-          GNUNET_assert (GNUNET_SYSERR != ret);
+          GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == ret);
         }
         break;
 
       case PERF_TALER_EXCHANGEDB_CMD_GET_DENOMINATION:
         {
           unsigned int denom_index;
-          int ret;
+          enum GNUNET_DB_QueryStatus qs;
           struct PERF_TALER_EXCHANGEDB_Data *data;
 
           denom_index = 
state->cmd[state->i].details.get_denomination.index_denom;
           data = &state->cmd[denom_index].exposed;
-          ret = state->plugin->get_denomination_info (state->plugin->cls,
-                                                      state->session,
-                                                      
&data->data.dki->denom_pub,
-                                                      &data->data.dki->issue);
-          GNUNET_assert (GNUNET_SYSERR != ret);
+          qs = state->plugin->get_denomination_info (state->plugin->cls,
+                                                    state->session,
+                                                    &data->data.dki->denom_pub,
+                                                    &data->data.dki->issue);
+          GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs);
         }
         break;
 
@@ -1490,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;
 
@@ -1523,13 +1529,16 @@ interpret (struct 
PERF_TALER_EXCHANGEDB_interpreter_state *state)
           unsigned int coin_index;
           struct PERF_TALER_EXCHANGEDB_Coin *coin;
           struct TALER_EXCHANGEDB_TransactionList *transactions;
+         enum GNUNET_DB_QueryStatus qs;
 
           coin_index = 
state->cmd[state->i].details.get_coin_transaction.index_coin;
           coin = state->cmd[coin_index].exposed.data.coin;
-          transactions = state->plugin->get_coin_transactions 
(state->plugin->cls,
-                                                               state->session,
-                                                               
&coin->public_info.coin_pub);
-          GNUNET_assert (transactions != NULL);
+          qs = state->plugin->get_coin_transactions (state->plugin->cls,
+                                                    state->session,
+                                                    
&coin->public_info.coin_pub,
+                                                    &transactions);
+         GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs);
+         GNUNET_assert (transactions != NULL);
           state->plugin->free_coin_transaction_list (state->plugin->cls,
                                                      transactions);
         }
@@ -1544,10 +1553,11 @@ interpret (struct 
PERF_TALER_EXCHANGEDB_interpreter_state *state)
           refresh_session = PERF_TALER_EXCHANGEDB_refresh_session_init ();
           GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_WEAK,
                                             hash);
-          state->plugin->create_refresh_session (state->session,
-                                                 state->session,
-                                                 hash,
-                                                 refresh_session);
+          GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
+                        state->plugin->create_refresh_session (state->session,
+                                                               state->session,
+                                                               hash,
+                                                               
refresh_session));
           state->cmd[state->i].exposed.data.session_hash = hash;
           PERF_TALER_EXCHANGEDB_refresh_session_free (refresh_session);
           GNUNET_free (refresh_session);
@@ -1580,11 +1590,12 @@ interpret (struct 
PERF_TALER_EXCHANGEDB_interpreter_state *state)
           denom_index = 
state->cmd[state->i].details.insert_refresh_order.index_denom;
           session_hash = state->cmd[hash_index].exposed.data.session_hash;
           denom = state->cmd[denom_index].exposed.data.dki;
-          state->plugin->insert_refresh_order (state->plugin->cls,
-                                               state->session,
-                                               session_hash,
-                                               1,
-                                               &denom->denom_pub);
+          GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
+                        state->plugin->insert_refresh_order 
(state->plugin->cls,
+                                                             state->session,
+                                                             session_hash,
+                                                             1,
+                                                             
&denom->denom_pub));
 
         }
         break;
@@ -1607,18 +1618,18 @@ interpret (struct 
PERF_TALER_EXCHANGEDB_interpreter_state *state)
 
       case PERF_TALER_EXCHANGEDB_CMD_INSERT_REFRESH_COMMIT_COIN:
         {
-          int ret;
+          enum GNUNET_DB_QueryStatus qs;
           unsigned int hash_index;
           struct TALER_EXCHANGEDB_RefreshCommitCoin *refresh_commit;
 
           hash_index = 
state->cmd[state->i].details.insert_refresh_commit_coin.index_hash;
           refresh_commit = PERF_TALER_EXCHANGEDB_refresh_commit_coin_init ();
-          ret = state->plugin->insert_refresh_commit_coins (state->plugin->cls,
-                                                            state->session,
-                                                            
state->cmd[hash_index].exposed.data.session_hash,
-                                                            1,
-                                                            refresh_commit);
-          GNUNET_assert (GNUNET_OK == ret);
+          qs = state->plugin->insert_refresh_commit_coins (state->plugin->cls,
+                                                          state->session,
+                                                          
state->cmd[hash_index].exposed.data.session_hash,
+                                                          1,
+                                                          refresh_commit);
+          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 e251722..8b3fe7f 100644
--- a/src/exchangedb/plugin_exchangedb_postgres.c
+++ b/src/exchangedb/plugin_exchangedb_postgres.c
@@ -30,38 +30,6 @@
 
 #include "plugin_exchangedb_common.c"
 
-/**
- * Error code returned by Postgres for deadlock.
- */
-#define PQ_DIAG_SQLSTATE_DEADLOCK "40P01"
-
-/**
- * Error code returned by Postgres for uniqueness violation.
- */
-#define PQ_DIAG_SQLSTATE_UNIQUE_VIOLATION "23505"
-
-/**
- * Error code returned by Postgres on serialization failure.
- */
-#define PQ_DIAG_SQLSTATE_SERIALIZATION_FAILURE "40001"
-
-
-/**
- * Log a query error.
- *
- * @param result PQ result object of the query that failed
- * @param conn SQL connection that was used
- */
-#define QUERY_ERR(result,conn)                         \
-  GNUNET_log (GNUNET_ERROR_TYPE_WARNING,             \
-              "Query failed at %s:%u: %s/%s/%s/%s/%s\n", \
-              __FILE__, __LINE__, \
-              PQresultErrorField (result, PG_DIAG_MESSAGE_PRIMARY), \
-              PQresultErrorField (result, PG_DIAG_MESSAGE_DETAIL), \
-              PQresultErrorMessage (result), \
-              PQresStatus (PQresultStatus (result)), \
-              PQerrorMessage (conn));
-
 
 /**
  * Log a really unexpected PQ error with all the details we can get hold of.
@@ -82,53 +50,6 @@
 
 
 /**
- * Shorthand for exit jumps.  Logs the current line number
- * and jumps to the "EXITIF_exit" label.
- *
- * @param cond condition that must be TRUE to exit with an error
- */
-#define EXITIF(cond)                                              \
-  do {                                                            \
-    if (cond) { GNUNET_break (0); goto EXITIF_exit; }             \
-  } while (0)
-
-
-/**
- * Execute an SQL statement and log errors on failure. Must be
- * run in a function that has an "SQLEXEC_fail" label to jump
- * to in case the SQL statement failed.
- *
- * @param conn database connection
- * @param sql SQL statement to run
- */
-#define SQLEXEC_(conn, sql)                                             \
-  do {                                                                  \
-    PGresult *result = PQexec (conn, sql);                              \
-    if (PGRES_COMMAND_OK != PQresultStatus (result))                    \
-    {                                                                   \
-      BREAK_DB_ERR (result, conn);                                      \
-      PQclear (result);                                                 \
-      goto SQLEXEC_fail;                                                \
-    }                                                                   \
-    PQclear (result);                                                   \
-  } while (0)
-
-
-/**
- * Run an SQL statement, ignoring errors and clearing the result.
- *
- * @param conn database connection
- * @param sql SQL statement to run
- */
-#define SQLEXEC_IGNORE_ERROR_(conn, sql)                                \
-  do {                                                                  \
-    PGresult *result = PQexec (conn, sql);                              \
-    PQclear (result);                                                   \
-  } while (0)
-
-
-
-/**
  * Handle for a database session (per-thread, for transactions).
  */
 struct TALER_EXCHANGEDB_Session
@@ -138,24 +59,6 @@ struct TALER_EXCHANGEDB_Session
    */
   PGconn *conn;
 
-  /**
-   * Transaction state.  Set to #GNUNET_OK by #postgres_start().
-   * Set to #GNUNET_NO if any part of the transaction failed in a
-   * transient way (i.e. #PG_DIAG_SQLSTATE_DEADLOCK or
-   * #PG_DIAG_SQLSTATE_SERIALIZATION_FAILURE).  Set to
-   * #GNUNET_SYSERR if any part of the transaction failed in a
-   * hard way or if we are not within a transaction scope.
-   *
-   * If #GNUNET_NO, #postgres_commit() will always just do a
-   * rollback and return #GNUNET_NO as well (to retry).
-   *
-   * If #GNUNET_SYSERR, #postgres_commit() will always just do a
-   * rollback and return #GNUNET_SYSERR as well.
-   *
-   * If #GNUNET_OK, #postgres_commit() will try to commit and
-   * return the result from the commit operation.
-   */
-  int state;
 };
 
 
@@ -242,318 +145,297 @@ static int
 postgres_create_tables (void *cls)
 {
   struct PostgresClosure *pc = cls;
+  struct GNUNET_PQ_ExecuteStatement es[] = {
+    /* Denomination table for holding the publicly available information of
+       denominations keys.  The denominations are to be referred to using
+       foreign keys. */
+    GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS denominations"
+                            "(denom_pub_hash BYTEA PRIMARY KEY CHECK 
(LENGTH(denom_pub_hash)=64)"
+                            ",denom_pub BYTEA NOT NULL"
+                            ",master_pub BYTEA NOT NULL CHECK 
(LENGTH(master_pub)=32)"
+                            ",master_sig BYTEA NOT NULL CHECK 
(LENGTH(master_sig)=64)"
+                            ",valid_from INT8 NOT NULL"
+                            ",expire_withdraw INT8 NOT NULL"
+                            ",expire_deposit INT8 NOT NULL"
+                            ",expire_legal INT8 NOT NULL"
+                            ",coin_val INT8 NOT NULL" /* value of this denom */
+                            ",coin_frac INT4 NOT NULL" /* fractional value of 
this denom */
+                            ",coin_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT 
NULL" /* assuming same currency for fees */
+                            ",fee_withdraw_val INT8 NOT NULL"
+                            ",fee_withdraw_frac INT4 NOT NULL"
+                            ",fee_withdraw_curr 
VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+                            ",fee_deposit_val INT8 NOT NULL"
+                            ",fee_deposit_frac INT4 NOT NULL"
+                            ",fee_deposit_curr 
VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+                            ",fee_refresh_val INT8 NOT NULL"
+                            ",fee_refresh_frac INT4 NOT NULL"
+                            ",fee_refresh_curr 
VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+                            ",fee_refund_val INT8 NOT NULL"
+                            ",fee_refund_frac INT4 NOT NULL"
+                            ",fee_refund_curr 
VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+                            ")"),
+    /* denomination_revocations table is for remembering which denomination 
keys have been revoked */
+    GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS 
denomination_revocations"
+                            "(denom_revocations_serial_id BIGSERIAL"
+                            ",denom_pub_hash BYTEA PRIMARY KEY REFERENCES 
denominations (denom_pub_hash) ON DELETE CASCADE"
+                            ",master_sig BYTEA NOT NULL CHECK 
(LENGTH(master_sig)=64)"
+                            ");"),
+    /* reserves table is for summarization of a reserve.  It is updated when 
new
+       funds are added and existing funds are withdrawn.  The 'expiration_date'
+       can be used to eventually get rid of reserves that have not been used
+       for a very long time (either by refunding the owner or by greedily
+       grabbing the money, depending on the Exchange's terms of service) */
+    GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS reserves"
+                            "(reserve_pub BYTEA PRIMARY KEY 
CHECK(LENGTH(reserve_pub)=32)"
+                            ",account_details TEXT NOT NULL "
+                            ",current_balance_val INT8 NOT NULL"
+                            ",current_balance_frac INT4 NOT NULL"
+                            ",current_balance_curr 
VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+                            ",expiration_date INT8 NOT NULL"
+                            ");"),
+    /* index on reserves table */
+    GNUNET_PQ_make_try_execute ("CREATE INDEX reserves_reserve_pub_index ON "
+                                "reserves (reserve_pub);"),
+    GNUNET_PQ_make_try_execute ("CREATE INDEX reserves_expiration_index"
+                                " ON reserves (expiration_date);"),
+    /* reserves_in table collects the transactions which transfer funds
+       into the reserve.  The rows of this table correspond to each
+       incoming transaction. */
+    GNUNET_PQ_make_execute("CREATE TABLE IF NOT EXISTS reserves_in"
+                           "(reserve_in_serial_id BIGSERIAL"
+                           ",reserve_pub BYTEA NOT NULL REFERENCES reserves 
(reserve_pub) ON DELETE CASCADE"
+                           ",wire_reference BYTEA NOT NULL"
+                           ",credit_val INT8 NOT NULL"
+                           ",credit_frac INT4 NOT NULL"
+                           ",credit_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT 
NULL"
+                           ",sender_account_details TEXT NOT NULL"
+                           ",execution_date INT8 NOT NULL"
+                           ",PRIMARY KEY (reserve_pub, wire_reference)"
+                           ");"),
+    /* Create indices on reserves_in */
+    GNUNET_PQ_make_try_execute ("CREATE INDEX reserves_in_execution_index"
+                                " ON reserves_in (execution_date);"),
+    /* This table contains the data for wire transfers the exchange has
+       executed to close a reserve. */
+    GNUNET_PQ_make_execute("CREATE TABLE IF NOT EXISTS reserves_close "
+                           "(close_uuid BIGSERIAL PRIMARY KEY"
+                           ",reserve_pub BYTEA NOT NULL REFERENCES reserves 
(reserve_pub) ON DELETE CASCADE"
+                           ",execution_date INT8 NOT NULL"
+                           ",wtid BYTEA NOT NULL CHECK (LENGTH(wtid)=32)"
+                           ",receiver_account TEXT NOT NULL"
+                           ",amount_val INT8 NOT NULL"
+                           ",amount_frac INT4 NOT NULL"
+                           ",amount_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT 
NULL"
+                           ",closing_fee_val INT8 NOT NULL"
+                           ",closing_fee_frac INT4 NOT NULL"
+                           ",closing_fee_curr 
VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+                           ");"),
+    GNUNET_PQ_make_try_execute("CREATE INDEX reserves_close_by_reserve "
+                               "ON reserves_close(reserve_pub)"),
+    /* Table with the withdraw operations that have been performed on a 
reserve.
+       The 'h_blind_ev' is the hash of the blinded coin. It serves as a primary
+       key, as (broken) clients that use a non-random coin and blinding factor
+       should fail to even withdraw, as otherwise the coins will fail to 
deposit
+       (as they really must be unique). */
+    GNUNET_PQ_make_execute ("CREATE TABLE IF NOT EXISTS reserves_out"
+                            "(reserve_out_serial_id BIGSERIAL"
+                            ",h_blind_ev BYTEA PRIMARY KEY"
+                            ",denom_pub_hash BYTEA NOT NULL REFERENCES 
denominations (denom_pub_hash) ON DELETE CASCADE"
+                            ",denom_sig BYTEA NOT NULL"
+                            ",reserve_pub BYTEA NOT NULL REFERENCES reserves 
(reserve_pub) ON DELETE CASCADE"
+                            ",reserve_sig BYTEA NOT NULL CHECK 
(LENGTH(reserve_sig)=64)"
+                            ",execution_date INT8 NOT NULL"
+                            ",amount_with_fee_val INT8 NOT NULL"
+                            ",amount_with_fee_frac INT4 NOT NULL"
+                            ",amount_with_fee_curr 
VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+                            ");"),
+    /* Index blindcoins(reserve_pub) for get_reserves_out statement */
+    GNUNET_PQ_make_try_execute ("CREATE INDEX reserves_out_reserve_pub_index 
ON"
+                                " reserves_out (reserve_pub)"),
+    GNUNET_PQ_make_try_execute ("CREATE INDEX reserves_out_execution_date ON "
+                                "reserves_out (execution_date)"),
+    /* Table with coins that have been (partially) spent, used to track
+       coin information only once. */
+    GNUNET_PQ_make_execute("CREATE TABLE IF NOT EXISTS known_coins "
+                           "(coin_pub BYTEA NOT NULL PRIMARY KEY CHECK 
(LENGTH(coin_pub)=32)"
+                           ",denom_pub_hash BYTEA NOT NULL REFERENCES 
denominations (denom_pub_hash) ON DELETE CASCADE"
+                           ",denom_sig BYTEA NOT NULL"
+                           ");"),
+    /**
+     * The DB will show negative values for some values of the following 
fields as
+     * we use them as 16 bit unsigned integers
+     *   @a num_newcoins
+     *   @a noreveal_index
+     * Do not do arithmetic in SQL on these fields.
+     * NOTE: maybe we should instead forbid values >= 2^15 categorically?
+     */
+    GNUNET_PQ_make_execute("CREATE TABLE IF NOT EXISTS refresh_sessions "
+                           "(melt_serial_id BIGSERIAL"
+                           ",session_hash BYTEA PRIMARY KEY CHECK 
(LENGTH(session_hash)=64)"
+                           ",old_coin_pub BYTEA NOT NULL REFERENCES 
known_coins (coin_pub) ON DELETE CASCADE"
+                           ",old_coin_sig BYTEA NOT NULL 
CHECK(LENGTH(old_coin_sig)=64)"
+                           ",amount_with_fee_val INT8 NOT NULL"
+                           ",amount_with_fee_frac INT4 NOT NULL"
+                           ",amount_with_fee_curr 
VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+                           ",num_newcoins INT2 NOT NULL"
+                           ",noreveal_index INT2 NOT NULL"
+                           ");"),
+    /* Table with information about the desired denominations to be created
+       during a refresh operation; contains the denomination key for each
+       of the coins (for a given refresh session) */
+    GNUNET_PQ_make_execute("CREATE TABLE IF NOT EXISTS refresh_order "
+                           "(session_hash BYTEA NOT NULL REFERENCES 
refresh_sessions (session_hash) ON DELETE CASCADE"
+                           ",newcoin_index INT2 NOT NULL "
+                           ",denom_pub_hash BYTEA NOT NULL REFERENCES 
denominations (denom_pub_hash) ON DELETE CASCADE"
+                           ",PRIMARY KEY (session_hash, newcoin_index)"
+                           ");"),
+    /* Table with the commitments for a refresh operation; includes
+       the session_hash for which this is the link information, the
+       oldcoin index and the cut-and-choose index (from 0 to 
#TALER_CNC_KAPPA-1),
+       as well as the actual link data (the transfer public key and the 
encrypted
+       link secret) */
+    GNUNET_PQ_make_execute("CREATE TABLE IF NOT EXISTS 
refresh_transfer_public_key "
+                           "(session_hash BYTEA NOT NULL PRIMARY KEY 
REFERENCES refresh_sessions (session_hash) ON DELETE CASCADE"
+                           ",transfer_pub BYTEA NOT NULL 
CHECK(LENGTH(transfer_pub)=32)"
+                           ");"),
+    /* Table with the commitments for the new coins that are to be created
+       during a melting session.  Includes the session, the cut-and-choose
+       index and the index of the new coin, and the envelope of the new
+       coin to be signed, as well as the encrypted information about the
+       private key and the blinding factor for the coin (for verification
+       in case this newcoin_index is chosen to be revealed) */
+    GNUNET_PQ_make_execute("CREATE TABLE IF NOT EXISTS refresh_commit_coin "
+                           "(session_hash BYTEA NOT NULL REFERENCES 
refresh_sessions (session_hash) ON DELETE CASCADE"
+                           ",newcoin_index INT2 NOT NULL"
+                           ",coin_ev BYTEA NOT NULL"
+                           ",UNIQUE (session_hash, newcoin_index)"
+                           ");"),
+    GNUNET_PQ_make_try_execute("CREATE INDEX 
refresh_commit_coin_session_hash_index "
+                               "ON refresh_commit_coin(session_hash, 
newcoin_index)"),
+    /* Table with the signatures over coins generated during a refresh
+       operation. Needed to answer /refresh/link queries later.  Stores
+       the coin signatures under the respective session hash and index. */
+    GNUNET_PQ_make_execute("CREATE TABLE IF NOT EXISTS refresh_out "
+                           "(session_hash BYTEA NOT NULL REFERENCES 
refresh_sessions (session_hash) ON DELETE CASCADE"
+                           ",newcoin_index INT2 NOT NULL"
+                           ",ev_sig BYTEA NOT NULL"
+                           ",UNIQUE (session_hash, newcoin_index)"
+                           ");"),
+    GNUNET_PQ_make_try_execute("CREATE INDEX refresh_out_session_hash_index "
+                               "ON refresh_out(session_hash, newcoin_index)"),
+    /* This table contains the wire transfers the exchange is supposed to
+       execute to transmit funds to the merchants (and manage refunds). */
+    GNUNET_PQ_make_execute("CREATE TABLE IF NOT EXISTS deposits "
+                           "(deposit_serial_id BIGSERIAL PRIMARY KEY"
+                           ",coin_pub BYTEA NOT NULL REFERENCES known_coins 
(coin_pub) ON DELETE CASCADE"
+                           ",amount_with_fee_val INT8 NOT NULL"
+                           ",amount_with_fee_frac INT4 NOT NULL"
+                           ",amount_with_fee_curr 
VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+                           ",timestamp INT8 NOT NULL"
+                           ",refund_deadline INT8 NOT NULL"
+                           ",wire_deadline INT8 NOT NULL"
+                           ",merchant_pub BYTEA NOT NULL CHECK 
(LENGTH(merchant_pub)=32)"
+                           ",h_contract_terms BYTEA NOT NULL CHECK 
(LENGTH(h_contract_terms)=64)"
+                           ",h_wire BYTEA NOT NULL CHECK (LENGTH(h_wire)=64)"
+                           ",coin_sig BYTEA NOT NULL CHECK 
(LENGTH(coin_sig)=64)"
+                           ",wire TEXT NOT NULL"
+                           ",tiny BOOLEAN NOT NULL DEFAULT false"
+                           ",done BOOLEAN NOT NULL DEFAULT false"
+                           ",UNIQUE (coin_pub, h_contract_terms, merchant_pub)"
+                           ");"),
+    /* Index for get_deposit statement on coin_pub, h_contract_terms and 
merchant_pub */
+    GNUNET_PQ_make_try_execute("CREATE INDEX deposits_coin_pub_index "
+                               "ON deposits(coin_pub, h_contract_terms, 
merchant_pub)"),
+    /* Table with information about coins that have been refunded. (Technically
+       one of the deposit operations that a coin was involved with is 
refunded.)*/
+    GNUNET_PQ_make_execute("CREATE TABLE IF NOT EXISTS refunds "
+                           "(refund_serial_id BIGSERIAL"
+                           ",coin_pub BYTEA NOT NULL REFERENCES known_coins 
(coin_pub) ON DELETE CASCADE"
+                           ",merchant_pub BYTEA NOT NULL 
CHECK(LENGTH(merchant_pub)=32)"
+                           ",merchant_sig BYTEA NOT NULL 
CHECK(LENGTH(merchant_sig)=64)"
+                           ",h_contract_terms BYTEA NOT NULL 
CHECK(LENGTH(h_contract_terms)=64)"
+                           ",rtransaction_id INT8 NOT NULL"
+                           ",amount_with_fee_val INT8 NOT NULL"
+                           ",amount_with_fee_frac INT4 NOT NULL"
+                           ",amount_with_fee_curr 
VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
+                           ",PRIMARY KEY (coin_pub, merchant_pub, 
h_contract_terms, rtransaction_id)" /* this combo must be unique, and we 
usually select by coin_pub */
+                           ");"),
+    /* This table contains the data for
+       wire transfers the exchange has executed. */
+    GNUNET_PQ_make_execute("CREATE TABLE IF NOT EXISTS wire_out "
+                           "(wireout_uuid BIGSERIAL PRIMARY KEY"
+                           ",execution_date INT8 NOT NULL"
+                           ",wtid_raw BYTEA UNIQUE NOT NULL CHECK 
(LENGTH(wtid_raw)=" TALER_WIRE_TRANSFER_IDENTIFIER_LEN_STR ")"
+                           ",wire_target TEXT NOT NULL"
+                           ",amount_val INT8 NOT NULL"
+                           ",amount_frac INT4 NOT NULL"
+                           ",amount_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT 
NULL"
+                           ");"),
+    /* Table for the tracking API, mapping from wire transfer identifiers
+       to transactions and back */
+    GNUNET_PQ_make_execute("CREATE TABLE IF NOT EXISTS aggregation_tracking "
+                           "(aggregation_serial_id BIGSERIAL"
+                           ",deposit_serial_id INT8 PRIMARY KEY REFERENCES 
deposits (deposit_serial_id) ON DELETE CASCADE"
+                           ",wtid_raw BYTEA  CONSTRAINT wire_out_ref 
REFERENCES wire_out(wtid_raw) ON DELETE CASCADE DEFERRABLE"
+                           ");"),
+    /* Index for lookup_transactions statement on wtid */
+    GNUNET_PQ_make_try_execute("CREATE INDEX aggregation_tracking_wtid_index "
+                               "ON aggregation_tracking(wtid_raw)"),
+    /* Table for the wire fees. */
+    GNUNET_PQ_make_execute("CREATE TABLE IF NOT EXISTS wire_fee "
+                           "(wire_method VARCHAR NOT NULL"
+                           ",start_date INT8 NOT NULL"
+                           ",end_date INT8 NOT NULL"
+                           ",wire_fee_val INT8 NOT NULL"
+                           ",wire_fee_frac INT4 NOT NULL"
+                           ",wire_fee_curr VARCHAR("TALER_CURRENCY_LEN_STR") 
NOT NULL"
+                           ",master_sig BYTEA NOT NULL CHECK 
(LENGTH(master_sig)=64)"
+                           ",PRIMARY KEY (wire_method, start_date)" /* this 
combo must be unique */
+                           ");"),
+    /* Index for lookup_transactions statement on wtid */
+    GNUNET_PQ_make_try_execute("CREATE INDEX aggregation_tracking_wtid_index "
+                               "ON aggregation_tracking(wtid_raw);"),
+    /* Table for /payback information */
+    GNUNET_PQ_make_execute("CREATE TABLE IF NOT EXISTS payback "
+                           "(payback_uuid BIGSERIAL"
+                           ",reserve_pub BYTEA NOT NULL REFERENCES reserves 
(reserve_pub) ON DELETE CASCADE"
+                           ",coin_pub BYTEA NOT NULL REFERENCES known_coins 
(coin_pub) ON DELETE CASCADE"
+                           ",coin_sig BYTEA NOT NULL 
CHECK(LENGTH(coin_sig)=64)"
+                           ",coin_blind BYTEA NOT NULL 
CHECK(LENGTH(coin_blind)=32)"
+                           ",amount_val INT8 NOT NULL"
+                           ",amount_frac INT4 NOT NULL"
+                           ",amount_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT 
NULL"
+                           ",timestamp INT8 NOT NULL"
+                           ",h_blind_ev BYTEA NOT NULL REFERENCES reserves_out 
(h_blind_ev) ON DELETE CASCADE"
+                           ");"),
+    GNUNET_PQ_make_try_execute("CREATE INDEX payback_by_coin_index "
+                               "ON payback(coin_pub);"),
+    GNUNET_PQ_make_try_execute("CREATE INDEX payback_by_reserve_index "
+                               "ON payback(reserve_pub);"),
+
+    /* This table contains the pre-commit data for
+       wire transfers the exchange is about to execute. */
+    GNUNET_PQ_make_execute("CREATE TABLE IF NOT EXISTS prewire "
+                           "(prewire_uuid BIGSERIAL PRIMARY KEY"
+                           ",type TEXT NOT NULL"
+                           ",finished BOOLEAN NOT NULL DEFAULT false"
+                           ",buf BYTEA NOT NULL"
+                           ");"),
+    /* Index for prepare_data_iterate statement */
+    GNUNET_PQ_make_try_execute("CREATE INDEX prepare_iteration_index "
+                               "ON prewire(type,finished);"),
+    GNUNET_PQ_EXECUTE_STATEMENT_END
+  };
   PGconn *conn;
+  int ret;
 
   conn = GNUNET_PQ_connect (pc->connection_cfg_str);
   if (NULL == conn)
     return GNUNET_SYSERR;
-#define SQLEXEC(sql) SQLEXEC_(conn, sql);
-#define SQLEXEC_INDEX(sql) SQLEXEC_IGNORE_ERROR_(conn, sql);
-  /* Denomination table for holding the publicly available information of
-     denominations keys.  The denominations are to be referred to using
-     foreign keys. */
-  SQLEXEC ("CREATE TABLE IF NOT EXISTS denominations"
-           "(denom_pub_hash BYTEA PRIMARY KEY CHECK 
(LENGTH(denom_pub_hash)=64)"
-          ",denom_pub BYTEA NOT NULL"
-           ",master_pub BYTEA NOT NULL CHECK (LENGTH(master_pub)=32)"
-           ",master_sig BYTEA NOT NULL CHECK (LENGTH(master_sig)=64)"
-           ",valid_from INT8 NOT NULL"
-           ",expire_withdraw INT8 NOT NULL"
-           ",expire_deposit INT8 NOT NULL"
-           ",expire_legal INT8 NOT NULL"
-           ",coin_val INT8 NOT NULL" /* value of this denom */
-           ",coin_frac INT4 NOT NULL" /* fractional value of this denom */
-           ",coin_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL" /* assuming 
same currency for fees */
-           ",fee_withdraw_val INT8 NOT NULL"
-           ",fee_withdraw_frac INT4 NOT NULL"
-           ",fee_withdraw_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
-           ",fee_deposit_val INT8 NOT NULL"
-           ",fee_deposit_frac INT4 NOT NULL"
-           ",fee_deposit_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
-           ",fee_refresh_val INT8 NOT NULL"
-           ",fee_refresh_frac INT4 NOT NULL"
-           ",fee_refresh_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
-           ",fee_refund_val INT8 NOT NULL"
-           ",fee_refund_frac INT4 NOT NULL"
-           ",fee_refund_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
-           ")");
-  /* denomination_revocations table is for remembering which denomination keys 
have been revoked */
-  SQLEXEC ("CREATE TABLE IF NOT EXISTS denomination_revocations"
-           "(denom_revocations_serial_id BIGSERIAL"
-          ",denom_pub_hash BYTEA PRIMARY KEY REFERENCES denominations 
(denom_pub_hash) ON DELETE CASCADE"
-           ",master_sig BYTEA NOT NULL CHECK (LENGTH(master_sig)=64)"
-           ")");
-
-  /* reserves table is for summarization of a reserve.  It is updated when new
-     funds are added and existing funds are withdrawn.  The 'expiration_date'
-     can be used to eventually get rid of reserves that have not been used
-     for a very long time (either by refunding the owner or by greedily
-     grabbing the money, depending on the Exchange's terms of service) */
-  SQLEXEC ("CREATE TABLE IF NOT EXISTS reserves"
-           "(reserve_pub BYTEA PRIMARY KEY CHECK(LENGTH(reserve_pub)=32)"
-          ",account_details TEXT NOT NULL "
-           ",current_balance_val INT8 NOT NULL"
-           ",current_balance_frac INT4 NOT NULL"
-           ",current_balance_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
-           ",expiration_date INT8 NOT NULL"
-           ")");
-  /* index on reserves table */
-  SQLEXEC_INDEX ("CREATE INDEX reserves_reserve_pub_index ON "
-                 "reserves (reserve_pub)");
-  SQLEXEC_INDEX ("CREATE INDEX reserves_expiration_index"
-                " ON reserves (expiration_date);");
-
-  /* reserves_in table collects the transactions which transfer funds
-     into the reserve.  The rows of this table correspond to each
-     incoming transaction. */
-  SQLEXEC("CREATE TABLE IF NOT EXISTS reserves_in"
-          "(reserve_in_serial_id BIGSERIAL"
-         ",reserve_pub BYTEA NOT NULL REFERENCES reserves (reserve_pub) ON 
DELETE CASCADE"
-          ",wire_reference BYTEA NOT NULL"
-          ",credit_val INT8 NOT NULL"
-          ",credit_frac INT4 NOT NULL"
-          ",credit_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
-          ",sender_account_details TEXT NOT NULL"
-          ",execution_date INT8 NOT NULL"
-          ",PRIMARY KEY (reserve_pub, wire_reference)"
-          ");");
-  /* Create indices on reserves_in */
-  SQLEXEC_INDEX ("CREATE INDEX reserves_in_execution_index"
-                " ON reserves_in (execution_date);");
-
-  /* This table contains the data for wire transfers the exchange has
-     executed to close a reserve. */
-  SQLEXEC("CREATE TABLE IF NOT EXISTS reserves_close "
-          "(close_uuid BIGSERIAL PRIMARY KEY"
-          ",reserve_pub BYTEA NOT NULL REFERENCES reserves (reserve_pub) ON 
DELETE CASCADE"
-         ",execution_date INT8 NOT NULL"
-         ",wtid BYTEA NOT NULL CHECK (LENGTH(wtid)=32)"
-          ",receiver_account TEXT NOT NULL"
-          ",amount_val INT8 NOT NULL"
-          ",amount_frac INT4 NOT NULL"
-          ",amount_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
-          ",closing_fee_val INT8 NOT NULL"
-          ",closing_fee_frac INT4 NOT NULL"
-          ",closing_fee_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
-          ")");
-  SQLEXEC_INDEX("CREATE INDEX reserves_close_by_reserve "
-                "ON reserves_close(reserve_pub)");
-
-  /* Table with the withdraw operations that have been performed on a reserve.
-     The 'h_blind_ev' is the hash of the blinded coin. It serves as a primary
-     key, as (broken) clients that use a non-random coin and blinding factor
-     should fail to even withdraw, as otherwise the coins will fail to deposit
-     (as they really must be unique). */
-  SQLEXEC ("CREATE TABLE IF NOT EXISTS reserves_out"
-           "(reserve_out_serial_id BIGSERIAL"
-          ",h_blind_ev BYTEA PRIMARY KEY"
-           ",denom_pub_hash BYTEA NOT NULL REFERENCES denominations 
(denom_pub_hash) ON DELETE CASCADE"
-           ",denom_sig BYTEA NOT NULL"
-           ",reserve_pub BYTEA NOT NULL REFERENCES reserves (reserve_pub) ON 
DELETE CASCADE"
-           ",reserve_sig BYTEA NOT NULL CHECK (LENGTH(reserve_sig)=64)"
-           ",execution_date INT8 NOT NULL"
-           ",amount_with_fee_val INT8 NOT NULL"
-           ",amount_with_fee_frac INT4 NOT NULL"
-           ",amount_with_fee_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
-           ");");
-  /* Index blindcoins(reserve_pub) for get_reserves_out statement */
-  SQLEXEC_INDEX ("CREATE INDEX reserves_out_reserve_pub_index ON"
-                 " reserves_out (reserve_pub)");
-  SQLEXEC_INDEX ("CREATE INDEX reserves_out_execution_date ON "
-                 "reserves_out (execution_date)");
-  /* Table with coins that have been (partially) spent, used to track
-     coin information only once. */
-  SQLEXEC("CREATE TABLE IF NOT EXISTS known_coins "
-          "(coin_pub BYTEA NOT NULL PRIMARY KEY CHECK (LENGTH(coin_pub)=32)"
-          ",denom_pub_hash BYTEA NOT NULL REFERENCES denominations 
(denom_pub_hash) ON DELETE CASCADE"
-          ",denom_sig BYTEA NOT NULL"
-          ")");
-  /**
-   * The DB will show negative values for some values of the following fields 
as
-   * we use them as 16 bit unsigned integers
-   *   @a num_newcoins
-   *   @a noreveal_index
-   * Do not do arithmetic in SQL on these fields.
-   * NOTE: maybe we should instead forbid values >= 2^15 categorically?
-   */
-  SQLEXEC("CREATE TABLE IF NOT EXISTS refresh_sessions "
-          "(melt_serial_id BIGSERIAL"
-         ",session_hash BYTEA PRIMARY KEY CHECK (LENGTH(session_hash)=64)"
-          ",old_coin_pub BYTEA NOT NULL REFERENCES known_coins (coin_pub) ON 
DELETE CASCADE"
-          ",old_coin_sig BYTEA NOT NULL CHECK(LENGTH(old_coin_sig)=64)"
-          ",amount_with_fee_val INT8 NOT NULL"
-          ",amount_with_fee_frac INT4 NOT NULL"
-          ",amount_with_fee_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
-          ",num_newcoins INT2 NOT NULL"
-          ",noreveal_index INT2 NOT NULL"
-          ")");
-
-  /* Table with information about the desired denominations to be created
-     during a refresh operation; contains the denomination key for each
-     of the coins (for a given refresh session) */
-  SQLEXEC("CREATE TABLE IF NOT EXISTS refresh_order "
-          "(session_hash BYTEA NOT NULL REFERENCES refresh_sessions 
(session_hash) ON DELETE CASCADE"
-          ",newcoin_index INT2 NOT NULL "
-          ",denom_pub_hash BYTEA NOT NULL REFERENCES denominations 
(denom_pub_hash) ON DELETE CASCADE"
-          ",PRIMARY KEY (session_hash, newcoin_index)"
-          ")");
-
-  /* Table with the commitments for a refresh operation; includes
-     the session_hash for which this is the link information, the
-     oldcoin index and the cut-and-choose index (from 0 to #TALER_CNC_KAPPA-1),
-     as well as the actual link data (the transfer public key and the encrypted
-     link secret) */
-  SQLEXEC("CREATE TABLE IF NOT EXISTS refresh_transfer_public_key "
-          "(session_hash BYTEA NOT NULL PRIMARY KEY REFERENCES 
refresh_sessions (session_hash) ON DELETE CASCADE"
-          ",transfer_pub BYTEA NOT NULL CHECK(LENGTH(transfer_pub)=32)"
-          ")");
-
-  /* Table with the commitments for the new coins that are to be created
-     during a melting session.  Includes the session, the cut-and-choose
-     index and the index of the new coin, and the envelope of the new
-     coin to be signed, as well as the encrypted information about the
-     private key and the blinding factor for the coin (for verification
-     in case this newcoin_index is chosen to be revealed) */
-  SQLEXEC("CREATE TABLE IF NOT EXISTS refresh_commit_coin "
-          "(session_hash BYTEA NOT NULL REFERENCES refresh_sessions 
(session_hash) ON DELETE CASCADE"
-          ",newcoin_index INT2 NOT NULL"
-          ",coin_ev BYTEA NOT NULL"
-          ",UNIQUE (session_hash, newcoin_index)"
-          ")");
-  SQLEXEC_INDEX("CREATE INDEX refresh_commit_coin_session_hash_index "
-                "ON refresh_commit_coin(session_hash, newcoin_index)");
-
-
-  /* Table with the signatures over coins generated during a refresh
-     operation. Needed to answer /refresh/link queries later.  Stores
-     the coin signatures under the respective session hash and index. */
-  SQLEXEC("CREATE TABLE IF NOT EXISTS refresh_out "
-          "(session_hash BYTEA NOT NULL REFERENCES refresh_sessions 
(session_hash) ON DELETE CASCADE"
-          ",newcoin_index INT2 NOT NULL"
-          ",ev_sig BYTEA NOT NULL"
-          ",UNIQUE (session_hash, newcoin_index)"
-          ")");
-  SQLEXEC_INDEX("CREATE INDEX refresh_out_session_hash_index "
-                "ON refresh_out(session_hash, newcoin_index)");
-
-  /* This table contains the wire transfers the exchange is supposed to
-     execute to transmit funds to the merchants (and manage refunds). */
-  SQLEXEC("CREATE TABLE IF NOT EXISTS deposits "
-          "(deposit_serial_id BIGSERIAL PRIMARY KEY"
-          ",coin_pub BYTEA NOT NULL REFERENCES known_coins (coin_pub) ON 
DELETE CASCADE"
-          ",amount_with_fee_val INT8 NOT NULL"
-          ",amount_with_fee_frac INT4 NOT NULL"
-          ",amount_with_fee_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
-          ",timestamp INT8 NOT NULL"
-          ",refund_deadline INT8 NOT NULL"
-          ",wire_deadline INT8 NOT NULL"
-          ",merchant_pub BYTEA NOT NULL CHECK (LENGTH(merchant_pub)=32)"
-          ",h_contract_terms BYTEA NOT NULL CHECK 
(LENGTH(h_contract_terms)=64)"
-          ",h_wire BYTEA NOT NULL CHECK (LENGTH(h_wire)=64)"
-          ",coin_sig BYTEA NOT NULL CHECK (LENGTH(coin_sig)=64)"
-          ",wire TEXT NOT NULL"
-          ",tiny BOOLEAN NOT NULL DEFAULT false"
-          ",done BOOLEAN NOT NULL DEFAULT false"
-          ",UNIQUE (coin_pub, h_contract_terms, merchant_pub)"
-          ")");
-  /* Index for get_deposit statement on coin_pub, h_contract_terms and 
merchant_pub */
-  SQLEXEC_INDEX("CREATE INDEX deposits_coin_pub_index "
-                "ON deposits(coin_pub, h_contract_terms, merchant_pub)");
-
-  /* Table with information about coins that have been refunded. (Technically
-     one of the deposit operations that a coin was involved with is 
refunded.)*/
-  SQLEXEC("CREATE TABLE IF NOT EXISTS refunds "
-          "(refund_serial_id BIGSERIAL"
-         ",coin_pub BYTEA NOT NULL REFERENCES known_coins (coin_pub) ON DELETE 
CASCADE"
-          ",merchant_pub BYTEA NOT NULL CHECK(LENGTH(merchant_pub)=32)"
-          ",merchant_sig BYTEA NOT NULL CHECK(LENGTH(merchant_sig)=64)"
-          ",h_contract_terms BYTEA NOT NULL CHECK(LENGTH(h_contract_terms)=64)"
-          ",rtransaction_id INT8 NOT NULL"
-          ",amount_with_fee_val INT8 NOT NULL"
-          ",amount_with_fee_frac INT4 NOT NULL"
-          ",amount_with_fee_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
-          ",PRIMARY KEY (coin_pub, merchant_pub, h_contract_terms, 
rtransaction_id)" /* this combo must be unique, and we usually select by 
coin_pub */
-          ") ");
-
-  /* This table contains the data for
-     wire transfers the exchange has executed. */
-  SQLEXEC("CREATE TABLE IF NOT EXISTS wire_out "
-          "(wireout_uuid BIGSERIAL PRIMARY KEY"
-          ",execution_date INT8 NOT NULL"
-          ",wtid_raw BYTEA UNIQUE NOT NULL CHECK (LENGTH(wtid_raw)=" 
TALER_WIRE_TRANSFER_IDENTIFIER_LEN_STR ")"
-          ",wire_target TEXT NOT NULL"
-          ",amount_val INT8 NOT NULL"
-          ",amount_frac INT4 NOT NULL"
-          ",amount_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
-          ")");
-
-  /* Table for the tracking API, mapping from wire transfer identifiers
-     to transactions and back */
-  SQLEXEC("CREATE TABLE IF NOT EXISTS aggregation_tracking "
-          "(aggregation_serial_id BIGSERIAL"
-          ",deposit_serial_id INT8 PRIMARY KEY REFERENCES deposits 
(deposit_serial_id) ON DELETE CASCADE"
-          ",wtid_raw BYTEA  CONSTRAINT wire_out_ref REFERENCES 
wire_out(wtid_raw) ON DELETE CASCADE DEFERRABLE"
-          ")");
-  /* Index for lookup_transactions statement on wtid */
-  SQLEXEC_INDEX("CREATE INDEX aggregation_tracking_wtid_index "
-                "ON aggregation_tracking(wtid_raw)");
-
-
-  /* Table for the wire fees. */
-  SQLEXEC("CREATE TABLE IF NOT EXISTS wire_fee "
-          "(wire_method VARCHAR NOT NULL"
-          ",start_date INT8 NOT NULL"
-          ",end_date INT8 NOT NULL"
-          ",wire_fee_val INT8 NOT NULL"
-          ",wire_fee_frac INT4 NOT NULL"
-          ",wire_fee_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
-          ",master_sig BYTEA NOT NULL CHECK (LENGTH(master_sig)=64)"
-          ",PRIMARY KEY (wire_method, start_date)" /* this combo must be 
unique */
-          ")");
-  /* Index for lookup_transactions statement on wtid */
-  SQLEXEC_INDEX("CREATE INDEX aggregation_tracking_wtid_index "
-                "ON aggregation_tracking(wtid_raw)");
-
-  /* Table for /payback information */
-  SQLEXEC("CREATE TABLE IF NOT EXISTS payback "
-          "(payback_uuid BIGSERIAL"
-          ",reserve_pub BYTEA NOT NULL REFERENCES reserves (reserve_pub) ON 
DELETE CASCADE"
-          ",coin_pub BYTEA NOT NULL REFERENCES known_coins (coin_pub) ON 
DELETE CASCADE"
-          ",coin_sig BYTEA NOT NULL CHECK(LENGTH(coin_sig)=64)"
-          ",coin_blind BYTEA NOT NULL CHECK(LENGTH(coin_blind)=32)"
-          ",amount_val INT8 NOT NULL"
-          ",amount_frac INT4 NOT NULL"
-          ",amount_curr VARCHAR("TALER_CURRENCY_LEN_STR") NOT NULL"
-          ",timestamp INT8 NOT NULL"
-          ",h_blind_ev BYTEA NOT NULL REFERENCES reserves_out (h_blind_ev) ON 
DELETE CASCADE"
-          ")");
-  SQLEXEC_INDEX("CREATE INDEX payback_by_coin_index "
-                "ON payback(coin_pub)");
-  SQLEXEC_INDEX("CREATE INDEX payback_by_reserve_index "
-                "ON payback(reserve_pub)");
-
-  /* This table contains the pre-commit data for
-     wire transfers the exchange is about to execute. */
-  SQLEXEC("CREATE TABLE IF NOT EXISTS prewire "
-          "(prewire_uuid BIGSERIAL PRIMARY KEY"
-          ",type TEXT NOT NULL"
-          ",finished BOOLEAN NOT NULL DEFAULT false"
-          ",buf BYTEA NOT NULL"
-          ")");
-  /* Index for prepare_data_iterate statement */
-  SQLEXEC_INDEX("CREATE INDEX prepare_iteration_index "
-                "ON prewire(type,finished)");
-
-
-#undef SQLEXEC
-#undef SQLEXEC_INDEX
-
-  PQfinish (conn);
-  return GNUNET_OK;
-
- SQLEXEC_fail:
+  ret = GNUNET_PQ_exec_statements (conn,
+                                   es);
   PQfinish (conn);
-  return GNUNET_SYSERR;
+  return ret;
 }
 
 
@@ -566,1050 +448,1015 @@ postgres_create_tables (void *cls)
 static int
 postgres_prepare (PGconn *db_conn)
 {
-  PGresult *result;
-
-#define PREPARE(name, sql, ...)                                 \
-  do {                                                          \
-    result = PQprepare (db_conn, name, sql, __VA_ARGS__);       \
-    if (PGRES_COMMAND_OK != PQresultStatus (result))            \
-    {                                                           \
-      BREAK_DB_ERR (result, db_conn);                           \
-      PQclear (result); result = NULL;                          \
-      return GNUNET_SYSERR;                                     \
-    }                                                           \
-    PQclear (result); result = NULL;                            \
-  } while (0);
-
-  /* Used in #postgres_insert_denomination_info() */
-  PREPARE ("denomination_insert",
-           "INSERT INTO denominations "
-           "(denom_pub_hash"
-          ",denom_pub"
-           ",master_pub"
-           ",master_sig"
-           ",valid_from"
-           ",expire_withdraw"
-           ",expire_deposit"
-           ",expire_legal"
-           ",coin_val" /* value of this denom */
-           ",coin_frac" /* fractional value of this denom */
-           ",coin_curr" /* assuming same currency for fees */
-           ",fee_withdraw_val"
-           ",fee_withdraw_frac"
-           ",fee_withdraw_curr" /* must match coin_curr */
-           ",fee_deposit_val"
-           ",fee_deposit_frac"
-           ",fee_deposit_curr"  /* must match coin_curr */
-           ",fee_refresh_val"
-           ",fee_refresh_frac"
-           ",fee_refresh_curr" /* must match coin_curr */
-           ",fee_refund_val"
-           ",fee_refund_frac"
-           ",fee_refund_curr" /* must match coin_curr */
-           ") VALUES "
-           "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10,"
-           " $11, $12, $13, $14, $15, $16, $17, $18,"
-          " $19, $20, $21, $22, $23);",
-           23, NULL);
-
-  /* Used in #postgres_get_denomination_info() */
-  PREPARE ("denomination_get",
-           "SELECT"
-           " master_pub"
-           ",master_sig"
-           ",valid_from"
-           ",expire_withdraw"
-           ",expire_deposit"
-           ",expire_legal"
-           ",coin_val"  /* value of this denom */
-           ",coin_frac" /* fractional value of this denom */
-           ",coin_curr" /* assuming same currency for fees */
-           ",fee_withdraw_val"
-           ",fee_withdraw_frac"
-           ",fee_withdraw_curr" /* must match coin_curr */
-           ",fee_deposit_val"
-           ",fee_deposit_frac"
-           ",fee_deposit_curr"  /* must match coin_curr */
-           ",fee_refresh_val"
-           ",fee_refresh_frac"
-           ",fee_refresh_curr" /* must match coin_curr */
-           ",fee_refund_val"
-           ",fee_refund_frac"
-           ",fee_refund_curr" /* must match coin_curr */
-           " FROM denominations"
-           " WHERE denom_pub=$1;",
-           1, NULL);
-
-  /* Used in #postgres_insert_denomination_revocation() */
-  PREPARE ("denomination_revocation_insert",
-           "INSERT INTO denomination_revocations "
-           "(denom_pub_hash"
-           ",master_sig"
-           ") VALUES "
-           "($1, $2);",
-           2, NULL);
-
-  /* Used in #postgres_get_denomination_revocation() */
-  PREPARE ("denomination_revocation_get",
-           "SELECT"
-           " master_sig"
-          ",denom_revocations_serial_id"
-           " FROM denomination_revocations"
-           " WHERE denom_pub_hash=$1;",
-           1, NULL);
-
-
-  /* Used in #postgres_reserve_get() */
-  PREPARE ("reserve_get",
-           "SELECT"
-           " current_balance_val"
-           ",current_balance_frac"
-           ",current_balance_curr"
-           ",expiration_date"
-           " FROM reserves"
-           " WHERE reserve_pub=$1"
-           " LIMIT 1;",
-           1, NULL);
-
-  /* Used in #postgres_reserves_in_insert() when the reserve is new */
-  PREPARE ("reserve_create",
-           "INSERT INTO reserves "
-           "(reserve_pub"
-          ",account_details"
-           ",current_balance_val"
-           ",current_balance_frac"
-           ",current_balance_curr"
-           ",expiration_date"
-           ") VALUES "
-           "($1, $2, $3, $4, $5, $6);",
-           6, NULL);
-
-  /* Used in #postgres_insert_reserve_closed() */
-  PREPARE ("reserves_close_insert",
-          "INSERT INTO reserves_close "
-          "(reserve_pub"
-          ",execution_date"
-          ",wtid"
-          ",receiver_account"
-          ",amount_val"
-          ",amount_frac"
-          ",amount_curr"
-          ",closing_fee_val"
-          ",closing_fee_frac"
-          ",closing_fee_curr"
-          ") VALUES "
-           "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10);",
-           10, NULL);
-
-  /* Used in #postgres_reserves_update() when the reserve is updated */
-  PREPARE ("reserve_update",
-           "UPDATE reserves"
-           " SET"
-           " expiration_date=$1 "
-           ",current_balance_val=$2 "
-           ",current_balance_frac=$3 "
-           "WHERE current_balance_curr=$4 AND reserve_pub=$5",
-           5, NULL);
-
-  /* Used in #postgres_reserves_in_insert() to store transaction details */
-  PREPARE ("reserves_in_add_transaction",
-           "INSERT INTO reserves_in "
-           "(reserve_pub"
-           ",wire_reference"
-           ",credit_val"
-           ",credit_frac"
-           ",credit_curr"
-           ",sender_account_details"
-           ",execution_date"
-           ") VALUES "
-           "($1, $2, $3, $4, $5, $6, $7);",
-           7, NULL);
-
-
-  /* Used in postgres_select_reserves_in_above_serial_id() to obtain inbound
-     transactions for reserves with serial id '\geq' the given parameter */
-  PREPARE ("reserves_in_get_latest_wire_reference",
-           "SELECT"
-           " wire_reference"
-           " FROM reserves_in"
-           " ORDER BY reserve_in_serial_id DESC LIMIT 1",
-           0, NULL);
-
-  /* Used in postgres_select_reserves_in_above_serial_id() to obtain inbound
-     transactions for reserves with serial id '\geq' the given parameter */
-  PREPARE ("audit_reserves_in_get_transactions_incr",
-           "SELECT"
-           " reserve_pub"
-           ",wire_reference"
-           ",credit_val"
-           ",credit_frac"
-           ",credit_curr"
-           ",execution_date"
-           ",sender_account_details"
-           ",reserve_in_serial_id"
-           " FROM reserves_in"
-           " WHERE reserve_in_serial_id>=$1"
-           " ORDER BY reserve_in_serial_id",
-           1, NULL);
-
-  /* Used in #postgres_get_reserve_history() to obtain inbound transactions
-     for a reserve */
-  PREPARE ("reserves_in_get_transactions",
-           "SELECT"
-           " wire_reference"
-           ",credit_val"
-           ",credit_frac"
-           ",credit_curr"
-           ",execution_date"
-           ",sender_account_details"
-           " FROM reserves_in"
-           " WHERE reserve_pub=$1",
-           1, NULL);
-
-  /* Used in #postgres_insert_withdraw_info() to store
-     the signature of a blinded coin with the blinded coin's
-     details before returning it during /reserve/withdraw. We store
-     the coin's denomination information (public key, signature)
-     and the blinded message as well as the reserve that the coin
-     is being withdrawn from and the signature of the message
-     authorizing the withdrawal. */
-  PREPARE ("insert_withdraw_info",
-           "INSERT INTO reserves_out "
-           "(h_blind_ev"
-           ",denom_pub_hash"
-           ",denom_sig"
-           ",reserve_pub"
-           ",reserve_sig"
-           ",execution_date"
-           ",amount_with_fee_val"
-           ",amount_with_fee_frac"
-           ",amount_with_fee_curr"
-           ") VALUES "
-           "($1, $2, $3, $4, $5, $6, $7, $8, $9);",
-           9, NULL);
-
-  /* Used in #postgres_get_withdraw_info() to
-     locate the response for a /reserve/withdraw request
-     using the hash of the blinded message.  Used to
-     make sure /reserve/withdraw requests are idempotent. */
-  PREPARE ("get_withdraw_info",
-           "SELECT"
-           " denom.denom_pub"
-           ",denom_sig"
-           ",reserve_sig"
-           ",reserve_pub"
-           ",execution_date"
-           ",amount_with_fee_val"
-           ",amount_with_fee_frac"
-           ",amount_with_fee_curr"
-           ",denom.fee_withdraw_val"
-           ",denom.fee_withdraw_frac"
-           ",denom.fee_withdraw_curr"
-           " FROM reserves_out"
-           "    JOIN denominations denom USING (denom_pub_hash)"
-           " WHERE h_blind_ev=$1",
-           1, NULL);
-
-  /* Used during #postgres_get_reserve_history() to
-     obtain all of the /reserve/withdraw operations that
-     have been performed on a given reserve. (i.e. to
-     demonstrate double-spending) */
-  PREPARE ("get_reserves_out",
-           "SELECT"
-           " h_blind_ev"
-           ",denom.denom_pub"
-           ",denom_sig"
-           ",reserve_sig"
-           ",execution_date"
-           ",amount_with_fee_val"
-           ",amount_with_fee_frac"
-           ",amount_with_fee_curr"
-           ",denom.fee_withdraw_val"
-           ",denom.fee_withdraw_frac"
-           ",denom.fee_withdraw_curr"
-           " FROM reserves_out"
-           "    JOIN denominations denom USING (denom_pub_hash)"
-           " WHERE reserve_pub=$1;",
-           1, NULL);
-
-  /* Used in #postgres_select_reserves_out_above_serial_id() */
-  PREPARE ("audit_get_reserves_out_incr",
-           "SELECT"
-           " h_blind_ev"
-           ",denom.denom_pub"
-           ",denom_sig"
-           ",reserve_sig"
-           ",reserve_pub"
-           ",execution_date"
-           ",amount_with_fee_val"
-           ",amount_with_fee_frac"
-           ",amount_with_fee_curr"
-           ",reserve_out_serial_id"
-           " FROM reserves_out"
-           "    JOIN denominations denom USING (denom_pub_hash)"
-           " WHERE reserve_out_serial_id>=$1"
-           " ORDER BY reserve_out_serial_id ASC",
-           1, NULL);
-
-  /* Used in #postgres_get_refresh_session() to fetch
-     high-level information about a refresh session */
-  PREPARE ("get_refresh_session",
-           "SELECT"
-           " old_coin_pub"
-           ",old_coin_sig"
-           ",amount_with_fee_val"
-           ",amount_with_fee_frac"
-           ",amount_with_fee_curr"
-           ",denom.fee_refresh_val "
-           ",denom.fee_refresh_frac "
-           ",denom.fee_refresh_curr "
-           ",num_newcoins"
-           ",noreveal_index"
-           " FROM refresh_sessions"
-           "    JOIN known_coins ON (refresh_sessions.old_coin_pub = 
known_coins.coin_pub)"
-           "    JOIN denominations denom USING (denom_pub_hash)"
-           " WHERE session_hash=$1 ",
-           1, NULL);
-
-  /* Used in #postgres_select_refreshs_above_serial_id() to fetch
-     refresh session with id '\geq' the given parameter */
-  PREPARE ("audit_get_refresh_sessions_incr",
-           "SELECT"
-           " denom.denom_pub"
-           ",old_coin_pub"
-           ",old_coin_sig"
-           ",amount_with_fee_val"
-           ",amount_with_fee_frac"
-           ",amount_with_fee_curr"
-           ",num_newcoins"
-           ",noreveal_index"
-           ",melt_serial_id"
-           ",session_hash"
-           " FROM refresh_sessions"
-           "   JOIN known_coins kc ON (refresh_sessions.old_coin_pub = 
kc.coin_pub)"
-           "   JOIN denominations denom ON (kc.denom_pub_hash = 
denom.denom_pub_hash)"
-           " WHERE melt_serial_id>=$1"
-           " ORDER BY melt_serial_id ASC",
-           1, NULL);
-
-  /* Used in #postgres_create_refresh_session() to store
-     high-level information about a refresh session */
-  PREPARE ("insert_refresh_session",
-           "INSERT INTO refresh_sessions "
-           "(session_hash "
-           ",old_coin_pub "
-           ",old_coin_sig "
-           ",amount_with_fee_val "
-           ",amount_with_fee_frac "
-           ",amount_with_fee_curr "
-           ",num_newcoins "
-           ",noreveal_index "
-           ") VALUES "
-           "($1, $2, $3, $4, $5, $6, $7, $8);",
-           8, NULL);
-
-  /* Used in #postgres_get_known_coin() to fetch
-     the denomination public key and signature for
-     a coin known to the exchange. */
-  PREPARE ("get_known_coin",
-           "SELECT"
-           " denom.denom_pub"
-           ",denom_sig"
-           " FROM known_coins"
-           "    JOIN denominations denom USING (denom_pub_hash)"
-           " WHERE coin_pub=$1",
-           1, NULL);
-
-  /* Used in #postgres_insert_known_coin() to store
-     the denomination public key and signature for
-     a coin known to the exchange. */
-  PREPARE ("insert_known_coin",
-           "INSERT INTO known_coins "
-           "(coin_pub"
-           ",denom_pub_hash"
-           ",denom_sig"
-           ") VALUES "
-           "($1,$2,$3);",
-           3, NULL);
-
-  /* Store information about the desired denominations for a
-     refresh operation, used in #postgres_insert_refresh_order() */
-  PREPARE ("insert_refresh_order",
-           "INSERT INTO refresh_order "
-           "(newcoin_index "
-           ",session_hash "
-           ",denom_pub_hash "
-           ") VALUES "
-           "($1, $2, $3);",
-           3, NULL);
-
-  /* Obtain information about the desired denominations for a
-     refresh operation, used in #postgres_get_refresh_order() */
-  PREPARE ("get_refresh_order",
-           "SELECT denom_pub"
-           " FROM refresh_order"
-           "    JOIN denominations denom USING (denom_pub_hash)"
-           " WHERE session_hash=$1 AND newcoin_index=$2",
-           2, NULL);
-
-  /* Query the 'refresh_sessions' by coin public key */
-  PREPARE ("get_refresh_session_by_coin",
-           "SELECT"
-           " session_hash"
-           ",old_coin_sig"
-           ",amount_with_fee_val"
-           ",amount_with_fee_frac"
-           ",amount_with_fee_curr"
-           ",denom.fee_refresh_val "
-           ",denom.fee_refresh_frac "
-           ",denom.fee_refresh_curr "
-           " FROM refresh_sessions"
-           "    JOIN known_coins ON (refresh_sessions.old_coin_pub = 
known_coins.coin_pub)"
-           "    JOIN denominations denom USING (denom_pub_hash)"
-           " WHERE old_coin_pub=$1",
-           1, NULL);
-
-  /* Fetch refunds with rowid '\geq' the given parameter */
-  PREPARE ("audit_get_refunds_incr",
-           "SELECT"
-           " merchant_pub"
-           ",merchant_sig"
-           ",h_contract_terms"
-           ",rtransaction_id"
-           ",denom.denom_pub"
-           ",coin_pub"
-           ",amount_with_fee_val"
-           ",amount_with_fee_frac"
-           ",amount_with_fee_curr"
-           ",refund_serial_id"
-           " FROM refunds"
-           "   JOIN known_coins kc USING (coin_pub)"
-           "   JOIN denominations denom ON (kc.denom_pub_hash = 
denom.denom_pub_hash)"
-           " WHERE refund_serial_id>=$1"
-           " ORDER BY refund_serial_id ASC",
-           1, NULL);
-
-  /* Query the 'refunds' by coin public key */
-  PREPARE ("get_refunds_by_coin",
-           "SELECT"
-           " merchant_pub"
-           ",merchant_sig"
-           ",h_contract_terms"
-           ",rtransaction_id"
-           ",amount_with_fee_val"
-           ",amount_with_fee_frac"
-           ",amount_with_fee_curr"
-           ",denom.fee_refund_val "
-           ",denom.fee_refund_frac "
-           ",denom.fee_refund_curr "
-           " FROM refunds"
-           "    JOIN known_coins USING (coin_pub)"
-           "    JOIN denominations denom USING (denom_pub_hash)"
-           " WHERE coin_pub=$1",
-           1, NULL);
-
-  /* Used in #postgres_insert_transfer_public_key() to
-     store commitments */
-  PREPARE ("insert_transfer_public_key",
-           "INSERT INTO refresh_transfer_public_key "
-           "(session_hash"
-           ",transfer_pub"
-           ") VALUES "
-           "($1, $2);",
-           3, NULL);
-
-  /* Used in #postgres_get_refresh_transfer_public_key() to
-     retrieve original commitments during /refresh/reveal */
-  PREPARE ("get_refresh_transfer_public_key",
-           "SELECT"
-           " transfer_pub"
-           " FROM refresh_transfer_public_key"
-           " WHERE session_hash=$1",
-           1, NULL);
-
-  /* Used in #postgres_insert_refresh_commit_coins() to
-     store coin commitments. */
-  PREPARE ("insert_refresh_commit_coin",
-           "INSERT INTO refresh_commit_coin "
-           "(session_hash"
-           ",newcoin_index"
-           ",coin_ev"
-           ") VALUES "
-           "($1, $2, $3);",
-           3, NULL);
-
-  /* Used in #postgres_get_refresh_commit_coins() to
-     retrieve the original coin envelopes, to either be
-     verified or signed. */
-  PREPARE ("get_refresh_commit_coin",
-           "SELECT"
-           " coin_ev"
-           " FROM refresh_commit_coin"
-           " WHERE session_hash=$1 AND newcoin_index=$2",
-           2, NULL);
-
-  /* Store information about a /deposit the exchange is to execute.
-     Used in #postgres_insert_deposit(). */
-  PREPARE ("insert_deposit",
-           "INSERT INTO deposits "
-           "(coin_pub"
-           ",amount_with_fee_val"
-           ",amount_with_fee_frac"
-           ",amount_with_fee_curr"
-           ",timestamp"
-           ",refund_deadline"
-           ",wire_deadline"
-           ",merchant_pub"
-           ",h_contract_terms"
-           ",h_wire"
-           ",coin_sig"
-           ",wire"
-           ") VALUES "
-           "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10,"
-           " $11, $12);",
-           12, NULL);
-
-  /* Used in #postgres_insert_refund() to store refund information */
-  PREPARE ("insert_refund",
-           "INSERT INTO refunds "
-           "(coin_pub "
-           ",merchant_pub "
-           ",merchant_sig "
-           ",h_contract_terms "
-           ",rtransaction_id "
-           ",amount_with_fee_val "
-           ",amount_with_fee_frac "
-           ",amount_with_fee_curr "
-           ") VALUES "
-           "($1, $2, $3, $4, $5, $6, $7, $8);",
-           8, NULL);
-
-  /* Fetch an existing deposit request, used to ensure idempotency
-     during /deposit processing. Used in #postgres_have_deposit(). */
-  PREPARE ("get_deposit",
-           "SELECT"
-           " amount_with_fee_val"
-           ",amount_with_fee_frac"
-           ",amount_with_fee_curr"
-           ",timestamp"
-           ",refund_deadline"
-           ",wire_deadline"
-           ",h_contract_terms"
-           ",h_wire"
-           " FROM deposits"
-           " WHERE ("
-           "  (coin_pub=$1) AND"
-           "  (h_contract_terms=$2) AND"
-           "  (merchant_pub=$3)"
-           " )",
-           3, NULL);
-
-  /* Fetch deposits with rowid '\geq' the given parameter */
-  PREPARE ("audit_get_deposits_incr",
-           "SELECT"
-           " amount_with_fee_val"
-           ",amount_with_fee_frac"
-           ",amount_with_fee_curr"
-           ",timestamp"
-          ",merchant_pub"
-           ",denom.denom_pub"
-          ",coin_pub"
-          ",coin_sig"
-           ",refund_deadline"
-           ",wire_deadline"
-           ",h_contract_terms"
-           ",wire"
-           ",done"
-           ",deposit_serial_id"
-           " FROM deposits"
-           "   JOIN known_coins USING (coin_pub)"
-           "    JOIN denominations denom USING (denom_pub_hash)"
-           " WHERE ("
-           "  (deposit_serial_id>=$1)"
-           " )"
-          " ORDER BY deposit_serial_id ASC",
-           1, NULL);
-
-  /* Fetch an existing deposit request.
-     Used in #postgres_wire_lookup_deposit_wtid(). */
-  PREPARE ("get_deposit_for_wtid",
-           "SELECT"
-           " amount_with_fee_val"
-           ",amount_with_fee_frac"
-           ",amount_with_fee_curr"
-           ",denom.fee_deposit_val"
-           ",denom.fee_deposit_frac"
-           ",denom.fee_deposit_curr"
-           ",wire_deadline"
-           " FROM deposits"
-           "    JOIN known_coins USING (coin_pub)"
-           "    JOIN denominations denom USING (denom_pub_hash)"
-           " WHERE ("
-           "  (coin_pub=$1) AND"
-           "  (merchant_pub=$2) AND"
-           "  (h_contract_terms=$3) AND"
-           "  (h_wire=$4)"
-           " )",
-           4, NULL);
-
-  /* Used in #postgres_get_ready_deposit() */
-  PREPARE ("deposits_get_ready",
-           "SELECT"
-           " deposit_serial_id"
-           ",amount_with_fee_val"
-           ",amount_with_fee_frac"
-           ",amount_with_fee_curr"
-           ",denom.fee_deposit_val"
-           ",denom.fee_deposit_frac"
-           ",denom.fee_deposit_curr"
-           ",wire_deadline"
-           ",h_contract_terms"
-           ",wire"
-           ",merchant_pub"
-           ",coin_pub"
-           " FROM deposits"
-           "    JOIN known_coins USING (coin_pub)"
-           "    JOIN denominations denom USING (denom_pub_hash)"
-           " WHERE"
-           " tiny=false AND"
-           " done=false AND"
-           " wire_deadline<=$1 AND"
-           " refund_deadline<$1"
-           " ORDER BY wire_deadline ASC"
-           " LIMIT 1",
-           1, NULL);
-
-  /* Used in #postgres_iterate_matching_deposits() */
-  PREPARE ("deposits_iterate_matching",
-           "SELECT"
-           " deposit_serial_id"
-           ",amount_with_fee_val"
-           ",amount_with_fee_frac"
-           ",amount_with_fee_curr"
-           ",denom.fee_deposit_val"
-           ",denom.fee_deposit_frac"
-           ",denom.fee_deposit_curr"
-           ",wire_deadline"
-           ",h_contract_terms"
-           ",coin_pub"
-           " FROM deposits"
-           "    JOIN known_coins USING (coin_pub)"
-           "    JOIN denominations denom USING (denom_pub_hash)"
-           " WHERE"
-           " merchant_pub=$1 AND"
-           " h_wire=$2 AND"
-           " done=false"
-           " ORDER BY wire_deadline ASC"
-           " LIMIT " TALER_EXCHANGEDB_MATCHING_DEPOSITS_LIMIT_STR,
-           2, NULL);
-
-  /* Used in #postgres_mark_deposit_tiny() */
-  PREPARE ("mark_deposit_tiny",
-           "UPDATE deposits"
-           " SET tiny=true"
-           " WHERE deposit_serial_id=$1",
-           1, NULL);
-
-  /* Used in #postgres_mark_deposit_done() */
-  PREPARE ("mark_deposit_done",
-           "UPDATE deposits"
-           " SET done=true"
-           " WHERE deposit_serial_id=$1",
-           1, NULL);
-
-  /* Used in #postgres_test_deposit_done() */
-  PREPARE ("test_deposit_done",
-           "SELECT done"
-           " FROM deposits"
-           " WHERE coin_pub=$1"
-           " AND merchant_pub=$2"
-           " AND h_contract_terms=$3"
-           " AND h_wire=$4",
-           5, NULL);
-
-  /* Used in #postgres_get_coin_transactions() to obtain information
-     about how a coin has been spend with /deposit requests. */
-  PREPARE ("get_deposit_with_coin_pub",
-           "SELECT"
-           " amount_with_fee_val"
-           ",amount_with_fee_frac"
-           ",amount_with_fee_curr"
-           ",denom.fee_deposit_val"
-           ",denom.fee_deposit_frac"
-           ",denom.fee_deposit_curr"
-           ",timestamp"
-           ",refund_deadline"
-           ",merchant_pub"
-           ",h_contract_terms"
-           ",h_wire"
-           ",wire"
-           ",coin_sig"
-           " FROM deposits"
-           "    JOIN known_coins USING (coin_pub)"
-           "    JOIN denominations denom USING (denom_pub_hash)"
-           " WHERE coin_pub=$1",
-           1, NULL);
-
-  /* Used in #postgres_insert_refresh_out() to store the
-     generated signature(s) for future requests, i.e. /refresh/link */
-  PREPARE ("insert_refresh_out",
-           "INSERT INTO refresh_out "
-           "(session_hash"
-           ",newcoin_index"
-           ",ev_sig"
-           ") VALUES "
-           "($1, $2, $3)",
-           3, NULL);
-
-  /* Used in #postgres_get_refresh_out() to test if the
-     generated signature(s) already exists */
-  PREPARE ("get_refresh_out",
-           "SELECT ev_sig"
-           " FROM refresh_out"
-           " WHERE session_hash=$1"
-           " AND newcoin_index=$2",
-           2, NULL);
-
-  /* Used in #postgres_get_link_data_list().  We use the session_hash
-     to obtain the "noreveal_index" for that session, and then select the
-     corresponding signatures (ev_sig) and the denomination keys from
-     the respective tables (namely refresh_melts and refresh_order)
-     using the session_hash as the primary filter (on join) and the
-     'noreveal_index' to constrain the selection on the commitment.
-     We also want to get the triplet for each of the newcoins, so we
-     have another constraint to ensure we get each triplet with
-     matching "newcoin_index" values.  NOTE: This may return many
-     results, both for different sessions and for the different coins
-     being exchangeed in the refresh ops.  NOTE: There may be more
-     efficient ways to express the same query.  */
-  PREPARE ("get_link",
-           "SELECT "
-          " ev_sig"
-          ",denoms.denom_pub"
-           " FROM refresh_sessions"
-           "     JOIN refresh_order ro USING (session_hash)"
-           "     JOIN refresh_commit_coin rcc USING (session_hash)"
-           "     JOIN refresh_out rc USING (session_hash)"
-           "     JOIN denominations denoms ON (ro.denom_pub_hash = 
denoms.denom_pub_hash)"
-           " WHERE ro.session_hash=$1"
-           "  AND ro.newcoin_index=rcc.newcoin_index"
-           "  AND ro.newcoin_index=rc.newcoin_index",
-           1, NULL);
-
-  /* Used in #postgres_get_transfer().  Given the public key of a
-     melted coin, we obtain the corresponding encrypted link secret
-     and the transfer public key.  This is done by first finding
-     the session_hash(es) of all sessions the coin was melted into,
-     and then constraining the result to the selected "noreveal_index".
-     NOTE: This may (in theory) return multiple results, one per session
-     that the old coin was melted into. */
-  PREPARE ("get_transfer",
-           "SELECT transfer_pub,session_hash"
-           " FROM refresh_sessions rs"
-           "     JOIN refresh_transfer_public_key rcl USING (session_hash)"
-           " WHERE rs.old_coin_pub=$1",
-           1, NULL);
-
-  /* Used in #postgres_lookup_wire_transfer */
-  PREPARE ("lookup_transactions",
-           "SELECT"
-           " aggregation_serial_id"
-           ",deposits.h_contract_terms"
-           ",deposits.wire"
-           ",deposits.h_wire"
-           ",deposits.coin_pub"
-           ",deposits.merchant_pub"
-           ",wire_out.execution_date"
-           ",deposits.amount_with_fee_val"
-           ",deposits.amount_with_fee_frac"
-           ",deposits.amount_with_fee_curr"
-           ",denom.fee_deposit_val"
-           ",denom.fee_deposit_frac"
-           ",denom.fee_deposit_curr"
-           " FROM aggregation_tracking"
-           "    JOIN deposits USING (deposit_serial_id)"
-           "    JOIN known_coins USING (coin_pub)"
-           "    JOIN denominations denom USING (denom_pub_hash)"
-           "    JOIN wire_out USING (wtid_raw)"
-           " WHERE wtid_raw=$1",
-           1, NULL);
-
-  /* Used in #postgres_wire_lookup_deposit_wtid */
-  PREPARE ("lookup_deposit_wtid",
-           "SELECT"
-           " aggregation_tracking.wtid_raw"
-           ",wire_out.execution_date"
-           ",amount_with_fee_val"
-           ",amount_with_fee_frac"
-           ",amount_with_fee_curr"
-           ",denom.fee_deposit_val"
-           ",denom.fee_deposit_frac"
-           ",denom.fee_deposit_curr"
-           " FROM deposits"
-           "    JOIN aggregation_tracking USING (deposit_serial_id)"
-           "    JOIN known_coins USING (coin_pub)"
-           "    JOIN denominations denom USING (denom_pub_hash)"
-           "    JOIN wire_out USING (wtid_raw)"
-           " WHERE coin_pub=$1"
-           "  AND h_contract_terms=$2"
-           "  AND h_wire=$3"
-           "  AND merchant_pub=$4",
-           4, NULL);
-
-  /* Used in #postgres_insert_aggregation_tracking */
-  PREPARE ("insert_aggregation_tracking",
-           "INSERT INTO aggregation_tracking "
-           "(deposit_serial_id"
-           ",wtid_raw"
-           ") VALUES "
-           "($1, $2)",
-           2, NULL);
-
-  /* Used in #postgres_get_wire_fee() */
-  PREPARE ("get_wire_fee",
-           "SELECT "
-           " start_date"
-           ",end_date"
-           ",wire_fee_val"
-           ",wire_fee_frac"
-           ",wire_fee_curr"
-           ",master_sig"
-           " FROM wire_fee"
-           " WHERE wire_method=$1"
-           " AND start_date <= $2"
-           " AND end_date > $2",
-           2, NULL);
-
-  /* Used in #postgres_insert_wire_fee */
-  PREPARE ("insert_wire_fee",
-           "INSERT INTO wire_fee "
-           "(wire_method"
-           ",start_date"
-           ",end_date"
-           ",wire_fee_val"
-           ",wire_fee_frac"
-           ",wire_fee_curr"
-           ",master_sig"
-           ") VALUES "
-           "($1, $2, $3, $4, $5, $6, $7)",
-           7, NULL);
-
-  /* Used in #postgres_store_wire_transfer_out */
-  PREPARE ("insert_wire_out",
-           "INSERT INTO wire_out "
-           "(execution_date"
-           ",wtid_raw"
-           ",wire_target"
-           ",amount_val"
-           ",amount_frac"
-           ",amount_curr"
-           ") VALUES "
-           "($1, $2, $3, $4, $5, $6)",
-           6, NULL);
-
-  /* Used in #postgres_wire_prepare_data_insert() to store
-     wire transfer information before actually committing it with the bank */
-  PREPARE ("wire_prepare_data_insert",
-           "INSERT INTO prewire "
-           "(type"
-           ",buf"
-           ") VALUES "
-           "($1, $2)",
-           2, NULL);
-
-  /* Used in #postgres_wire_prepare_data_mark_finished() */
-  PREPARE ("wire_prepare_data_mark_done",
-           "UPDATE prewire"
-           " SET finished=true"
-           " WHERE prewire_uuid=$1",
-           1, NULL);
-
-  /* Used in #postgres_wire_prepare_data_get() */
-  PREPARE ("wire_prepare_data_get",
-           "SELECT"
-           " prewire_uuid"
-           ",type"
-           ",buf"
-           " FROM prewire"
-           " WHERE finished=false"
-           " ORDER BY prewire_uuid ASC"
-           " LIMIT 1",
-           0, NULL);
-
-  /* Used in #postgres_gc() */
-  PREPARE ("gc_prewire",
-           "DELETE"
-           " FROM prewire"
-           " WHERE finished=true",
-           0, NULL);
-
-  /* Used in #postgres_select_wire_out_above_serial_id() */
-  PREPARE ("audit_get_wire_incr",
-           "SELECT"
-           " wireout_uuid"
-           ",execution_date"
-           ",wtid_raw"
-           ",wire_target"
-           ",amount_val"
-           ",amount_frac"
-           ",amount_curr"
-           " FROM wire_out"
-           " WHERE wireout_uuid>=$1"
-           " ORDER BY wireout_uuid ASC",
-           1, NULL);
-
-  /* Used in #postgres_insert_payback_request() to store payback
-     information */
-  PREPARE ("payback_insert",
-           "INSERT INTO payback "
-           "(reserve_pub"
-           ",coin_pub"
-           ",coin_sig"
-           ",coin_blind"
-           ",amount_val"
-           ",amount_frac"
-           ",amount_curr"
-           ",timestamp"
-           ",h_blind_ev"
-           ") VALUES "
-           "($1, $2, $3, $4, $5, $6, $7, $8, $9)",
-           9, NULL);
-
-  /* Used in #postgres_select_payback_above_serial_id() to obtain payback 
transactions */
-  PREPARE ("payback_get_incr",
-           "SELECT"
-           " payback_uuid"
-           ",timestamp"
-           ",reserve_pub"
-           ",coin_pub"
-           ",coin_sig"
-           ",coin_blind"
-           ",h_blind_ev"
-           ",denoms.denom_pub"
-           ",coins.denom_sig"
-           ",amount_val"
-           ",amount_frac"
-           ",amount_curr"
-           " FROM payback"
-           "    JOIN known_coins coins USING (coin_pub)"
-           "    JOIN denominations denoms USING (denom_pub_hash)"
-           " WHERE payback_uuid>=$1"
-           " ORDER BY payback_uuid ASC",
-           1, NULL);
-
+  struct GNUNET_PQ_PreparedStatement ps[] = {
+    /* Used in #postgres_insert_denomination_info() */
+    GNUNET_PQ_make_prepare ("denomination_insert",
+                            "INSERT INTO denominations "
+                            "(denom_pub_hash"
+                            ",denom_pub"
+                            ",master_pub"
+                            ",master_sig"
+                            ",valid_from"
+                            ",expire_withdraw"
+                            ",expire_deposit"
+                            ",expire_legal"
+                            ",coin_val" /* value of this denom */
+                            ",coin_frac" /* fractional value of this denom */
+                            ",coin_curr" /* assuming same currency for fees */
+                            ",fee_withdraw_val"
+                            ",fee_withdraw_frac"
+                            ",fee_withdraw_curr" /* must match coin_curr */
+                            ",fee_deposit_val"
+                            ",fee_deposit_frac"
+                            ",fee_deposit_curr"  /* must match coin_curr */
+                            ",fee_refresh_val"
+                            ",fee_refresh_frac"
+                            ",fee_refresh_curr" /* must match coin_curr */
+                            ",fee_refund_val"
+                            ",fee_refund_frac"
+                            ",fee_refund_curr" /* must match coin_curr */
+                            ") VALUES "
+                            "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10,"
+                            " $11, $12, $13, $14, $15, $16, $17, $18,"
+                            " $19, $20, $21, $22, $23);",
+                            23),
+    /* Used in #postgres_get_denomination_info() */
+    GNUNET_PQ_make_prepare ("denomination_get",
+                            "SELECT"
+                            " master_pub"
+                            ",master_sig"
+                            ",valid_from"
+                            ",expire_withdraw"
+                            ",expire_deposit"
+                            ",expire_legal"
+                            ",coin_val"  /* value of this denom */
+                            ",coin_frac" /* fractional value of this denom */
+                            ",coin_curr" /* assuming same currency for fees */
+                            ",fee_withdraw_val"
+                            ",fee_withdraw_frac"
+                            ",fee_withdraw_curr" /* must match coin_curr */
+                            ",fee_deposit_val"
+                            ",fee_deposit_frac"
+                            ",fee_deposit_curr"  /* must match coin_curr */
+                            ",fee_refresh_val"
+                            ",fee_refresh_frac"
+                            ",fee_refresh_curr" /* must match coin_curr */
+                            ",fee_refund_val"
+                            ",fee_refund_frac"
+                            ",fee_refund_curr" /* must match coin_curr */
+                            " FROM denominations"
+                            " WHERE denom_pub=$1;",
+                            1),
+    /* Used in #postgres_insert_denomination_revocation() */
+    GNUNET_PQ_make_prepare ("denomination_revocation_insert",
+                            "INSERT INTO denomination_revocations "
+                            "(denom_pub_hash"
+                            ",master_sig"
+                            ") VALUES "
+                            "($1, $2);",
+                            2),
+    /* Used in #postgres_get_denomination_revocation() */
+    GNUNET_PQ_make_prepare ("denomination_revocation_get",
+                            "SELECT"
+                            " master_sig"
+                            ",denom_revocations_serial_id"
+                            " FROM denomination_revocations"
+                            " WHERE denom_pub_hash=$1;",
+                            1),
+    /* Used in #postgres_reserve_get() */
+    GNUNET_PQ_make_prepare ("reserve_get",
+                            "SELECT"
+                            " current_balance_val"
+                            ",current_balance_frac"
+                            ",current_balance_curr"
+                            ",expiration_date"
+                            " FROM reserves"
+                            " WHERE reserve_pub=$1"
+                            " LIMIT 1;",
+                            1),
+    /* Used in #postgres_reserves_in_insert() when the reserve is new */
+    GNUNET_PQ_make_prepare ("reserve_create",
+                            "INSERT INTO reserves "
+                            "(reserve_pub"
+                            ",account_details"
+                            ",current_balance_val"
+                            ",current_balance_frac"
+                            ",current_balance_curr"
+                            ",expiration_date"
+                            ") VALUES "
+                            "($1, $2, $3, $4, $5, $6);",
+                            6),
+    /* Used in #postgres_insert_reserve_closed() */
+    GNUNET_PQ_make_prepare ("reserves_close_insert",
+                            "INSERT INTO reserves_close "
+                            "(reserve_pub"
+                            ",execution_date"
+                            ",wtid"
+                            ",receiver_account"
+                            ",amount_val"
+                            ",amount_frac"
+                            ",amount_curr"
+                            ",closing_fee_val"
+                            ",closing_fee_frac"
+                            ",closing_fee_curr"
+                            ") VALUES "
+                            "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10);",
+                            10),
+    /* Used in #postgres_reserves_update() when the reserve is updated */
+    GNUNET_PQ_make_prepare ("reserve_update",
+                            "UPDATE reserves"
+                            " SET"
+                            " expiration_date=$1 "
+                            ",current_balance_val=$2 "
+                            ",current_balance_frac=$3"
+                            " WHERE current_balance_curr=$4"
+                            " AND reserve_pub=$5;",
+                            5),
+    /* Used in #postgres_reserves_in_insert() to store transaction details */
+    GNUNET_PQ_make_prepare ("reserves_in_add_transaction",
+                            "INSERT INTO reserves_in "
+                            "(reserve_pub"
+                            ",wire_reference"
+                            ",credit_val"
+                            ",credit_frac"
+                            ",credit_curr"
+                            ",sender_account_details"
+                            ",execution_date"
+                            ") VALUES "
+                            "($1, $2, $3, $4, $5, $6, $7);",
+                            7),
+    /* Used in postgres_select_reserves_in_above_serial_id() to obtain inbound
+       transactions for reserves with serial id '\geq' the given parameter */
+    GNUNET_PQ_make_prepare ("reserves_in_get_latest_wire_reference",
+                            "SELECT"
+                            " wire_reference"
+                            " FROM reserves_in"
+                            " ORDER BY reserve_in_serial_id DESC"
+                            " LIMIT 1;",
+                            0),
+    /* Used in postgres_select_reserves_in_above_serial_id() to obtain inbound
+       transactions for reserves with serial id '\geq' the given parameter */
+    GNUNET_PQ_make_prepare ("audit_reserves_in_get_transactions_incr",
+                            "SELECT"
+                            " reserve_pub"
+                            ",wire_reference"
+                            ",credit_val"
+                            ",credit_frac"
+                            ",credit_curr"
+                            ",execution_date"
+                            ",sender_account_details"
+                            ",reserve_in_serial_id"
+                            " FROM reserves_in"
+                            " WHERE reserve_in_serial_id>=$1"
+                            " ORDER BY reserve_in_serial_id;",
+                            1),
+    /* Used in #postgres_get_reserve_history() to obtain inbound transactions
+       for a reserve */
+    GNUNET_PQ_make_prepare ("reserves_in_get_transactions",
+                            "SELECT"
+                            " wire_reference"
+                            ",credit_val"
+                            ",credit_frac"
+                            ",credit_curr"
+                            ",execution_date"
+                            ",sender_account_details"
+                            " FROM reserves_in"
+                            " WHERE reserve_pub=$1;",
+                            1),
+    /* Used in #postgres_insert_withdraw_info() to store
+       the signature of a blinded coin with the blinded coin's
+       details before returning it during /reserve/withdraw. We store
+       the coin's denomination information (public key, signature)
+       and the blinded message as well as the reserve that the coin
+       is being withdrawn from and the signature of the message
+       authorizing the withdrawal. */
+    GNUNET_PQ_make_prepare ("insert_withdraw_info",
+                            "INSERT INTO reserves_out "
+                            "(h_blind_ev"
+                            ",denom_pub_hash"
+                            ",denom_sig"
+                            ",reserve_pub"
+                            ",reserve_sig"
+                            ",execution_date"
+                            ",amount_with_fee_val"
+                            ",amount_with_fee_frac"
+                            ",amount_with_fee_curr"
+                            ") VALUES "
+                            "($1, $2, $3, $4, $5, $6, $7, $8, $9);",
+                            9),
+    /* Used in #postgres_get_withdraw_info() to
+       locate the response for a /reserve/withdraw request
+       using the hash of the blinded message.  Used to
+       make sure /reserve/withdraw requests are idempotent. */
+    GNUNET_PQ_make_prepare ("get_withdraw_info",
+                            "SELECT"
+                            " denom.denom_pub"
+                            ",denom_sig"
+                            ",reserve_sig"
+                            ",reserve_pub"
+                            ",execution_date"
+                            ",amount_with_fee_val"
+                            ",amount_with_fee_frac"
+                            ",amount_with_fee_curr"
+                            ",denom.fee_withdraw_val"
+                            ",denom.fee_withdraw_frac"
+                            ",denom.fee_withdraw_curr"
+                            " FROM reserves_out"
+                            "    JOIN denominations denom"
+                            "      USING (denom_pub_hash)"
+                            " WHERE h_blind_ev=$1;",
+                            1),
+    /* Used during #postgres_get_reserve_history() to
+       obtain all of the /reserve/withdraw operations that
+       have been performed on a given reserve. (i.e. to
+       demonstrate double-spending) */
+    GNUNET_PQ_make_prepare ("get_reserves_out",
+                            "SELECT"
+                            " h_blind_ev"
+                            ",denom.denom_pub"
+                            ",denom_sig"
+                            ",reserve_sig"
+                            ",execution_date"
+                            ",amount_with_fee_val"
+                            ",amount_with_fee_frac"
+                            ",amount_with_fee_curr"
+                            ",denom.fee_withdraw_val"
+                            ",denom.fee_withdraw_frac"
+                            ",denom.fee_withdraw_curr"
+                            " FROM reserves_out"
+                            "    JOIN denominations denom"
+                            "      USING (denom_pub_hash)"
+                            " WHERE reserve_pub=$1;",
+                            1),
+    /* Used in #postgres_select_reserves_out_above_serial_id() */
+    GNUNET_PQ_make_prepare ("audit_get_reserves_out_incr",
+                            "SELECT"
+                            " h_blind_ev"
+                            ",denom.denom_pub"
+                            ",denom_sig"
+                            ",reserve_sig"
+                            ",reserve_pub"
+                            ",execution_date"
+                            ",amount_with_fee_val"
+                            ",amount_with_fee_frac"
+                            ",amount_with_fee_curr"
+                            ",reserve_out_serial_id"
+                            " FROM reserves_out"
+                            "    JOIN denominations denom"
+                            "      USING (denom_pub_hash)"
+                            " WHERE reserve_out_serial_id>=$1"
+                            " ORDER BY reserve_out_serial_id ASC;",
+                            1),
+    /* Used in #postgres_get_refresh_session() to fetch
+       high-level information about a refresh session */
+    GNUNET_PQ_make_prepare ("get_refresh_session",
+                            "SELECT"
+                            " old_coin_pub"
+                            ",old_coin_sig"
+                            ",amount_with_fee_val"
+                            ",amount_with_fee_frac"
+                            ",amount_with_fee_curr"
+                            ",denom.fee_refresh_val "
+                            ",denom.fee_refresh_frac "
+                            ",denom.fee_refresh_curr "
+                            ",num_newcoins"
+                            ",noreveal_index"
+                            " FROM refresh_sessions"
+                            "    JOIN known_coins"
+                            "      ON (refresh_sessions.old_coin_pub = 
known_coins.coin_pub)"
+                            "    JOIN denominations denom"
+                            "      USING (denom_pub_hash)"
+                            " WHERE session_hash=$1;",
+                            1),
+    /* Used in #postgres_select_refreshs_above_serial_id() to fetch
+       refresh session with id '\geq' the given parameter */
+    GNUNET_PQ_make_prepare ("audit_get_refresh_sessions_incr",
+                            "SELECT"
+                            " denom.denom_pub"
+                            ",old_coin_pub"
+                            ",old_coin_sig"
+                            ",amount_with_fee_val"
+                            ",amount_with_fee_frac"
+                            ",amount_with_fee_curr"
+                            ",num_newcoins"
+                            ",noreveal_index"
+                            ",melt_serial_id"
+                            ",session_hash"
+                            " FROM refresh_sessions"
+                            "   JOIN known_coins kc"
+                            "     ON (refresh_sessions.old_coin_pub = 
kc.coin_pub)"
+                            "   JOIN denominations denom"
+                            "     ON (kc.denom_pub_hash = 
denom.denom_pub_hash)"
+                            " WHERE melt_serial_id>=$1"
+                            " ORDER BY melt_serial_id ASC;",
+                            1),
+    /* Used in #postgres_create_refresh_session() to store
+       high-level information about a refresh session */
+    GNUNET_PQ_make_prepare ("insert_refresh_session",
+                            "INSERT INTO refresh_sessions "
+                            "(session_hash "
+                            ",old_coin_pub "
+                            ",old_coin_sig "
+                            ",amount_with_fee_val "
+                            ",amount_with_fee_frac "
+                            ",amount_with_fee_curr "
+                            ",num_newcoins "
+                            ",noreveal_index "
+                            ") VALUES "
+                            "($1, $2, $3, $4, $5, $6, $7, $8);",
+                            8),
+    /* Used in #postgres_get_known_coin() to fetch
+       the denomination public key and signature for
+       a coin known to the exchange. */
+    GNUNET_PQ_make_prepare ("get_known_coin",
+                            "SELECT"
+                            " denom.denom_pub"
+                            ",denom_sig"
+                            " FROM known_coins"
+                            "    JOIN denominations denom"
+                            "      USING (denom_pub_hash)"
+                            " WHERE coin_pub=$1;",
+                            1),
+    /* Used in #postgres_insert_known_coin() to store
+       the denomination public key and signature for
+       a coin known to the exchange. */
+    GNUNET_PQ_make_prepare ("insert_known_coin",
+                            "INSERT INTO known_coins "
+                            "(coin_pub"
+                            ",denom_pub_hash"
+                            ",denom_sig"
+                            ") VALUES "
+                            "($1,$2,$3);",
+                            3),
+    /* Store information about the desired denominations for a
+       refresh operation, used in #postgres_insert_refresh_order() */
+    GNUNET_PQ_make_prepare ("insert_refresh_order",
+                            "INSERT INTO refresh_order "
+                            "(newcoin_index "
+                            ",session_hash "
+                            ",denom_pub_hash "
+                            ") VALUES "
+                            "($1, $2, $3);",
+                            3),
+    /* Obtain information about the desired denominations for a
+       refresh operation, used in #postgres_get_refresh_order() */
+    GNUNET_PQ_make_prepare ("get_refresh_order",
+                            "SELECT denom_pub"
+                            " FROM refresh_order"
+                            "    JOIN denominations denom "
+                            "      USING (denom_pub_hash)"
+                            " WHERE session_hash=$1"
+                            "   AND newcoin_index=$2;",
+                            2),
+    /* Query the 'refresh_sessions' by coin public key */
+    GNUNET_PQ_make_prepare ("get_refresh_session_by_coin",
+                            "SELECT"
+                            " session_hash"
+                            ",old_coin_sig"
+                            ",amount_with_fee_val"
+                            ",amount_with_fee_frac"
+                            ",amount_with_fee_curr"
+                            ",denom.fee_refresh_val "
+                            ",denom.fee_refresh_frac "
+                            ",denom.fee_refresh_curr "
+                            " FROM refresh_sessions"
+                            "    JOIN known_coins "
+                            "      ON (refresh_sessions.old_coin_pub = 
known_coins.coin_pub)"
+                            "    JOIN denominations denom USING 
(denom_pub_hash)"
+                            " WHERE old_coin_pub=$1;",
+                            1),
+    /* Fetch refunds with rowid '\geq' the given parameter */
+    GNUNET_PQ_make_prepare ("audit_get_refunds_incr",
+                            "SELECT"
+                            " merchant_pub"
+                            ",merchant_sig"
+                            ",h_contract_terms"
+                            ",rtransaction_id"
+                            ",denom.denom_pub"
+                            ",coin_pub"
+                            ",amount_with_fee_val"
+                            ",amount_with_fee_frac"
+                            ",amount_with_fee_curr"
+                            ",refund_serial_id"
+                            " FROM refunds"
+                            "   JOIN known_coins kc USING (coin_pub)"
+                            "   JOIN denominations denom ON (kc.denom_pub_hash 
= denom.denom_pub_hash)"
+                            " WHERE refund_serial_id>=$1"
+                            " ORDER BY refund_serial_id ASC;",
+                            1),
+    /* Query the 'refunds' by coin public key */
+    GNUNET_PQ_make_prepare ("get_refunds_by_coin",
+                            "SELECT"
+                            " merchant_pub"
+                            ",merchant_sig"
+                            ",h_contract_terms"
+                            ",rtransaction_id"
+                            ",amount_with_fee_val"
+                            ",amount_with_fee_frac"
+                            ",amount_with_fee_curr"
+                            ",denom.fee_refund_val "
+                            ",denom.fee_refund_frac "
+                            ",denom.fee_refund_curr "
+                            " FROM refunds"
+                            "    JOIN known_coins USING (coin_pub)"
+                            "    JOIN denominations denom USING 
(denom_pub_hash)"
+                            " WHERE coin_pub=$1;",
+                            1),
+    /* Used in #postgres_insert_transfer_public_key() to
+       store commitments */
+    GNUNET_PQ_make_prepare ("insert_transfer_public_key",
+                            "INSERT INTO refresh_transfer_public_key "
+                            "(session_hash"
+                            ",transfer_pub"
+                            ") VALUES "
+                            "($1, $2);",
+                            2),
+    /* Used in #postgres_get_refresh_transfer_public_key() to
+       retrieve original commitments during /refresh/reveal */
+    GNUNET_PQ_make_prepare ("get_refresh_transfer_public_key",
+                            "SELECT"
+                            " transfer_pub"
+                            " FROM refresh_transfer_public_key"
+                            " WHERE session_hash=$1;",
+                            1),
+    /* Used in #postgres_insert_refresh_commit_coins() to
+       store coin commitments. */
+    GNUNET_PQ_make_prepare ("insert_refresh_commit_coin",
+                            "INSERT INTO refresh_commit_coin "
+                            "(session_hash"
+                            ",newcoin_index"
+                            ",coin_ev"
+                            ") VALUES "
+                            "($1, $2, $3);",
+                            3),
+    /* Used in #postgres_get_refresh_commit_coins() to
+       retrieve the original coin envelopes, to either be
+       verified or signed. */
+    GNUNET_PQ_make_prepare ("get_refresh_commit_coin",
+                            "SELECT"
+                            " coin_ev"
+                            " FROM refresh_commit_coin"
+                            " WHERE session_hash=$1 AND newcoin_index=$2;",
+                            2),
+    /* Store information about a /deposit the exchange is to execute.
+       Used in #postgres_insert_deposit(). */
+    GNUNET_PQ_make_prepare ("insert_deposit",
+                            "INSERT INTO deposits "
+                            "(coin_pub"
+                            ",amount_with_fee_val"
+                            ",amount_with_fee_frac"
+                            ",amount_with_fee_curr"
+                            ",timestamp"
+                            ",refund_deadline"
+                            ",wire_deadline"
+                            ",merchant_pub"
+                            ",h_contract_terms"
+                            ",h_wire"
+                            ",coin_sig"
+                            ",wire"
+                            ") VALUES "
+                            "($1, $2, $3, $4, $5, $6, $7, $8, $9, $10,"
+                            " $11, $12);",
+                            12),
+    /* Used in #postgres_insert_refund() to store refund information */
+    GNUNET_PQ_make_prepare ("insert_refund",
+                            "INSERT INTO refunds "
+                            "(coin_pub "
+                            ",merchant_pub "
+                            ",merchant_sig "
+                            ",h_contract_terms "
+                            ",rtransaction_id "
+                            ",amount_with_fee_val "
+                            ",amount_with_fee_frac "
+                            ",amount_with_fee_curr "
+                            ") VALUES "
+                            "($1, $2, $3, $4, $5, $6, $7, $8);",
+                            8),
+    /* Fetch an existing deposit request, used to ensure idempotency
+       during /deposit processing. Used in #postgres_have_deposit(). */
+    GNUNET_PQ_make_prepare ("get_deposit",
+                            "SELECT"
+                            " amount_with_fee_val"
+                            ",amount_with_fee_frac"
+                            ",amount_with_fee_curr"
+                            ",timestamp"
+                            ",refund_deadline"
+                            ",wire_deadline"
+                            ",h_contract_terms"
+                            ",h_wire"
+                            " FROM deposits"
+                            " WHERE ("
+                            "        (coin_pub=$1)"
+                            "    AND (h_contract_terms=$2)"
+                            "    AND (merchant_pub=$3)"
+                            " );",
+                            3),
+    /* Fetch deposits with rowid '\geq' the given parameter */
+    GNUNET_PQ_make_prepare ("audit_get_deposits_incr",
+                            "SELECT"
+                            " amount_with_fee_val"
+                            ",amount_with_fee_frac"
+                            ",amount_with_fee_curr"
+                            ",timestamp"
+                            ",merchant_pub"
+                            ",denom.denom_pub"
+                            ",coin_pub"
+                            ",coin_sig"
+                            ",refund_deadline"
+                            ",wire_deadline"
+                            ",h_contract_terms"
+                            ",wire"
+                            ",done"
+                            ",deposit_serial_id"
+                            " FROM deposits"
+                            "    JOIN known_coins USING (coin_pub)"
+                            "    JOIN denominations denom USING 
(denom_pub_hash)"
+                            " WHERE ("
+                            "  (deposit_serial_id>=$1)"
+                            " )"
+                            " ORDER BY deposit_serial_id ASC;",
+                            1),
+    /* Fetch an existing deposit request.
+       Used in #postgres_wire_lookup_deposit_wtid(). */
+    GNUNET_PQ_make_prepare ("get_deposit_for_wtid",
+                            "SELECT"
+                            " amount_with_fee_val"
+                            ",amount_with_fee_frac"
+                            ",amount_with_fee_curr"
+                            ",denom.fee_deposit_val"
+                            ",denom.fee_deposit_frac"
+                            ",denom.fee_deposit_curr"
+                            ",wire_deadline"
+                            " FROM deposits"
+                            "    JOIN known_coins USING (coin_pub)"
+                            "    JOIN denominations denom USING 
(denom_pub_hash)"
+                            " WHERE ("
+                            "      (coin_pub=$1)"
+                            "    AND (merchant_pub=$2)"
+                            "    AND (h_contract_terms=$3)"
+                            "    AND (h_wire=$4)"
+                            " );",
+                            4),
+    /* Used in #postgres_get_ready_deposit() */
+    GNUNET_PQ_make_prepare ("deposits_get_ready",
+                            "SELECT"
+                            " deposit_serial_id"
+                            ",amount_with_fee_val"
+                            ",amount_with_fee_frac"
+                            ",amount_with_fee_curr"
+                            ",denom.fee_deposit_val"
+                            ",denom.fee_deposit_frac"
+                            ",denom.fee_deposit_curr"
+                            ",wire_deadline"
+                            ",h_contract_terms"
+                            ",wire"
+                            ",merchant_pub"
+                            ",coin_pub"
+                            " FROM deposits"
+                            "    JOIN known_coins USING (coin_pub)"
+                            "    JOIN denominations denom USING 
(denom_pub_hash)"
+                            " WHERE tiny=false"
+                            "    AND done=false"
+                            "    AND wire_deadline<=$1"
+                            "    AND refund_deadline<$1"
+                            " ORDER BY wire_deadline ASC"
+                            " LIMIT 1;",
+                            1),
+    /* Used in #postgres_iterate_matching_deposits() */
+    GNUNET_PQ_make_prepare ("deposits_iterate_matching",
+                            "SELECT"
+                            " deposit_serial_id"
+                            ",amount_with_fee_val"
+                            ",amount_with_fee_frac"
+                            ",amount_with_fee_curr"
+                            ",denom.fee_deposit_val"
+                            ",denom.fee_deposit_frac"
+                            ",denom.fee_deposit_curr"
+                            ",wire_deadline"
+                            ",h_contract_terms"
+                            ",coin_pub"
+                            " FROM deposits"
+                            "    JOIN known_coins"
+                            "      USING (coin_pub)"
+                            "    JOIN denominations denom"
+                            "      USING (denom_pub_hash)"
+                            " WHERE"
+                            " merchant_pub=$1 AND"
+                            " h_wire=$2 AND"
+                            " done=false"
+                            " ORDER BY wire_deadline ASC"
+                            " LIMIT " 
TALER_EXCHANGEDB_MATCHING_DEPOSITS_LIMIT_STR ";",
+                            2),
+    /* Used in #postgres_mark_deposit_tiny() */
+    GNUNET_PQ_make_prepare ("mark_deposit_tiny",
+                            "UPDATE deposits"
+                            " SET tiny=true"
+                            " WHERE deposit_serial_id=$1",
+                            1),
+    /* Used in #postgres_mark_deposit_done() */
+    GNUNET_PQ_make_prepare ("mark_deposit_done",
+                            "UPDATE deposits"
+                            " SET done=true"
+                            " WHERE deposit_serial_id=$1;",
+                            1),
+    /* Used in #postgres_test_deposit_done() */
+    GNUNET_PQ_make_prepare ("test_deposit_done",
+                            "SELECT done"
+                            " FROM deposits"
+                            " WHERE coin_pub=$1"
+                            "   AND merchant_pub=$2"
+                            "   AND h_contract_terms=$3"
+                            "   AND h_wire=$4;",
+                            5),
+    /* Used in #postgres_get_coin_transactions() to obtain information
+       about how a coin has been spend with /deposit requests. */
+    GNUNET_PQ_make_prepare ("get_deposit_with_coin_pub",
+                            "SELECT"
+                            " amount_with_fee_val"
+                            ",amount_with_fee_frac"
+                            ",amount_with_fee_curr"
+                            ",denom.fee_deposit_val"
+                            ",denom.fee_deposit_frac"
+                            ",denom.fee_deposit_curr"
+                            ",timestamp"
+                            ",refund_deadline"
+                            ",merchant_pub"
+                            ",h_contract_terms"
+                            ",h_wire"
+                            ",wire"
+                            ",coin_sig"
+                            " FROM deposits"
+                            "    JOIN known_coins"
+                            "      USING (coin_pub)"
+                            "    JOIN denominations denom"
+                            "      USING (denom_pub_hash)"
+                            " WHERE coin_pub=$1;",
+                            1),
+    /* Used in #postgres_insert_refresh_out() to store the
+       generated signature(s) for future requests, i.e. /refresh/link */
+    GNUNET_PQ_make_prepare ("insert_refresh_out",
+                            "INSERT INTO refresh_out "
+                            "(session_hash"
+                            ",newcoin_index"
+                            ",ev_sig"
+                            ") VALUES "
+                            "($1, $2, $3);",
+                            3),
+    /* Used in #postgres_get_refresh_out() to test if the
+       generated signature(s) already exists */
+    GNUNET_PQ_make_prepare ("get_refresh_out",
+                            "SELECT ev_sig"
+                            " FROM refresh_out"
+                            " WHERE session_hash=$1"
+                            " AND newcoin_index=$2;",
+                            2),
+    /* Used in #postgres_get_link_data_list().  We use the session_hash
+       to obtain the "noreveal_index" for that session, and then select the
+       corresponding signatures (ev_sig) and the denomination keys from
+       the respective tables (namely refresh_melts and refresh_order)
+       using the session_hash as the primary filter (on join) and the
+       'noreveal_index' to constrain the selection on the commitment.
+       We also want to get the triplet for each of the newcoins, so we
+       have another constraint to ensure we get each triplet with
+       matching "newcoin_index" values.  NOTE: This may return many
+       results, both for different sessions and for the different coins
+       being exchangeed in the refresh ops.  NOTE: There may be more
+       efficient ways to express the same query.  */
+    GNUNET_PQ_make_prepare ("get_link",
+                            "SELECT "
+                            " ev_sig"
+                            ",denoms.denom_pub"
+                            " FROM refresh_sessions"
+                            "     JOIN refresh_order ro"
+                            "       USING (session_hash)"
+                            "     JOIN refresh_commit_coin rcc"
+                            "       USING (session_hash)"
+                            "     JOIN refresh_out rc"
+                            "       USING (session_hash)"
+                            "     JOIN denominations denoms"
+                            "       ON (ro.denom_pub_hash = 
denoms.denom_pub_hash)"
+                            " WHERE ro.session_hash=$1"
+                            "  AND ro.newcoin_index=rcc.newcoin_index"
+                            "  AND ro.newcoin_index=rc.newcoin_index;",
+                            1),
+    /* Used in #postgres_get_transfer().  Given the public key of a
+       melted coin, we obtain the corresponding encrypted link secret
+       and the transfer public key.  This is done by first finding
+       the session_hash(es) of all sessions the coin was melted into,
+       and then constraining the result to the selected "noreveal_index".
+       NOTE: This may (in theory) return multiple results, one per session
+       that the old coin was melted into. */
+    GNUNET_PQ_make_prepare ("get_transfer",
+                            "SELECT transfer_pub,session_hash"
+                            " FROM refresh_sessions rs"
+                            "     JOIN refresh_transfer_public_key rcl"
+                            "       USING (session_hash)"
+                            " WHERE rs.old_coin_pub=$1;",
+                            1),
+    /* Used in #postgres_lookup_wire_transfer */
+    GNUNET_PQ_make_prepare ("lookup_transactions",
+                            "SELECT"
+                            " aggregation_serial_id"
+                            ",deposits.h_contract_terms"
+                            ",deposits.wire"
+                            ",deposits.h_wire"
+                            ",deposits.coin_pub"
+                            ",deposits.merchant_pub"
+                            ",wire_out.execution_date"
+                            ",deposits.amount_with_fee_val"
+                            ",deposits.amount_with_fee_frac"
+                            ",deposits.amount_with_fee_curr"
+                            ",denom.fee_deposit_val"
+                            ",denom.fee_deposit_frac"
+                            ",denom.fee_deposit_curr"
+                            " FROM aggregation_tracking"
+                            "    JOIN deposits"
+                            "      USING (deposit_serial_id)"
+                            "    JOIN known_coins"
+                            "      USING (coin_pub)"
+                            "    JOIN denominations denom"
+                            "      USING (denom_pub_hash)"
+                            "    JOIN wire_out"
+                            "      USING (wtid_raw)"
+                            " WHERE wtid_raw=$1;",
+                            1),
+    /* Used in #postgres_wire_lookup_deposit_wtid */
+    GNUNET_PQ_make_prepare ("lookup_deposit_wtid",
+                            "SELECT"
+                            " aggregation_tracking.wtid_raw"
+                            ",wire_out.execution_date"
+                            ",amount_with_fee_val"
+                            ",amount_with_fee_frac"
+                            ",amount_with_fee_curr"
+                            ",denom.fee_deposit_val"
+                            ",denom.fee_deposit_frac"
+                            ",denom.fee_deposit_curr"
+                            " FROM deposits"
+                            "    JOIN aggregation_tracking"
+                            "      USING (deposit_serial_id)"
+                            "    JOIN known_coins"
+                            "      USING (coin_pub)"
+                            "    JOIN denominations denom"
+                            "      USING (denom_pub_hash)"
+                            "    JOIN wire_out"
+                            "      USING (wtid_raw)"
+                            " WHERE coin_pub=$1"
+                            "  AND h_contract_terms=$2"
+                            "  AND h_wire=$3"
+                            "  AND merchant_pub=$4;",
+                            4),
+    /* Used in #postgres_insert_aggregation_tracking */
+    GNUNET_PQ_make_prepare ("insert_aggregation_tracking",
+                            "INSERT INTO aggregation_tracking "
+                            "(deposit_serial_id"
+                            ",wtid_raw"
+                            ") VALUES "
+                            "($1, $2);",
+                            2),
+    /* Used in #postgres_get_wire_fee() */
+    GNUNET_PQ_make_prepare ("get_wire_fee",
+                            "SELECT "
+                            " start_date"
+                            ",end_date"
+                            ",wire_fee_val"
+                            ",wire_fee_frac"
+                            ",wire_fee_curr"
+                            ",master_sig"
+                            " FROM wire_fee"
+                            " WHERE wire_method=$1"
+                            "   AND start_date <= $2"
+                            "   AND end_date > $2;",
+                            2),
+    /* Used in #postgres_insert_wire_fee */
+    GNUNET_PQ_make_prepare ("insert_wire_fee",
+                            "INSERT INTO wire_fee "
+                            "(wire_method"
+                            ",start_date"
+                            ",end_date"
+                            ",wire_fee_val"
+                            ",wire_fee_frac"
+                            ",wire_fee_curr"
+                            ",master_sig"
+                            ") VALUES "
+                            "($1, $2, $3, $4, $5, $6, $7);",
+                            7),
+    /* Used in #postgres_store_wire_transfer_out */
+    GNUNET_PQ_make_prepare ("insert_wire_out",
+                            "INSERT INTO wire_out "
+                            "(execution_date"
+                            ",wtid_raw"
+                            ",wire_target"
+                            ",amount_val"
+                            ",amount_frac"
+                            ",amount_curr"
+                            ") VALUES "
+                            "($1, $2, $3, $4, $5, $6);",
+                            6),
+    /* Used in #postgres_wire_prepare_data_insert() to store
+       wire transfer information before actually committing it with the bank */
+    GNUNET_PQ_make_prepare ("wire_prepare_data_insert",
+                            "INSERT INTO prewire "
+                            "(type"
+                            ",buf"
+                            ") VALUES "
+                            "($1, $2);",
+                            2),
+    /* Used in #postgres_wire_prepare_data_mark_finished() */
+    GNUNET_PQ_make_prepare ("wire_prepare_data_mark_done",
+                            "UPDATE prewire"
+                            " SET finished=true"
+                            " WHERE prewire_uuid=$1;",
+                            1),
+    /* Used in #postgres_wire_prepare_data_get() */
+    GNUNET_PQ_make_prepare ("wire_prepare_data_get",
+                            "SELECT"
+                            " prewire_uuid"
+                            ",type"
+                            ",buf"
+                            " FROM prewire"
+                            " WHERE finished=false"
+                            " ORDER BY prewire_uuid ASC"
+                            " LIMIT 1;",
+                            0),
+    /* Used in #postgres_gc() */
+    GNUNET_PQ_make_prepare ("gc_prewire",
+                            "DELETE"
+                            " FROM prewire"
+                            " WHERE finished=true;",
+                            0),
+    /* Used in #postgres_select_wire_out_above_serial_id() */
+    GNUNET_PQ_make_prepare ("audit_get_wire_incr",
+                            "SELECT"
+                            " wireout_uuid"
+                            ",execution_date"
+                            ",wtid_raw"
+                            ",wire_target"
+                            ",amount_val"
+                            ",amount_frac"
+                            ",amount_curr"
+                            " FROM wire_out"
+                            " WHERE wireout_uuid>=$1"
+                            " ORDER BY wireout_uuid ASC;",
+                            1),
+    /* Used in #postgres_insert_payback_request() to store payback
+       information */
+    GNUNET_PQ_make_prepare ("payback_insert",
+                            "INSERT INTO payback "
+                            "(reserve_pub"
+                            ",coin_pub"
+                            ",coin_sig"
+                            ",coin_blind"
+                            ",amount_val"
+                            ",amount_frac"
+                            ",amount_curr"
+                            ",timestamp"
+                            ",h_blind_ev"
+                            ") VALUES "
+                            "($1, $2, $3, $4, $5, $6, $7, $8, $9);",
+                            9),
+    /* Used in #postgres_select_payback_above_serial_id() to obtain payback 
transactions */
+    GNUNET_PQ_make_prepare ("payback_get_incr",
+                            "SELECT"
+                            " payback_uuid"
+                            ",timestamp"
+                            ",reserve_pub"
+                            ",coin_pub"
+                            ",coin_sig"
+                            ",coin_blind"
+                            ",h_blind_ev"
+                            ",denoms.denom_pub"
+                            ",coins.denom_sig"
+                            ",amount_val"
+                            ",amount_frac"
+                            ",amount_curr"
+                            " FROM payback"
+                            "    JOIN known_coins coins"
+                            "      USING (coin_pub)"
+                            "    JOIN denominations denoms"
+                            "      USING (denom_pub_hash)"
+                            " WHERE payback_uuid>=$1"
+                            " ORDER BY payback_uuid ASC;",
+                            1),
     /* Used in #postgres_select_reserve_closed_above_serial_id() to
        obtain information about closed reserves */
-  PREPARE ("reserves_close_get_incr",
-           "SELECT"
-           " close_uuid"
-           ",reserve_pub"
-           ",execution_date"
-           ",wtid"
-           ",receiver_account"
-           ",amount_val"
-           ",amount_frac"
-           ",amount_curr"
-           ",closing_fee_val"
-           ",closing_fee_frac"
-           ",closing_fee_curr"
-           " FROM reserves_close"
-           " WHERE close_uuid>=$1"
-           " ORDER BY close_uuid ASC",
-           1, NULL);
-
-  /* Used in #postgres_get_reserve_history() to obtain payback transactions
-     for a reserve */
-  PREPARE ("payback_by_reserve",
-           "SELECT"
-           " coin_pub"
-           ",coin_sig"
-           ",coin_blind"
-           ",amount_val"
-           ",amount_frac"
-           ",amount_curr"
-           ",timestamp"
-           ",denoms.denom_pub"
-           ",coins.denom_sig"
-           " FROM payback"
-           "    JOIN known_coins coins USING (coin_pub)"
-           "    JOIN denominations denoms USING (denom_pub_hash)"
-           " WHERE payback.reserve_pub=$1",
-           1, NULL);
-
-  /* Used in #postgres_get_reserve_history() */
-  PREPARE ("close_by_reserve",
-           "SELECT"
-           " amount_val"
-           ",amount_frac"
-           ",amount_curr"
-           ",closing_fee_val"
-           ",closing_fee_frac"
-           ",closing_fee_curr"
-          ",execution_date"
-          ",receiver_account"
-          ",wtid"
-           " FROM reserves_close"
-           " WHERE reserve_pub=$1;",
-           1, NULL);
-
-  /* Used in #postgres_get_expired_reserves() */
-  PREPARE ("get_expired_reserves",
-           "SELECT"
-          " expiration_date"
-          ",account_details"
-          ",reserve_pub"
-           ",current_balance_val"
-           ",current_balance_frac"
-           ",current_balance_curr"
-           " FROM reserves"
-           " WHERE expiration_date<=$1"
-          " AND (current_balance_val != 0 "
-          "      OR current_balance_frac != 0);",
-           1, NULL);
-
-  /* Used in #postgres_get_coin_transactions() to obtain payback transactions
-     for a coin */
-  PREPARE ("payback_by_coin",
-           "SELECT"
-           " payback.reserve_pub"
-           ",coin_sig"
-           ",coin_blind"
-           ",amount_val"
-           ",amount_frac"
-           ",amount_curr"
-           ",timestamp"
-           ",denoms.denom_pub"
-           ",coins.denom_sig"
-           " FROM payback"
-           "    JOIN known_coins coins USING (coin_pub)"
-           "    JOIN denominations denoms USING (denom_pub_hash)"
-           " WHERE payback.coin_pub=$1",
-           1, NULL);
-
-  /* Used in #postgres_get_reserve_by_h_blind() */
-  PREPARE ("reserve_by_h_blind",
-           "SELECT"
-           " reserve_pub"
-           " FROM reserves_out"
-           " WHERE h_blind_ev=$1"
-           " LIMIT 1;",
-           1, NULL);
-
-  PREPARE ("gc_denominations",
-           "DELETE"
-           " FROM denominations"
-           " WHERE expire_legal < $1",
-           1, NULL);
-  PREPARE ("gc_reserves",
-           "DELETE"
-           " FROM reserves"
-           " WHERE expiration_date < $1"
-           "   AND current_balance_val = 0"
-           "   AND current_balance_frac = 0",
-           1, NULL);
+    GNUNET_PQ_make_prepare ("reserves_close_get_incr",
+                            "SELECT"
+                            " close_uuid"
+                            ",reserve_pub"
+                            ",execution_date"
+                            ",wtid"
+                            ",receiver_account"
+                            ",amount_val"
+                            ",amount_frac"
+                            ",amount_curr"
+                            ",closing_fee_val"
+                            ",closing_fee_frac"
+                            ",closing_fee_curr"
+                            " FROM reserves_close"
+                            " WHERE close_uuid>=$1"
+                            " ORDER BY close_uuid ASC;",
+                            1),
+    /* Used in #postgres_get_reserve_history() to obtain payback transactions
+       for a reserve */
+    GNUNET_PQ_make_prepare ("payback_by_reserve",
+                            "SELECT"
+                            " coin_pub"
+                            ",coin_sig"
+                            ",coin_blind"
+                            ",amount_val"
+                            ",amount_frac"
+                            ",amount_curr"
+                            ",timestamp"
+                            ",denoms.denom_pub"
+                            ",coins.denom_sig"
+                            " FROM payback"
+                            "    JOIN known_coins coins"
+                            "      USING (coin_pub)"
+                            "    JOIN denominations denoms"
+                            "      USING (denom_pub_hash)"
+                            " WHERE payback.reserve_pub=$1;",
+                            1),
+    /* Used in #postgres_get_reserve_history() */
+    GNUNET_PQ_make_prepare ("close_by_reserve",
+                            "SELECT"
+                            " amount_val"
+                            ",amount_frac"
+                            ",amount_curr"
+                            ",closing_fee_val"
+                            ",closing_fee_frac"
+                            ",closing_fee_curr"
+                            ",execution_date"
+                            ",receiver_account"
+                            ",wtid"
+                            " FROM reserves_close"
+                            " WHERE reserve_pub=$1;",
+                            1),
+    /* Used in #postgres_get_expired_reserves() */
+    GNUNET_PQ_make_prepare ("get_expired_reserves",
+                            "SELECT"
+                            " expiration_date"
+                            ",account_details"
+                            ",reserve_pub"
+                            ",current_balance_val"
+                            ",current_balance_frac"
+                            ",current_balance_curr"
+                            " FROM reserves"
+                            " WHERE expiration_date<=$1"
+                            "   AND (current_balance_val != 0 "
+                            "        OR current_balance_frac != 0)"
+                           " ORDER BY expiration_date ASC"
+                           " LIMIT 1;",
+                            1),
+    /* Used in #postgres_get_coin_transactions() to obtain payback transactions
+       for a coin */
+    GNUNET_PQ_make_prepare ("payback_by_coin",
+                            "SELECT"
+                            " payback.reserve_pub"
+                            ",coin_sig"
+                            ",coin_blind"
+                            ",amount_val"
+                            ",amount_frac"
+                            ",amount_curr"
+                            ",timestamp"
+                            ",denoms.denom_pub"
+                            ",coins.denom_sig"
+
+                            " FROM payback"
+                            "    JOIN known_coins coins"
+                            "      USING (coin_pub)"
+                            "    JOIN denominations denoms"
+                            "      USING (denom_pub_hash)"
+                            " WHERE payback.coin_pub=$1;",
+                            1),
+    /* Used in #postgres_get_reserve_by_h_blind() */
+    GNUNET_PQ_make_prepare ("reserve_by_h_blind",
+                            "SELECT"
+                            " reserve_pub"
+                            " FROM reserves_out"
+                            " WHERE h_blind_ev=$1"
+                            " LIMIT 1;",
+                            1),
+    /* used in #postgres_commit */
+    GNUNET_PQ_make_prepare ("do_commit",
+                            "COMMIT",
+                            0),
+    GNUNET_PQ_make_prepare ("gc_denominations",
+                            "DELETE"
+                            " FROM denominations"
+                            " WHERE expire_legal < $1;",
+                            1),
+    GNUNET_PQ_make_prepare ("gc_reserves",
+                            "DELETE"
+                            " FROM reserves"
+                            " WHERE expiration_date < $1"
+                            "   AND current_balance_val = 0"
+                            "   AND current_balance_frac = 0;",
+                            1),
+    GNUNET_PQ_PREPARED_STATEMENT_END
+  };
 
-  return GNUNET_OK;
-#undef PREPARE
+  return GNUNET_PQ_prepare_statements (db_conn,
+                                       ps);
 }
 
 
@@ -1622,8 +1469,11 @@ static void
 db_conn_destroy (void *cls)
 {
   struct TALER_EXCHANGEDB_Session *session = cls;
-  PGconn *db_conn = session->conn;
+  PGconn *db_conn;
 
+  if (NULL == session)
+    return;
+  db_conn = session->conn;
   if (NULL != db_conn)
     PQfinish (db_conn);
   GNUNET_free (session);
@@ -1645,7 +1495,24 @@ postgres_get_session (void *cls)
   struct TALER_EXCHANGEDB_Session *session;
 
   if (NULL != (session = pthread_getspecific (pc->db_conn_threadlocal)))
-    return session;
+  {
+    if (CONNECTION_BAD == PQstatus (session->conn))
+    {
+      /**
+       * Reset the thread-local database-handle.  Disconnects from the
+       * DB.  Needed after the database server restarts as we need to
+       * properly reconnect. */
+      GNUNET_assert (0 ==
+                    pthread_setspecific (pc->db_conn_threadlocal,
+                                         NULL));
+      PQfinish (session->conn);
+      GNUNET_free (session);
+    }
+    else
+    {
+      return session;
+    }
+  }
   db_conn = GNUNET_PQ_connect (pc->connection_cfg_str);
   if (NULL == db_conn)
     return NULL;
@@ -1657,7 +1524,6 @@ postgres_get_session (void *cls)
     return NULL;
   }
   session = GNUNET_new (struct TALER_EXCHANGEDB_Session);
-  session->state = GNUNET_SYSERR;
   session->conn = db_conn;
   if (0 != pthread_setspecific (pc->db_conn_threadlocal,
                                 session))
@@ -1695,11 +1561,9 @@ postgres_start (void *cls,
                      PQerrorMessage (session->conn));
     GNUNET_break (0);
     PQclear (result);
-    session->state = GNUNET_SYSERR;
     return GNUNET_SYSERR;
   }
   PQclear (result);
-  session->state = GNUNET_OK;
   return GNUNET_OK;
 }
 
@@ -1722,51 +1586,6 @@ postgres_rollback (void *cls,
   GNUNET_break (PGRES_COMMAND_OK ==
                 PQresultStatus (result));
   PQclear (result);
-  session->state = GNUNET_SYSERR;
-}
-
-
-/**
- * Check the @a result's error code to see what happened.
- * Also logs errors.
- *
- * @param session session used
- * @param result result to check
- * @return #GNUNET_OK if the request/transaction succeeded
- *         #GNUNET_NO if it failed but could succeed if retried
- *         #GNUNET_SYSERR on hard errors
- */
-static int
-evaluate_pq_result (struct TALER_EXCHANGEDB_Session *session,
-                    PGresult *result)
-{
-  if (PGRES_COMMAND_OK !=
-      PQresultStatus (result))
-  {
-    const char *sqlstate;
-
-    sqlstate = PQresultErrorField (result,
-                                   PG_DIAG_SQLSTATE);
-    if (NULL == sqlstate)
-    {
-      /* very unexpected... */
-      GNUNET_break (0);
-      return GNUNET_SYSERR;
-    }
-    if ( (0 == strcmp (sqlstate,
-                       PQ_DIAG_SQLSTATE_DEADLOCK)) ||
-         (0 == strcmp (sqlstate,
-                       PQ_DIAG_SQLSTATE_SERIALIZATION_FAILURE)) )
-    {
-      /* These two can be retried and have a fair chance of working
-         the next time */
-      QUERY_ERR (result, session->conn);
-      return GNUNET_NO;
-    }
-    BREAK_DB_ERR(result, session->conn);
-    return GNUNET_SYSERR;
-  }
-  return GNUNET_OK;
 }
 
 
@@ -1775,113 +1594,19 @@ evaluate_pq_result (struct TALER_EXCHANGEDB_Session 
*session,
  *
  * @param cls the `struct PostgresClosure` with the plugin-specific state
  * @param session the database connection
- * @return #GNUNET_SYSERR on hard error,
- *         #GNUNET_NO if commit failed but retry may work,
- *         #GNUNET_OK on success
+ * @return final transaction status
  */
-static int
+static enum GNUNET_DB_QueryStatus
 postgres_commit (void *cls,
                  struct TALER_EXCHANGEDB_Session *session)
 {
-  PGresult *result;
-  int ret;
-  int state;
-
-  state = session->state;
-  if (GNUNET_OK != state)
-  {
-    postgres_rollback (cls,
-                       session);
-    return state;
-  }
-  result = PQexec (session->conn,
-                   "COMMIT");
-  ret = evaluate_pq_result (session,
-                            result);
-  GNUNET_break (GNUNET_SYSERR != ret);
-  PQclear (result);
-  return ret;
-}
-
-
-/**
- * Update the @a session state based on the latest @a result from
- * the database.  Checks the status code of @a result and possibly
- * sets the state to failed (#GNUNET_SYSERR) or transiently failed
- * (#GNUNET_NO).
- *
- * @param session the session in which the transaction is running
- * @param statement name of the statement we were executing (for logging)
- * @param result the result we got from Postgres
- * @return current session state, i.e.
- *         #GNUNET_OK on success
- *         #GNUNET_NO if the transaction had a transient failure
- *         #GNUNET_SYSERR if the transaction had a hard failure
- */
-static int
-update_session_from_result (struct TALER_EXCHANGEDB_Session *session,
-                            const char *statement,
-                            PGresult *result)
-{
-  int ret;
-
-  if (GNUNET_OK != session->state)
-  {
-    GNUNET_break (0);
-    return GNUNET_SYSERR; /* we already failed, why do we keep going? */
-  }
-  ret = evaluate_pq_result (session,
-                            result);
-  if (GNUNET_OK == ret)
-    return ret;
-  GNUNET_log ((GNUNET_NO == ret)
-              ? GNUNET_ERROR_TYPE_INFO
-              : GNUNET_ERROR_TYPE_ERROR,
-              "Statement `%s' failed: %s/%s/%s/%s/%s",
-              statement,
-              PQresultErrorField (result, PG_DIAG_MESSAGE_PRIMARY),
-              PQresultErrorField (result, PG_DIAG_MESSAGE_DETAIL),
-              PQresultErrorMessage (result),
-              PQresStatus (PQresultStatus (result)),
-              PQerrorMessage (session->conn));
-  session->state = ret;
-  return ret;
-}
-
-
-/**
- * Execute a named prepared @a statement that is NOT a SELECT statement
- * in @a session using the given @a params.  Returns the resulting session
- * state.
- *
- * @param session session to execute the statement in
- * @param statement name of the statement
- * @param params parameters to give to the statement 
(#GNUNET_PQ_query_param_end-terminated)
- * @return #GNUNET_OK on success
- *         #GNUNET_NO if the transaction had a transient failure
- *         #GNUNET_SYSERR if the transaction had a hard failure
- */
-static int
-execute_prepared_non_select (struct TALER_EXCHANGEDB_Session *session,
-                             const char *statement,
-                             const struct GNUNET_PQ_QueryParam *params)
-{
-  PGresult *result;
-  int ret;
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_end
+  };
 
-  if (GNUNET_OK != session->state)
-  {
-    GNUNET_break (0);
-    return GNUNET_SYSERR; /* we already failed, why keep going? */
-  }
-  result = GNUNET_PQ_exec_prepared (session->conn,
-                                    statement,
-                                    params);
-  ret = update_session_from_result (session,
-                                    statement,
-                                    result);
-  PQclear (result);
-  return ret;
+  return GNUNET_PQ_eval_prepared_non_select (session->conn,
+                                             "do_commit",
+                                             params);
 }
 
 
@@ -1893,9 +1618,9 @@ execute_prepared_non_select (struct 
TALER_EXCHANGEDB_Session *session,
  * @param session connection to use
  * @param denom_pub the public key used for signing coins of this denomination
  * @param issue issuing information with value, fees and other info about the 
coin
- * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+ * @return status of the query
  */
-static int
+static enum GNUNET_DB_QueryStatus
 postgres_insert_denomination_info (void *cls,
                                    struct TALER_EXCHANGEDB_Session *session,
                                    const struct TALER_DenominationPublicKey 
*denom_pub,
@@ -1932,9 +1657,9 @@ postgres_insert_denomination_info (void *cls,
                  TALER_amount_cmp_currency_nbo (&issue->properties.value,
                                                &issue->properties.fee_refund));
 
-  return execute_prepared_non_select (session,
-                                      "denomination_insert",
-                                      params);
+  return GNUNET_PQ_eval_prepared_non_select (session->conn,
+                                            "denomination_insert",
+                                            params);
 }
 
 
@@ -1944,89 +1669,57 @@ postgres_insert_denomination_info (void *cls,
  * @param cls the @e cls of this struct with the plugin-specific state
  * @param session connection to use
  * @param denom_pub the public key used for signing coins of this denomination
- * @param[out] issue set to issue information with value, fees and other info 
about the coin, can be NULL
- * @return #GNUNET_OK on success; #GNUNET_NO if no record was found, 
#GNUNET_SYSERR on failure
+ * @param[out] issue set to issue information with value, fees and other info 
about the coin
+ * @return transaction status code
  */
-static int
+static enum GNUNET_DB_QueryStatus
 postgres_get_denomination_info (void *cls,
                                 struct TALER_EXCHANGEDB_Session *session,
                                 const struct TALER_DenominationPublicKey 
*denom_pub,
                                 struct 
TALER_EXCHANGEDB_DenominationKeyInformationP *issue)
 {
-  PGresult *result;
+  enum GNUNET_DB_QueryStatus qs;
   struct GNUNET_PQ_QueryParam params[] = {
     GNUNET_PQ_query_param_rsa_public_key (denom_pub->rsa_public_key),
     GNUNET_PQ_query_param_end
   };
+  struct GNUNET_PQ_ResultSpec rs[] = {
+    GNUNET_PQ_result_spec_auto_from_type ("master_pub",
+                                         &issue->properties.master),
+    GNUNET_PQ_result_spec_auto_from_type ("master_sig",
+                                         &issue->signature),
+    GNUNET_PQ_result_spec_absolute_time_nbo ("valid_from",
+                                            &issue->properties.start),
+    GNUNET_PQ_result_spec_absolute_time_nbo ("expire_withdraw",
+                                            
&issue->properties.expire_withdraw),
+    GNUNET_PQ_result_spec_absolute_time_nbo ("expire_deposit",
+                                            &issue->properties.expire_deposit),
+    GNUNET_PQ_result_spec_absolute_time_nbo ("expire_legal",
+                                            &issue->properties.expire_legal),
+    TALER_PQ_result_spec_amount_nbo ("coin",
+                                    &issue->properties.value),
+    TALER_PQ_result_spec_amount_nbo ("fee_withdraw",
+                                    &issue->properties.fee_withdraw),
+    TALER_PQ_result_spec_amount_nbo ("fee_deposit",
+                                    &issue->properties.fee_deposit),
+    TALER_PQ_result_spec_amount_nbo ("fee_refresh",
+                                    &issue->properties.fee_refresh),
+    TALER_PQ_result_spec_amount_nbo ("fee_refund",
+                                    &issue->properties.fee_refund),
+    GNUNET_PQ_result_spec_end
+  };
 
-  result = GNUNET_PQ_exec_prepared (session->conn,
-                                    "denomination_get",
-                                    params);
-  if (PGRES_TUPLES_OK != PQresultStatus (result))
-  {
-    QUERY_ERR (result,
-               session->conn);
-    PQclear (result);
-    return GNUNET_SYSERR;
-  }
-  if (0 == PQntuples (result))
-  {
-    PQclear (result);
-    return GNUNET_NO;
-  }
-  if (1 != PQntuples (result))
-  {
-    GNUNET_break (0);
-    PQclear (result);
-    return GNUNET_SYSERR;
-  }
-  if (NULL == issue)
-  {
-    PQclear (result);
-    return GNUNET_OK;
-  }
-  {
-    struct GNUNET_PQ_ResultSpec rs[] = {
-      GNUNET_PQ_result_spec_auto_from_type ("master_pub",
-                                            &issue->properties.master),
-      GNUNET_PQ_result_spec_auto_from_type ("master_sig",
-                                            &issue->signature),
-      GNUNET_PQ_result_spec_absolute_time_nbo ("valid_from",
-                                               &issue->properties.start),
-      GNUNET_PQ_result_spec_absolute_time_nbo ("expire_withdraw",
-                                               
&issue->properties.expire_withdraw),
-      GNUNET_PQ_result_spec_absolute_time_nbo ("expire_deposit",
-                                               
&issue->properties.expire_deposit),
-      GNUNET_PQ_result_spec_absolute_time_nbo ("expire_legal",
-                                               
&issue->properties.expire_legal),
-      TALER_PQ_result_spec_amount_nbo ("coin",
-                                       &issue->properties.value),
-      TALER_PQ_result_spec_amount_nbo ("fee_withdraw",
-                                       &issue->properties.fee_withdraw),
-      TALER_PQ_result_spec_amount_nbo ("fee_deposit",
-                                       &issue->properties.fee_deposit),
-      TALER_PQ_result_spec_amount_nbo ("fee_refresh",
-                                       &issue->properties.fee_refresh),
-      TALER_PQ_result_spec_amount_nbo ("fee_refund",
-                                       &issue->properties.fee_refund),
-      GNUNET_PQ_result_spec_end
-    };
-
-    EXITIF (GNUNET_OK !=
-            GNUNET_PQ_extract_result (result,
-                                      rs,
-                                      0));
-  }
-  PQclear (result);
+  qs = GNUNET_PQ_eval_prepared_singleton_select (session->conn,
+                                                "denomination_get",
+                                                params,
+                                                rs);
+  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
+    return qs;
   issue->properties.purpose.size = htonl (sizeof (struct 
TALER_DenominationKeyValidityPS));
   issue->properties.purpose.purpose = htonl 
(TALER_SIGNATURE_MASTER_DENOMINATION_KEY_VALIDITY);
   GNUNET_CRYPTO_rsa_public_key_hash (denom_pub->rsa_public_key,
                                      &issue->properties.denom_hash);
-  return GNUNET_OK;
-
- EXITIF_exit:
-  PQclear (result);
-  return GNUNET_SYSERR;
+  return qs;
 }
 
 
@@ -2038,53 +1731,27 @@ postgres_get_denomination_info (void *cls,
  * @param[in,out] reserve the reserve data.  The public key of the reserve 
should be
  *          set in this structure; it is used to query the database.  The 
balance
  *          and expiration are then filled accordingly.
- * @return #GNUNET_OK upon success;
- *         #GNUNET_NO if there were no results (but no hard failure)
- *         #GNUNET_SYSERR upon failure
+ * @return transaction status
  */
-static int
+static enum GNUNET_DB_QueryStatus
 postgres_reserve_get (void *cls,
                       struct TALER_EXCHANGEDB_Session *session,
                       struct TALER_EXCHANGEDB_Reserve *reserve)
 {
-  PGresult *result;
   struct GNUNET_PQ_QueryParam params[] = {
     GNUNET_PQ_query_param_auto_from_type(&reserve->pub),
     GNUNET_PQ_query_param_end
   };
+  struct GNUNET_PQ_ResultSpec rs[] = {
+    TALER_PQ_result_spec_amount("current_balance", &reserve->balance),
+    GNUNET_PQ_result_spec_absolute_time("expiration_date", &reserve->expiry),
+    GNUNET_PQ_result_spec_end
+  };
 
-  result = GNUNET_PQ_exec_prepared (session->conn,
-                                   "reserve_get",
-                                   params);
-  if (PGRES_TUPLES_OK != PQresultStatus (result))
-  {
-    QUERY_ERR (result, session->conn);
-    PQclear (result);
-    return GNUNET_SYSERR;
-  }
-  if (0 == PQntuples (result))
-  {
-    PQclear (result);
-    return GNUNET_NO;
-  }
-  {
-    struct GNUNET_PQ_ResultSpec rs[] = {
-      TALER_PQ_result_spec_amount("current_balance", &reserve->balance),
-      GNUNET_PQ_result_spec_absolute_time("expiration_date", &reserve->expiry),
-      GNUNET_PQ_result_spec_end
-    };
-
-    EXITIF (GNUNET_OK !=
-            GNUNET_PQ_extract_result (result,
-                                      rs,
-                                      0));
-  }
-  PQclear (result);
-  return GNUNET_OK;
-
- EXITIF_exit:
-  PQclear (result);
-  return GNUNET_SYSERR;
+  return GNUNET_PQ_eval_prepared_singleton_select (session->conn,
+                                                   "reserve_get",
+                                                   params,
+                                                   rs);
 }
 
 
@@ -2095,11 +1762,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)
@@ -2111,9 +1776,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);
 }
 
 
@@ -2130,11 +1795,9 @@ reserves_update (void *cls,
  * @param sender_account_details account information for the sender
  * @param wire_reference unique reference identifying the wire transfer 
(binary blob)
  * @param wire_reference_size number of bytes in @a wire_reference
- * @return #GNUNET_OK upon success; #GNUNET_NO if the given
- *         @a details are already known for this @a reserve_pub,
- *         #GNUNET_SYSERR upon failures (DB error, incompatible currency)
+ * @return transaction status code
  */
-static int
+static enum GNUNET_DB_QueryStatus
 postgres_reserves_in_insert (void *cls,
                              struct TALER_EXCHANGEDB_Session *session,
                              const struct TALER_ReservePublicKeyP *reserve_pub,
@@ -2145,26 +1808,19 @@ postgres_reserves_in_insert (void *cls,
                              size_t wire_reference_size)
 {
   struct PostgresClosure *pg = cls;
-  PGresult *result;
-  int reserve_exists;
+  enum GNUNET_DB_QueryStatus reserve_exists;
+  enum GNUNET_DB_QueryStatus qs;
   struct TALER_EXCHANGEDB_Reserve reserve;
   struct GNUNET_TIME_Absolute expiry;
 
-  if (GNUNET_OK !=
-      postgres_start (cls,
-                      session))
-  {
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
-  }
   reserve.pub = *reserve_pub;
   reserve_exists = postgres_reserve_get (cls,
                                          session,
                                          &reserve);
-  if (GNUNET_SYSERR == reserve_exists)
+  if (0 > reserve_exists)
   {
     GNUNET_break (0);
-    goto rollback;
+    return reserve_exists;
   }
   if ( (0 == reserve.balance.value) &&
        (0 == reserve.balance.fraction) )
@@ -2184,9 +1840,14 @@ postgres_reserves_in_insert (void *cls,
     */
   }
 
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Creating reserve %s with expiration in %s\n",
+              TALER_B2S (reserve_pub),
+              GNUNET_STRINGS_relative_time_to_string 
(pg->idle_reserve_expiration_time,
+                                                      GNUNET_NO));
   expiry = GNUNET_TIME_absolute_add (execution_time,
                                      pg->idle_reserve_expiration_time);
-  if (GNUNET_NO == reserve_exists)
+  if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == reserve_exists)
   {
     /* New reserve, create balance for the first time; we do this
        before adding the actual transaction to "reserves_in", as
@@ -2203,22 +1864,22 @@ postgres_reserves_in_insert (void *cls,
 
     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                 "Reserve does not exist; creating a new one\n");
-    result = GNUNET_PQ_exec_prepared (session->conn,
-                                      "reserve_create",
-                                      params);
-    if (PGRES_COMMAND_OK != PQresultStatus(result))
+    qs = GNUNET_PQ_eval_prepared_non_select (session->conn,
+                                            "reserve_create",
+                                            params);
+    if (0 > qs)
+      return qs;
+    if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
     {
-      QUERY_ERR (result, session->conn);
-      PQclear (result);
-      goto rollback;
+      /* Maybe DB did not detect serializiability error already,
+        but clearly there must be one. Still odd. */
+      GNUNET_break (0);
+      return GNUNET_DB_STATUS_SOFT_ERROR;
     }
-    PQclear (result);
   }
   /* Create new incoming transaction, SQL "primary key" logic
      is used to guard against duplicates.  If a duplicate is
-     detected, we rollback (which really shouldn't undo
-     anything) and return #GNUNET_NO to indicate that this failure
-     is kind-of harmless (already executed). */
+     detected, we just "succeed" with no changes. */
   {
     struct GNUNET_PQ_QueryParam params[] = {
       GNUNET_PQ_query_param_auto_from_type (&reserve.pub),
@@ -2230,36 +1891,14 @@ postgres_reserves_in_insert (void *cls,
       GNUNET_PQ_query_param_end
     };
 
-    result = GNUNET_PQ_exec_prepared (session->conn,
-                                      "reserves_in_add_transaction",
-                                      params);
-  }
-  if (PGRES_COMMAND_OK != PQresultStatus(result))
-  {
-    const char *efield;
-
-    efield = PQresultErrorField (result,
-                                PG_DIAG_SQLSTATE);
-    if ( (PGRES_FATAL_ERROR == PQresultStatus(result)) &&
-        (NULL != strstr ("23505", /* unique violation */
-                         efield)) )
-    {
-      /* This means we had the same reserve/justification/details
-        before */
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                  "Uniqueness violation, deposit details already known\n");
-      PQclear (result);
-      postgres_rollback (cls,
-                        session);
-      return GNUNET_NO;
-    }
-    QUERY_ERR (result, session->conn);
-    PQclear (result);
-    goto rollback;
+    qs = GNUNET_PQ_eval_prepared_non_select (session->conn,
+                                            "reserves_in_add_transaction",
+                                            params);
+    if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
+      return qs;
   }
-  PQclear (result);
 
-  if (GNUNET_YES == reserve_exists)
+  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == reserve_exists)
   {
     /* If the reserve already existed, we need to still update the
        balance; we do this after checking for duplication, as
@@ -2267,7 +1906,7 @@ 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;
-
+    
     updated_reserve.pub = reserve.pub;
     if (GNUNET_OK !=
         TALER_amount_add (&updated_reserve.balance,
@@ -2277,31 +1916,15 @@ postgres_reserves_in_insert (void *cls,
       /* currency overflow or incompatible currency */
       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                   "Attempt to deposit incompatible amount into reserve\n");
-      goto rollback;
+      return GNUNET_DB_STATUS_HARD_ERROR;
     }
     updated_reserve.expiry = GNUNET_TIME_absolute_max (expiry,
                                                        reserve.expiry);
-    if (GNUNET_OK !=
-        reserves_update (cls,
-                         session,
-                         &updated_reserve))
-      goto rollback;
-  }
-  if (GNUNET_OK != postgres_commit (cls,
-                                    session))
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                "Failed to commit transaction adding amount to reserve\n");
-    return GNUNET_SYSERR;
+    return reserves_update (cls,
+                           session,
+                           &updated_reserve);
   }
-  return GNUNET_OK;
-
- rollback:
-  GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-              "Transaction failed, doing rollback\n");
-  postgres_rollback (cls,
-                     session);
-  return GNUNET_SYSERR;
+  return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
 }
 
 
@@ -2312,56 +1935,28 @@ postgres_reserves_in_insert (void *cls,
  * @param session the database session handle
  * @param[out] wire_reference set to unique reference identifying the wire 
transfer (binary blob)
  * @param[out] wire_reference_size set to number of bytes in @a wire_reference
- * @return #GNUNET_OK upon success; #GNUNET_NO if we never got any incoming 
transfers
- *         #GNUNET_SYSERR upon failures (DB error)
+ * @return transaction status code
  */
-static int
+static enum GNUNET_DB_QueryStatus
 postgres_get_latest_reserve_in_reference (void *cls,
                                           struct TALER_EXCHANGEDB_Session 
*session,
                                           void **wire_reference,
                                           size_t *wire_reference_size)
 {
-  PGresult *result;
   struct GNUNET_PQ_QueryParam params[] = {
     GNUNET_PQ_query_param_end
   };
-  int ret;
-
-  ret = GNUNET_SYSERR;
-  result = GNUNET_PQ_exec_prepared (session->conn,
-                                    "reserves_in_get_latest_wire_reference",
-                                    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_variable_size ("wire_reference",
-                                           wire_reference,
-                                           wire_reference_size),
-      GNUNET_PQ_result_spec_end
-    };
+  struct GNUNET_PQ_ResultSpec rs[] = {
+    GNUNET_PQ_result_spec_variable_size ("wire_reference",
+                                        wire_reference,
+                                        wire_reference_size),
+    GNUNET_PQ_result_spec_end
+  };
 
-    if (GNUNET_OK !=
-        GNUNET_PQ_extract_result (result,
-                                  rs,
-                                  0))
-    {
-      GNUNET_break (0);
-      goto cleanup;
-    }
-  }
-  ret = GNUNET_OK;
- cleanup:
-  PQclear (result);
-  return ret;
+  return GNUNET_PQ_eval_prepared_singleton_select (session->conn,
+                                                  
"reserves_in_get_latest_wire_reference",
+                                                  params,
+                                                  rs);
 }
 
 
@@ -2375,71 +1970,40 @@ 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;
+  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
+  };
 
-  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
-    };
-
-    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;
-}
+  collectable->h_coin_envelope = *h_blind;
+  return GNUNET_PQ_eval_prepared_singleton_select (session->conn,
+                                                  "get_withdraw_info",
+                                                  params,
+                                                  rs);
+}
 
 
 /**
@@ -2450,17 +2014,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;
@@ -2475,31 +2036,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_OK != postgres_reserve_get (cls,
-                                         session,
-                                         &reserve))
+  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+      (qs = postgres_reserve_get (cls,
+                                 session,
+                                 &reserve)))
   {
     /* 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,
@@ -2512,314 +2074,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))
+    bt = GNUNET_new (struct TALER_EXCHANGEDB_BankTransfer);
     {
-      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);
-      {
-        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))
+    payback = GNUNET_new (struct TALER_EXCHANGEDB_Payback);
     {
-      QUERY_ERR (result, session->conn);
-      goto cleanup;
-    }
-    rows = PQntuples (result);
-    while (0 < rows)
-    {
-      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;
 }
 
 
@@ -2829,11 +2458,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)
@@ -2844,75 +2473,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;
 }
 
 
@@ -2924,11 +2530,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)
@@ -2938,9 +2542,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);
 }
 
 
@@ -2951,10 +2555,11 @@ postgres_mark_deposit_tiny (void *cls,
  * @param cls the @e cls of this struct with the plugin-specific state
  * @param session connection to the database
  * @param deposit the deposit to check
- * @return #GNUNET_YES if is is marked done done, #GNUNET_NO if not,
- *         #GNUNET_SYSERR on error (deposit unknown)
+ * @return #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT if is is marked done,
+ *         #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if not,
+ *         otherwise transaction error status (incl. deposit unknown)
  */
-static int
+static enum GNUNET_DB_QueryStatus
 postgres_test_deposit_done (void *cls,
                             struct TALER_EXCHANGEDB_Session *session,
                             const struct TALER_EXCHANGEDB_Deposit *deposit)
@@ -2966,50 +2571,25 @@ postgres_test_deposit_done (void *cls,
     GNUNET_PQ_query_param_auto_from_type (&deposit->h_wire),
     GNUNET_PQ_query_param_end
   };
-  PGresult *result;
-
-  result = GNUNET_PQ_exec_prepared (session->conn,
-                                    "test_deposit_done",
-                                    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_SYSERR;
-  }
-  if (1 != PQntuples (result))
-  {
-    GNUNET_break (0);
-    PQclear (result);
-    return GNUNET_SYSERR;
-  }
-
-  {
-    uint8_t done = 0;
-    struct GNUNET_PQ_ResultSpec rs[] = {
-      GNUNET_PQ_result_spec_auto_from_type ("done",
-                                            &done),
-      GNUNET_PQ_result_spec_end
-    };
-
-    if (GNUNET_OK !=
-        GNUNET_PQ_extract_result (result,
-                                  rs,
-                                  0))
-    {
-      GNUNET_break (0);
-      PQclear (result);
-      return GNUNET_SYSERR;
-    }
-    PQclear (result);
-    return (done ? GNUNET_YES : GNUNET_NO);
-  }
+  uint8_t done = 0;
+  struct GNUNET_PQ_ResultSpec rs[] = {
+    GNUNET_PQ_result_spec_auto_from_type ("done",
+                                         &done),
+    GNUNET_PQ_result_spec_end
+  };
+  enum GNUNET_DB_QueryStatus qs;
+
+  qs = GNUNET_PQ_eval_prepared_singleton_select (session->conn,
+                                                "test_deposit_done",
+                                                params,
+                                                rs);
+  if (qs < 0)
+    return qs;
+  if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
+    return GNUNET_DB_STATUS_HARD_ERROR; /* deposit MUST exist */
+  return (done
+         ? GNUNET_DB_STATUS_SUCCESS_ONE_RESULT
+         : GNUNET_DB_STATUS_SUCCESS_NO_RESULTS);
 }
 
 
@@ -3021,11 +2601,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)
@@ -3035,9 +2613,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);
 }
 
 
@@ -3050,10 +2628,9 @@ postgres_mark_deposit_done (void *cls,
  * @param session connection to the database
  * @param deposit_cb function to call for ONE such deposit
  * @param deposit_cb_cls closure for @a deposit_cb
- * @return number of rows processed, 0 if none exist,
- *         #GNUNET_SYSERR on error
+ * @return transaction status code
  */
-static int
+static enum GNUNET_DB_QueryStatus
 postgres_get_ready_deposit (void *cls,
                             struct TALER_EXCHANGEDB_Session *session,
                             TALER_EXCHANGEDB_DepositIterator deposit_cb,
@@ -3064,77 +2641,161 @@ postgres_get_ready_deposit (void *cls,
     GNUNET_PQ_query_param_absolute_time (&now),
     GNUNET_PQ_query_param_end
   };
-  PGresult *result;
-  unsigned int n;
-  int ret;
+  struct TALER_Amount amount_with_fee;
+  struct TALER_Amount deposit_fee;
+  struct GNUNET_TIME_Absolute wire_deadline;
+  struct GNUNET_HashCode h_contract_terms;
+  struct TALER_MerchantPublicKeyP merchant_pub;
+  struct TALER_CoinSpendPublicKeyP coin_pub;
+  uint64_t serial_id;
+  json_t *wire;
+  struct GNUNET_PQ_ResultSpec rs[] = {
+    GNUNET_PQ_result_spec_uint64 ("deposit_serial_id",
+                                 &serial_id),
+    TALER_PQ_result_spec_amount ("amount_with_fee",
+                                &amount_with_fee),
+    TALER_PQ_result_spec_amount ("fee_deposit",
+                                &deposit_fee),
+    GNUNET_PQ_result_spec_absolute_time ("wire_deadline",
+                                        &wire_deadline),
+    GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
+                                         &h_contract_terms),
+    GNUNET_PQ_result_spec_auto_from_type ("merchant_pub",
+                                         &merchant_pub),
+    GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
+                                         &coin_pub),
+    TALER_PQ_result_spec_json ("wire",
+                              &wire),
+    GNUNET_PQ_result_spec_end
+  };
+  enum GNUNET_DB_QueryStatus qs;
+
+  qs = GNUNET_PQ_eval_prepared_singleton_select (session->conn,
+                                                "deposits_get_ready",
+                                                params,
+                                                rs);
+  if (qs <= 0)
+    return qs;
+  qs = deposit_cb (deposit_cb_cls,
+                  serial_id,
+                  &merchant_pub,
+                  &coin_pub,
+                  &amount_with_fee,
+                  &deposit_fee,
+                  &h_contract_terms,
+                  wire_deadline,
+                  wire);
+  GNUNET_PQ_cleanup_result (rs);
+  return qs;
+}
 
-  result = GNUNET_PQ_exec_prepared (session->conn,
-                                   "deposits_get_ready",
-                                   params);
-  if (PGRES_TUPLES_OK !=
-      PQresultStatus (result))
-  {
-    BREAK_DB_ERR (result, session->conn);
-    PQclear (result);
-    return GNUNET_SYSERR;
-  }
-  if (0 == (n = PQntuples (result)))
-  {
-    PQclear (result);
-    return 0;
-  }
-  GNUNET_break (1 == n);
+
+/**
+ * Closure for #match_deposit_cb().
+ */
+struct MatchingDepositContext
+{
+  /**
+   * Function to call for each result
+   */
+  TALER_EXCHANGEDB_DepositIterator deposit_cb;
+
+  /**
+   * Closure for @e deposit_cb.
+   */
+  void *deposit_cb_cls;
+
+  /**
+   * Public key of the merchant against which we are matching.
+   */
+  const struct TALER_MerchantPublicKeyP *merchant_pub;
+  
+  /**
+   * Maximum number of results to return.
+   */
+  uint32_t limit;
+
+  /**
+   * Loop counter, actual number of results returned.
+   */
+  unsigned int i;
+
+  /**
+   * Set to #GNUNET_SYSERR on hard errors.
+   */
+  int status;
+};
+
+
+/**
+ * Helper function for #postgres_iterate_matching_deposits().
+ * To be called with the results of a SELECT statement
+ * that has returned @a num_results results.
+ *
+ * @param cls closure of type `struct MatchingDepositContext *`
+ * @param result the postgres result
+ * @param num_result the number of results in @a result
+ */
+static void
+match_deposit_cb (void *cls,
+                 PGresult *result,
+                 unsigned int num_results)
+{
+  struct MatchingDepositContext *mdc = cls;
+  
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "Found %u/%u matching deposits\n",
+             num_results,
+             mdc->limit);
+  num_results = GNUNET_MIN (num_results,
+                           mdc->limit);
+  for (mdc->i=0;mdc->i<num_results;mdc->i++)
   {
     struct TALER_Amount amount_with_fee;
     struct TALER_Amount deposit_fee;
     struct GNUNET_TIME_Absolute wire_deadline;
     struct GNUNET_HashCode h_contract_terms;
-    struct TALER_MerchantPublicKeyP merchant_pub;
     struct TALER_CoinSpendPublicKeyP coin_pub;
     uint64_t serial_id;
-    json_t *wire;
+    enum GNUNET_DB_QueryStatus qs;
     struct GNUNET_PQ_ResultSpec rs[] = {
       GNUNET_PQ_result_spec_uint64 ("deposit_serial_id",
-                                   &serial_id),
+                                    &serial_id),
       TALER_PQ_result_spec_amount ("amount_with_fee",
                                    &amount_with_fee),
       TALER_PQ_result_spec_amount ("fee_deposit",
                                    &deposit_fee),
       GNUNET_PQ_result_spec_absolute_time ("wire_deadline",
-                                          &wire_deadline),
+                                           &wire_deadline),
       GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
-                                           &h_contract_terms),
-      GNUNET_PQ_result_spec_auto_from_type ("merchant_pub",
-                                           &merchant_pub),
+                                            &h_contract_terms),
       GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
-                                           &coin_pub),
-      TALER_PQ_result_spec_json ("wire",
-                                 &wire),
+                                            &coin_pub),
       GNUNET_PQ_result_spec_end
     };
-
+    
     if (GNUNET_OK !=
         GNUNET_PQ_extract_result (result,
                                   rs,
-                                  0))
+                                  mdc->i))
     {
       GNUNET_break (0);
-      PQclear (result);
-      return GNUNET_SYSERR;
+      mdc->status = GNUNET_SYSERR;
+      return;
     }
-    ret = deposit_cb (deposit_cb_cls,
-                      serial_id,
-                      &merchant_pub,
-                      &coin_pub,
-                      &amount_with_fee,
-                      &deposit_fee,
-                      &h_contract_terms,
-                      wire_deadline,
-                      wire);
+    qs = mdc->deposit_cb (mdc->deposit_cb_cls,
+                         serial_id,
+                         mdc->merchant_pub,
+                         &coin_pub,
+                         &amount_with_fee,
+                         &deposit_fee,
+                         &h_contract_terms,
+                         wire_deadline,
+                         NULL);
     GNUNET_PQ_cleanup_result (rs);
-    PQclear (result);
+    if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
+      break;
   }
-  return (GNUNET_OK == ret) ? 1 : 0;
 }
 
 
@@ -3149,10 +2810,10 @@ postgres_get_ready_deposit (void *cls,
  * @param deposit_cb function to call for each deposit
  * @param deposit_cb_cls closure for @a deposit_cb
  * @param limit maximum number of matching deposits to return
- * @return number of rows processed, 0 if none exist,
- *         #GNUNET_SYSERR on error
+ * @return transaction status code, if positive:
+ *         number of rows processed, 0 if none exist
  */
-static int
+static enum GNUNET_DB_QueryStatus
 postgres_iterate_matching_deposits (void *cls,
                                     struct TALER_EXCHANGEDB_Session *session,
                                     const struct GNUNET_HashCode *h_wire,
@@ -3166,75 +2827,27 @@ postgres_iterate_matching_deposits (void *cls,
     GNUNET_PQ_query_param_auto_from_type (h_wire),
     GNUNET_PQ_query_param_end
   };
-  PGresult *result;
-  unsigned int i;
-  unsigned int n;
-
-  result = GNUNET_PQ_exec_prepared (session->conn,
-                                    "deposits_iterate_matching",
-                                    params);
-  if (PGRES_TUPLES_OK !=
-      PQresultStatus (result))
-  {
-    BREAK_DB_ERR (result, session->conn);
-    PQclear (result);
-    return GNUNET_SYSERR;
-  }
-  if (0 == (n = PQntuples (result)))
-  {
-    PQclear (result);
-    return 0;
-  }
-  if (n > limit)
-    n = limit;
-  for (i=0;i<n;i++)
+  struct MatchingDepositContext mdc;
+  enum GNUNET_DB_QueryStatus qs;
+
+  mdc.deposit_cb = deposit_cb;
+  mdc.deposit_cb_cls = deposit_cb_cls;
+  mdc.merchant_pub = merchant_pub;
+  mdc.limit = limit;
+  mdc.status = GNUNET_OK;
+  qs = GNUNET_PQ_eval_prepared_multi_select (session->conn,
+                                            "deposits_iterate_matching",
+                                            params,
+                                            &match_deposit_cb,
+                                            &mdc);
+  if (GNUNET_OK != mdc.status)
   {
-    struct TALER_Amount amount_with_fee;
-    struct TALER_Amount deposit_fee;
-    struct GNUNET_TIME_Absolute wire_deadline;
-    struct GNUNET_HashCode h_contract_terms;
-    struct TALER_CoinSpendPublicKeyP coin_pub;
-    uint64_t serial_id;
-    int ret;
-    struct GNUNET_PQ_ResultSpec rs[] = {
-      GNUNET_PQ_result_spec_uint64 ("deposit_serial_id",
-                                    &serial_id),
-      TALER_PQ_result_spec_amount ("amount_with_fee",
-                                   &amount_with_fee),
-      TALER_PQ_result_spec_amount ("fee_deposit",
-                                   &deposit_fee),
-      GNUNET_PQ_result_spec_absolute_time ("wire_deadline",
-                                           &wire_deadline),
-      GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
-                                            &h_contract_terms),
-      GNUNET_PQ_result_spec_auto_from_type ("coin_pub",
-                                            &coin_pub),
-      GNUNET_PQ_result_spec_end
-    };
-    if (GNUNET_OK !=
-        GNUNET_PQ_extract_result (result,
-                                  rs,
-                                  i))
-    {
-      GNUNET_break (0);
-      PQclear (result);
-      return GNUNET_SYSERR;
-    }
-    ret = deposit_cb (deposit_cb_cls,
-                      serial_id,
-                      merchant_pub,
-                      &coin_pub,
-                      &amount_with_fee,
-                      &deposit_fee,
-                      &h_contract_terms,
-                      wire_deadline,
-                      NULL);
-    GNUNET_PQ_cleanup_result (rs);
-    if (GNUNET_OK != ret)
-      break;
+    GNUNET_break (0);
+    return GNUNET_DB_STATUS_HARD_ERROR;
   }
-  PQclear (result);
-  return i;
+  if (qs >= 0)
+    return mdc.i;
+  return qs;
 }
 
 
@@ -3245,66 +2858,31 @@ postgres_iterate_matching_deposits (void *cls,
  * @param session the database session handle
  * @param coin_pub the public key of the coin to search for
  * @param coin_info place holder for the returned coin information object
- * @return #GNUNET_SYSERR upon error; #GNUNET_NO if no coin is found; 
#GNUNET_OK
- *           if upon succesfullying retrieving the record data info @a
- *           coin_info
+ * @return transaction status code
  */
-static int
+static enum GNUNET_DB_QueryStatus
 get_known_coin (void *cls,
                 struct TALER_EXCHANGEDB_Session *session,
                 const struct TALER_CoinSpendPublicKeyP *coin_pub,
                 struct TALER_CoinPublicInfo *coin_info)
 {
-  PGresult *result;
   struct GNUNET_PQ_QueryParam params[] = {
     GNUNET_PQ_query_param_auto_from_type (coin_pub),
     GNUNET_PQ_query_param_end
   };
-  int nrows;
-
-  result = GNUNET_PQ_exec_prepared (session->conn,
-                                   "get_known_coin",
-                                   params);
-  if (PGRES_TUPLES_OK != PQresultStatus (result))
-  {
-    BREAK_DB_ERR (result, session->conn);
-    PQclear (result);
-    return GNUNET_SYSERR;
-  }
-  nrows = PQntuples (result);
-  if (0 == nrows)
-  {
-    PQclear (result);
-    return GNUNET_NO;
-  }
-  GNUNET_assert (1 == nrows);   /* due to primary key */
-  if (NULL == coin_info)
-  {
-    PQclear (result);
-    return GNUNET_YES;
-  }
-  {
-    struct GNUNET_PQ_ResultSpec rs[] = {
-      GNUNET_PQ_result_spec_rsa_public_key ("denom_pub",
-                                            
&coin_info->denom_pub.rsa_public_key),
-      GNUNET_PQ_result_spec_rsa_signature ("denom_sig",
-                                           
&coin_info->denom_sig.rsa_signature),
-      GNUNET_PQ_result_spec_end
-    };
-
-    if (GNUNET_OK !=
-        GNUNET_PQ_extract_result (result,
-                                  rs,
-                                  0))
-    {
-      PQclear (result);
-      GNUNET_break (0);
-      return GNUNET_SYSERR;
-    }
-  }
-  PQclear (result);
+  struct GNUNET_PQ_ResultSpec rs[] = {
+    GNUNET_PQ_result_spec_rsa_public_key ("denom_pub",
+                                         &coin_info->denom_pub.rsa_public_key),
+    GNUNET_PQ_result_spec_rsa_signature ("denom_sig",
+                                        &coin_info->denom_sig.rsa_signature),
+    GNUNET_PQ_result_spec_end
+  };
+  
   coin_info->coin_pub = *coin_pub;
-  return GNUNET_OK;
+  return GNUNET_PQ_eval_prepared_singleton_select (session->conn,
+                                                  "get_known_coin",
+                                                  params,
+                                                  rs);
 }
 
 
@@ -3316,11 +2894,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)
@@ -3335,9 +2911,53 @@ 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);
+}
+
+
+/**
+ * Make sure the given @a coin is known to the database.
+ *
+ * @param cls database connection plugin state
+ * @param session database session
+ * @param coin the coin that must be made known
+ * @return database transaction status, non-negative on success
+ */ 
+static enum GNUNET_DB_QueryStatus
+ensure_coin_known (struct PostgresClosure *cls,
+                  struct TALER_EXCHANGEDB_Session *session,
+                  const struct TALER_CoinPublicInfo *coin)
+{
+  enum GNUNET_DB_QueryStatus qs;
+  struct TALER_CoinPublicInfo known_coin;
+
+  /* check if the coin is already known */
+  qs = get_known_coin (cls,
+                      session,
+                      &coin->coin_pub,
+                      &known_coin);
+  if (0 > qs)
+  {
+    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+    return GNUNET_SYSERR;
+  }
+  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
+    return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; /* no change! */
+  GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs);
+  /* if not known, insert it */
+  qs = insert_known_coin (cls,
+                         session,
+                         coin);
+  if (0 >= qs)
+  {
+    if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
+      qs = GNUNET_DB_STATUS_HARD_ERROR; /* should be impossible */
+    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+    return qs;
+  }
+  return qs;
 }
 
 
@@ -3347,16 +2967,14 @@ 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),
@@ -3371,31 +2989,13 @@ postgres_insert_deposit (void *cls,
     GNUNET_PQ_query_param_end
   };
 
-  /* check if the coin is already known */
-  ret = get_known_coin (cls,
-                        session,
-                        &deposit->coin.coin_pub,
-                        NULL);
-  if (GNUNET_SYSERR == ret)
-  {
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
-  }
-  if (GNUNET_NO == ret)         /* if not, insert it */
-  {
-    if (GNUNET_OK !=
-        (ret = insert_known_coin (cls,
-                                  session,
-                                  &deposit->coin)))
-    {
-      GNUNET_break (GNUNET_NO == ret);
-      return ret;
-    }
-  }
-
-  return execute_prepared_non_select (session,
-                                      "insert_deposit",
-                                      params);
+  if (0 > (qs = ensure_coin_known (cls,
+                                  session,
+                                  &deposit->coin)))
+    return qs;
+  return GNUNET_PQ_eval_prepared_non_select (session->conn,
+                                            "insert_deposit",
+                                            params);
 }
 
 
@@ -3405,11 +3005,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)
@@ -3427,9 +3025,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);
 }
 
 
@@ -3439,88 +3037,55 @@ postgres_insert_refund (void *cls,
  * @param cls the `struct PostgresClosure` with the plugin-specific state
  * @param session database handle to use
  * @param session_hash hash over the melt to use to locate the session
- * @param[out] refresh_session where to store the result, can be NULL
- *             to just check if the session exists
- * @return #GNUNET_YES on success,
- *         #GNUNET_NO if not found,
- *         #GNUNET_SYSERR on DB failure
+ * @param[out] refresh_session where to store the result
+ * @return transaction status
  */
-static int
+static enum GNUNET_DB_QueryStatus
 postgres_get_refresh_session (void *cls,
                               struct TALER_EXCHANGEDB_Session *session,
                               const struct GNUNET_HashCode *session_hash,
                               struct TALER_EXCHANGEDB_RefreshSession 
*refresh_session)
 {
-  PGresult *result;
   struct GNUNET_PQ_QueryParam params[] = {
     GNUNET_PQ_query_param_auto_from_type (session_hash),
     GNUNET_PQ_query_param_end
   };
+  struct GNUNET_PQ_ResultSpec rs[] = {
+    GNUNET_PQ_result_spec_uint16 ("num_newcoins",
+                                 &refresh_session->num_newcoins),
+    GNUNET_PQ_result_spec_uint16 ("noreveal_index",
+                                 &refresh_session->noreveal_index),
+    GNUNET_PQ_result_spec_auto_from_type ("old_coin_pub",
+                                         &refresh_session->melt.coin.coin_pub),
+    GNUNET_PQ_result_spec_auto_from_type ("old_coin_sig",
+                                         &refresh_session->melt.coin_sig),
+    TALER_PQ_result_spec_amount ("amount_with_fee",
+                                &refresh_session->melt.amount_with_fee),
+    TALER_PQ_result_spec_amount ("fee_refresh",
+                                &refresh_session->melt.melt_fee),
+    GNUNET_PQ_result_spec_end
+  };
+  enum GNUNET_DB_QueryStatus qs;
 
-  result = GNUNET_PQ_exec_prepared (session->conn,
-                                    "get_refresh_session",
-                                    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;
-  }
-  GNUNET_assert (1 == PQntuples (result));
-  if (NULL == refresh_session)
-  {
-    /* We're done if the caller is only interested in whether the
-     * session exists or not */
-    PQclear (result);
-    return GNUNET_YES;
-  }
   memset (refresh_session,
           0,
           sizeof (struct TALER_EXCHANGEDB_RefreshSession));
-  {
-    struct GNUNET_PQ_ResultSpec rs[] = {
-      GNUNET_PQ_result_spec_uint16 ("num_newcoins",
-                                    &refresh_session->num_newcoins),
-      GNUNET_PQ_result_spec_uint16 ("noreveal_index",
-                                    &refresh_session->noreveal_index),
-      GNUNET_PQ_result_spec_auto_from_type ("old_coin_pub",
-                                            
&refresh_session->melt.coin.coin_pub),
-      GNUNET_PQ_result_spec_auto_from_type ("old_coin_sig",
-                                            &refresh_session->melt.coin_sig),
-      TALER_PQ_result_spec_amount ("amount_with_fee",
-                                   &refresh_session->melt.amount_with_fee),
-      TALER_PQ_result_spec_amount ("fee_refresh",
-                                   &refresh_session->melt.melt_fee),
-      GNUNET_PQ_result_spec_end
-    };
-
-    if (GNUNET_OK !=
-        GNUNET_PQ_extract_result (result,
-                                  rs,
-                                  0))
-    {
-      GNUNET_break (0);
-      PQclear (result);
-      return GNUNET_SYSERR;
-    }
-  }
-  PQclear (result);
-  if (GNUNET_OK !=
-      get_known_coin (cls,
-                      session,
-                      &refresh_session->melt.coin.coin_pub,
-                      &refresh_session->melt.coin))
-  {
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
+  qs = GNUNET_PQ_eval_prepared_singleton_select (session->conn,
+                                                "get_refresh_session",
+                                                params,
+                                                rs);
+  if ( (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) &&
+       (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+       (qs = get_known_coin (cls,
+                             session,
+                             &refresh_session->melt.coin.coin_pub,
+                             &refresh_session->melt.coin)) ) )
+  {
+    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+    return qs;
   }
   refresh_session->melt.session_hash = *session_hash;
-  return GNUNET_YES;
+  return qs;
 }
 
 
@@ -3531,11 +3096,9 @@ postgres_get_refresh_session (void *cls,
  * @param session database handle to use
  * @param session_hash hash over the melt to use to locate the session
  * @param refresh_session session data to store
- * @return #GNUNET_YES on success,
- *         #GNUNET_NO on transient error
- *         #GNUNET_SYSERR on DB failure
+ * @return query status for the transaction
  */
-static int
+static enum GNUNET_DB_QueryStatus
 postgres_create_refresh_session (void *cls,
                                  struct TALER_EXCHANGEDB_Session *session,
                                  const struct GNUNET_HashCode *session_hash,
@@ -3550,33 +3113,15 @@ postgres_create_refresh_session (void *cls,
     GNUNET_PQ_query_param_uint16 (&refresh_session->noreveal_index),
     GNUNET_PQ_query_param_end
   };
-  int ret;
-
-  /* check if the coin is already known */
-  ret = get_known_coin (cls,
-                        session,
-                        &refresh_session->melt.coin.coin_pub,
-                        NULL);
-  if (GNUNET_SYSERR == ret)
-  {
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
-  }
-  if (GNUNET_NO == ret)         /* if not, insert it */
-  {
-    if (GNUNET_OK !=
-        (ret = insert_known_coin (cls,
-                                  session,
-                                  &refresh_session->melt.coin)))
-    {
-      GNUNET_break (GNUNET_NO == ret);
-      return GNUNET_SYSERR;
-    }
-  }
-
-  return execute_prepared_non_select (session,
-                                      "insert_refresh_session",
-                                      params);
+  enum GNUNET_DB_QueryStatus qs;
+
+  if (0 > (qs = ensure_coin_known (cls,
+                                  session,
+                                  &refresh_session->melt.coin)))
+    return qs;
+  return GNUNET_PQ_eval_prepared_non_select (session->conn,
+                                            "insert_refresh_session",
+                                            params);
 }
 
 
@@ -3589,11 +3134,9 @@ postgres_create_refresh_session (void *cls,
  * @param session_hash hash to identify refresh session
  * @param num_newcoins number of coins to generate, size of the @a denom_pubs 
array
  * @param denom_pubs array denominations of the coins to create
- * @return #GNUNET_OK on success
- *         #GNUNET_NO on transient error
- *         #GNUNET_SYSERR on internal error
+ * @return query status for the transaction
  */
-static int
+static enum GNUNET_DB_QueryStatus
 postgres_insert_refresh_order (void *cls,
                                struct TALER_EXCHANGEDB_Session *session,
                                const struct GNUNET_HashCode *session_hash,
@@ -3612,18 +3155,18 @@ postgres_insert_refresh_order (void *cls,
         GNUNET_PQ_query_param_auto_from_type (&denom_pub_hash),
         GNUNET_PQ_query_param_end
       };
-      int ret;
+      enum GNUNET_DB_QueryStatus qs;
 
       GNUNET_CRYPTO_rsa_public_key_hash (denom_pubs[i].rsa_public_key,
                                         &denom_pub_hash);
-      ret = execute_prepared_non_select (session,
-                                         "insert_refresh_order",
-                                         params);
-      if (GNUNET_OK != ret)
-        return ret;
+      qs = GNUNET_PQ_eval_prepared_non_select (session->conn,
+                                              "insert_refresh_order",
+                                              params);
+      if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
+        return qs;
     }
   }
-  return GNUNET_OK;
+  return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
 }
 
 
@@ -3655,69 +3198,49 @@ free_dpk_result (struct TALER_DenominationPublicKey 
*denom_pubs,
  * @param session_hash hash to identify refresh session
  * @param num_newcoins size of the array of the @a denom_pubs array
  * @param denom_pubs where to store the deomination keys
- * @return #GNUNET_OK on success
- *         #GNUNET_SYSERR on internal error
+ * @return transaction status
  */
-static int
+static enum GNUNET_DB_QueryStatus
 postgres_get_refresh_order (void *cls,
                             struct TALER_EXCHANGEDB_Session *session,
                             const struct GNUNET_HashCode *session_hash,
                             uint16_t num_newcoins,
                             struct TALER_DenominationPublicKey *denom_pubs)
-{
-  unsigned int i;
-
-  for (i=0;i<(unsigned int) num_newcoins;i++)
+{ 
+  for (unsigned i=0;i<(unsigned int) num_newcoins;i++)
   {
     uint16_t newcoin_off = (uint16_t) i;
-    PGresult *result;
-
-    {
-      struct GNUNET_PQ_QueryParam params[] = {
-        GNUNET_PQ_query_param_auto_from_type (session_hash),
-        GNUNET_PQ_query_param_uint16 (&newcoin_off),
-        GNUNET_PQ_query_param_end
-      };
-
-      result = GNUNET_PQ_exec_prepared (session->conn,
-                                       "get_refresh_order",
-                                       params);
-    }
-    if (PGRES_TUPLES_OK != PQresultStatus (result))
-    {
-      BREAK_DB_ERR (result, session->conn);
-      PQclear (result);
-      free_dpk_result (denom_pubs, i);
-      return GNUNET_SYSERR;
-    }
-    if (0 == PQntuples (result))
+    enum GNUNET_DB_QueryStatus qs;
+    struct GNUNET_PQ_QueryParam params[] = {
+      GNUNET_PQ_query_param_auto_from_type (session_hash),
+      GNUNET_PQ_query_param_uint16 (&newcoin_off),
+      GNUNET_PQ_query_param_end
+    };
+    struct GNUNET_PQ_ResultSpec rs[] = {
+      GNUNET_PQ_result_spec_rsa_public_key ("denom_pub",
+                                           &denom_pubs[i].rsa_public_key),
+      GNUNET_PQ_result_spec_end
+    };
+    
+    qs = GNUNET_PQ_eval_prepared_singleton_select (session->conn,
+                                                  "get_refresh_order",
+                                                  params,
+                                                  rs);
+    switch (qs)
     {
-      PQclear (result);
-      /* FIXME: may want to distinguish between different error cases! */
+    case GNUNET_DB_STATUS_HARD_ERROR:
+    case GNUNET_DB_STATUS_SOFT_ERROR:
+    case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
       free_dpk_result (denom_pubs, i);
-      return GNUNET_SYSERR;
-    }
-    GNUNET_assert (1 == PQntuples (result));
-    {
-      struct GNUNET_PQ_ResultSpec rs[] = {
-        GNUNET_PQ_result_spec_rsa_public_key ("denom_pub",
-                                             &denom_pubs[i].rsa_public_key),
-        GNUNET_PQ_result_spec_end
-      };
-      if (GNUNET_OK !=
-          GNUNET_PQ_extract_result (result,
-                                    rs,
-                                    0))
-      {
-        PQclear (result);
-        GNUNET_break (0);
-        free_dpk_result (denom_pubs, i);
-        return GNUNET_SYSERR;
-      }
-      PQclear (result);
+      return qs;
+    case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+      break;
+    default:
+      GNUNET_break (0);
+      break;
     }
   }
-  return GNUNET_OK;
+  return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
 }
 
 
@@ -3730,11 +3253,9 @@ postgres_get_refresh_order (void *cls,
  * @param session_hash hash to identify refresh session
  * @param num_newcoins coin index size of the @a commit_coins array
  * @param commit_coins array of coin commitments to store
- * @return #GNUNET_OK on success
- *         #GNUNET_NO on transient error
- *         #GNUNET_SYSERR on error
+ * @return query transaction status
  */
-static int
+static enum GNUNET_DB_QueryStatus
 postgres_insert_refresh_commit_coins (void *cls,
                                       struct TALER_EXCHANGEDB_Session *session,
                                       const struct GNUNET_HashCode 
*session_hash,
@@ -3750,15 +3271,15 @@ postgres_insert_refresh_commit_coins (void *cls,
                                         commit_coins[coin_off].coin_ev_size),
       GNUNET_PQ_query_param_end
     };
-    int ret;
+    enum GNUNET_DB_QueryStatus qs;
 
-    ret = execute_prepared_non_select (session,
-                                       "insert_refresh_commit_coin",
-                                       params);
-    if (GNUNET_OK != ret)
-      return ret;
+    qs = GNUNET_PQ_eval_prepared_non_select (session->conn,
+                                            "insert_refresh_commit_coin",
+                                            params);
+    if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
+      return qs;
   }
-  return GNUNET_OK;
+  return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
 }
 
 
@@ -3793,11 +3314,9 @@ postgres_free_refresh_commit_coins (void *cls,
  * @param session_hash hash to identify refresh session
  * @param num_newcoins size of the @a commit_coins array
  * @param[out] commit_coins array of coin commitments to return
- * @return #GNUNET_OK on success
- *         #GNUNET_NO if not found
- *         #GNUNET_SYSERR on error
+ * @return transaction status
  */
-static int
+static enum GNUNET_DB_QueryStatus
 postgres_get_refresh_commit_coins (void *cls,
                                    struct TALER_EXCHANGEDB_Session *session,
                                    const struct GNUNET_HashCode *session_hash,
@@ -3814,53 +3333,29 @@ postgres_get_refresh_commit_coins (void *cls,
     };
     void *c_buf;
     size_t c_buf_size;
-    PGresult *result;
+    struct GNUNET_PQ_ResultSpec rs[] = {
+      GNUNET_PQ_result_spec_variable_size ("coin_ev",
+                                          &c_buf,
+                                          &c_buf_size),
+      GNUNET_PQ_result_spec_end
+    };
+    enum GNUNET_DB_QueryStatus qs;
 
-    result = GNUNET_PQ_exec_prepared (session->conn,
-                                     "get_refresh_commit_coin",
-                                     params);
-    if (PGRES_TUPLES_OK != PQresultStatus (result))
+    qs = GNUNET_PQ_eval_prepared_singleton_select (session->conn,
+                                                  "get_refresh_commit_coin",
+                                                  params,
+                                                  rs);
+    if (0 >= qs)
     {
-      BREAK_DB_ERR (result, session->conn);
-      PQclear (result);
       postgres_free_refresh_commit_coins (cls,
                                           i,
                                           commit_coins);
-      return GNUNET_SYSERR;
+      return qs;
     }
-    if (0 == PQntuples (result))
-    {
-      PQclear (result);
-      postgres_free_refresh_commit_coins (cls,
-                                          i,
-                                          commit_coins);
-      return GNUNET_NO;
-    }
-    {
-      struct GNUNET_PQ_ResultSpec rs[] = {
-        GNUNET_PQ_result_spec_variable_size ("coin_ev",
-                                            &c_buf,
-                                            &c_buf_size),
-        GNUNET_PQ_result_spec_end
-      };
-
-      if (GNUNET_YES !=
-          GNUNET_PQ_extract_result (result,
-                                    rs,
-                                    0))
-      {
-        PQclear (result);
-        postgres_free_refresh_commit_coins (cls,
-                                            i,
-                                            commit_coins);
-        return GNUNET_SYSERR;
-      }
-    }
-    PQclear (result);
     commit_coins[i].coin_ev = c_buf;
     commit_coins[i].coin_ev_size = c_buf_size;
   }
-  return GNUNET_YES;
+  return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
 }
 
 
@@ -3872,11 +3367,9 @@ postgres_get_refresh_commit_coins (void *cls,
  * @param session database connection to use
  * @param session_hash hash to identify refresh session
  * @param tp transfer public key to store
- * @return #GNUNET_SYSERR on internal error,
- *         #GNUNET_NO on transient errors
- *         #GNUNET_OK on success
+ * @return transaction status
  */
-static int
+static enum GNUNET_DB_QueryStatus
 postgres_insert_refresh_transfer_public_key (void *cls,
                                              struct TALER_EXCHANGEDB_Session 
*session,
                                              const struct GNUNET_HashCode 
*session_hash,
@@ -3888,9 +3381,9 @@ postgres_insert_refresh_transfer_public_key (void *cls,
     GNUNET_PQ_query_param_end
   };
 
-  return execute_prepared_non_select (session,
-                                      "insert_transfer_public_key",
-                                      params);
+  return GNUNET_PQ_eval_prepared_non_select (session->conn,
+                                            "insert_transfer_public_key",
+                                            params);
 }
 
 
@@ -3902,11 +3395,9 @@ postgres_insert_refresh_transfer_public_key (void *cls,
  * @param session database connection to use
  * @param session_hash hash to identify refresh session
  * @param[out] tp information to return
- * @return #GNUNET_SYSERR on internal error,
- *         #GNUNET_NO if commitment was not found
- *         #GNUNET_OK on success
+ * @return transaction status
  */
-static int
+static enum GNUNET_DB_QueryStatus
 postgres_get_refresh_transfer_public_key (void *cls,
                                           struct TALER_EXCHANGEDB_Session 
*session,
                                           const struct GNUNET_HashCode 
*session_hash,
@@ -3916,40 +3407,16 @@ postgres_get_refresh_transfer_public_key (void *cls,
     GNUNET_PQ_query_param_auto_from_type (session_hash),
     GNUNET_PQ_query_param_end
   };
-  PGresult *result;
-
-  result = GNUNET_PQ_exec_prepared (session->conn,
-                                    "get_refresh_transfer_public_key",
-                                    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 GNUNET_PQ_ResultSpec rs[] = {
-      GNUNET_PQ_result_spec_auto_from_type ("transfer_pub",
-                                            tp),
-      GNUNET_PQ_result_spec_end
-    };
-
-    if (GNUNET_YES !=
-        GNUNET_PQ_extract_result (result,
-                                  rs,
-                                  0))
-    {
-      PQclear (result);
-      return GNUNET_SYSERR;
-    }
-  }
-  PQclear (result);
-  return GNUNET_OK;
+  struct GNUNET_PQ_ResultSpec rs[] = {
+    GNUNET_PQ_result_spec_auto_from_type ("transfer_pub",
+                                         tp),
+    GNUNET_PQ_result_spec_end
+  };
+  
+  return GNUNET_PQ_eval_prepared_singleton_select (session->conn,
+                                                  
"get_refresh_transfer_public_key",
+                                                  params,
+                                                  rs);
 }
 
 
@@ -3963,17 +3430,15 @@ postgres_get_refresh_transfer_public_key (void *cls,
  * @param session_hash hash to identify refresh session
  * @param newcoin_index coin index
  * @param ev_sig coin signature
- * @return #GNUNET_OK on success, #GNUNET_NO if we have no such result
- *         #GNUNET_SYSERR on error
+ * @return transaction result status
  */
-static int
+static enum GNUNET_DB_QueryStatus
 postgres_get_refresh_out (void *cls,
                           struct TALER_EXCHANGEDB_Session *session,
                           const struct GNUNET_HashCode *session_hash,
                           uint16_t newcoin_index,
                           struct TALER_DenominationSignature *ev_sig)
 {
-  PGresult *result;
   struct GNUNET_PQ_QueryParam params[] = {
     GNUNET_PQ_query_param_auto_from_type (session_hash),
     GNUNET_PQ_query_param_uint16 (&newcoin_index),
@@ -3985,31 +3450,10 @@ postgres_get_refresh_out (void *cls,
     GNUNET_PQ_result_spec_end
   };
 
-  result = GNUNET_PQ_exec_prepared (session->conn,
-                                    "get_refresh_out",
-                                    params);
-  if (PGRES_TUPLES_OK != PQresultStatus (result))
-  {
-    BREAK_DB_ERR (result, session->conn);
-    PQclear (result);
-    return GNUNET_SYSERR;
-  }
-  if (1 != PQntuples (result))
-  {
-    PQclear (result);
-    return GNUNET_NO;
-  }
-  if (GNUNET_OK !=
-      GNUNET_PQ_extract_result (result,
-                                rs,
-                                0))
-  {
-    PQclear (result);
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
-  }
-  PQclear (result);
-  return GNUNET_OK;
+  return GNUNET_PQ_eval_prepared_singleton_select (session->conn,
+                                                  "get_refresh_out",
+                                                  params,
+                                                  rs);
 }
 
 
@@ -4024,11 +3468,9 @@ postgres_get_refresh_out (void *cls,
  * @param session_hash hash to identify refresh session
  * @param newcoin_index coin index
  * @param ev_sig coin signature
- * @return #GNUNET_OK on success
- *         #GNUNET_NO on transient error
- *         #GNUNET_SYSERR on error
+ * @return transaction result status
  */
-static int
+static enum GNUNET_DB_QueryStatus
 postgres_insert_refresh_out (void *cls,
                              struct TALER_EXCHANGEDB_Session *session,
                              const struct GNUNET_HashCode *session_hash,
@@ -4042,52 +3484,45 @@ postgres_insert_refresh_out (void *cls,
     GNUNET_PQ_query_param_end
   };
 
-  return execute_prepared_non_select (session,
-                                      "insert_refresh_out",
-                                      params);
+  return GNUNET_PQ_eval_prepared_non_select (session->conn,
+                                            "insert_refresh_out",
+                                            params);
 }
 
 
 /**
- * Obtain the link data of a coin, that is the encrypted link
- * information, the denomination keys and the signatures.
- *
- * @param cls the `struct PostgresClosure` with the plugin-specific state
- * @param session database connection
- * @param session_hash refresh session to get linkage data for
- * @return all known link data for the session
+ * Closure for #add_ldl().
  */
-static struct TALER_EXCHANGEDB_LinkDataList *
-postgres_get_link_data_list (void *cls,
-                             struct TALER_EXCHANGEDB_Session *session,
-                             const struct GNUNET_HashCode *session_hash)
+struct LinkDataContext
 {
+  /** 
+   * List we are building.
+   */ 
   struct TALER_EXCHANGEDB_LinkDataList *ldl;
-  int nrows;
-  struct GNUNET_PQ_QueryParam params[] = {
-    GNUNET_PQ_query_param_auto_from_type (session_hash),
-    GNUNET_PQ_query_param_end
-  };
-  PGresult *result;
 
-  result = GNUNET_PQ_exec_prepared (session->conn,
-                                   "get_link",
-                                   params);
-  ldl = NULL;
-  if (PGRES_TUPLES_OK != PQresultStatus (result))
-  {
-    BREAK_DB_ERR (result, session->conn);
-    PQclear (result);
-    return NULL;
-  }
-  nrows = PQntuples (result);
-  if (0 == nrows)
-  {
-    PQclear (result);
-    return NULL;
-  }
+  /**
+   * Status, set to #GNUNET_SYSERR on errors,
+   */
+  int status;
+};
 
-  for (int i = nrows-1; i >= 0; i--)
+
+/**
+ * Function to be called with the results of a SELECT statement
+ * that has returned @a num_results results.
+ *
+ * @param cls closure of type `struct LinkDataContext *`
+ * @param result the postgres result
+ * @param num_result the number of results in @a result
+ */
+static void
+add_ldl (void *cls,
+        PGresult *result,
+        unsigned int num_results)
+{
+  struct LinkDataContext *ldc = cls;
+  
+  for (int i = num_results - 1; i >= 0; i--)
   {
     struct GNUNET_CRYPTO_RsaPublicKey *denom_pub;
     struct GNUNET_CRYPTO_RsaSignature *sig;
@@ -4108,71 +3543,98 @@ postgres_get_link_data_list (void *cls,
                                     rs,
                                     i))
       {
-       PQclear (result);
        GNUNET_break (0);
        common_free_link_data_list (cls,
-                                   ldl);
+                                   ldc->ldl);
+       ldc->ldl = NULL;
        GNUNET_free (pos);
-       return NULL;
+       ldc->status = GNUNET_SYSERR;
+       return;
       }
     }
-    pos->next = ldl;
+    pos->next = ldc->ldl;
     pos->denom_pub.rsa_public_key = denom_pub;
     pos->ev_sig.rsa_signature = sig;
-    ldl = pos;
+    ldc->ldl = pos;
   }
-  PQclear (result);
-  return ldl;
 }
 
 
 /**
- * Obtain shared secret and transfer public key from the public key of
- * the coin.  This information and the link information returned by
- * #postgres_get_link_data_list() enable the owner of an old coin to
- * determine the private keys of the new coins after the melt.
+ * Obtain the link data of a coin, that is the encrypted link
+ * information, the denomination keys and the signatures.
  *
  * @param cls the `struct PostgresClosure` with the plugin-specific state
  * @param session database connection
- * @param coin_pub public key of the coin
- * @param tdc function to call for each session the coin was melted into
- * @param tdc_cls closure for @a tdc
- * @return #GNUNET_OK on success,
- *         #GNUNET_NO on failure (not found)
- *         #GNUNET_SYSERR on internal failure (database issue)
+ * @param session_hash refresh session to get linkage data for
+ * @param[out] ldlp set to all known link data for the session
+ * @return transaction status code
  */
-static int
-postgres_get_transfer (void *cls,
-                       struct TALER_EXCHANGEDB_Session *session,
-                       const struct TALER_CoinSpendPublicKeyP *coin_pub,
-                       TALER_EXCHANGEDB_TransferDataCallback tdc,
-                       void *tdc_cls)
+static enum GNUNET_DB_QueryStatus
+postgres_get_link_data_list (void *cls,
+                             struct TALER_EXCHANGEDB_Session *session,
+                             const struct GNUNET_HashCode *session_hash,
+                            struct TALER_EXCHANGEDB_LinkDataList **ldlp)
 {
+  struct LinkDataContext ldc;
   struct GNUNET_PQ_QueryParam params[] = {
-    GNUNET_PQ_query_param_auto_from_type (coin_pub),
+    GNUNET_PQ_query_param_auto_from_type (session_hash),
     GNUNET_PQ_query_param_end
   };
-  PGresult *result;
-  int nrows;
+  enum GNUNET_DB_QueryStatus qs;
+
+  ldc.status = GNUNET_OK;
+  ldc.ldl = NULL;
+  qs = GNUNET_PQ_eval_prepared_multi_select (session->conn,
+                                            "get_link",
+                                            params,
+                                            &add_ldl,
+                                            &ldc);
+  *ldlp = ldc.ldl;
+  if (GNUNET_OK != ldc.status)
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  return qs;
+}
 
-  result = GNUNET_PQ_exec_prepared (session->conn,
-                                   "get_transfer",
-                                   params);
-  if (PGRES_TUPLES_OK !=
-      PQresultStatus (result))
-  {
-    BREAK_DB_ERR (result, session->conn);
-    PQclear (result);
-    return GNUNET_SYSERR;
-  }
-  nrows = PQntuples (result);
-  if (0 == nrows)
-  {
-    /* no matches found */
-    PQclear (result);
-    return GNUNET_NO;
-  }
-  for (int i=0;i<nrows;i++)
+
+/**
+ * Closure for #add_link().
+ */
+struct AddLinkContext
+{
+  /**
+   * Function to call on each result.
+   */
+  TALER_EXCHANGEDB_TransferDataCallback tdc;
+
+  /**
+   * Closure for @e tdc.
+   */
+  void *tdc_cls;
+
+  /**
+   * Status code, set to #GNUNET_SYSERR on errors.
+   */
+  int status;
+};
+
+
+/**
+ * Function to be called with the results of a SELECT statement
+ * that has returned @a num_results results.
+ *
+ * @param cls closure of type `struct AddLinkContext *`
+ * @param result the postgres result
+ * @param num_result the number of results in @a result
+ */
+static void
+add_link (void *cls,
+         PGresult *result,
+         unsigned int num_results)
+{
+  struct AddLinkContext *alc = cls;
+
+  for (unsigned int i=0;i<num_results;i++)
   {
     struct GNUNET_HashCode session_hash;
     struct TALER_TransferPublicKeyP transfer_pub;
@@ -4187,379 +3649,485 @@ postgres_get_transfer (void *cls,
                                   rs,
                                   i))
     {
-      PQclear (result);
       GNUNET_break (0);
-      return GNUNET_SYSERR;
+      alc->status = GNUNET_SYSERR;
+      return;
     }
-    tdc (tdc_cls,
-         &session_hash,
-         &transfer_pub);
+    alc->tdc (alc->tdc_cls,
+             &session_hash,
+             &transfer_pub);
   }
-  PQclear (result);
-  return GNUNET_OK;
 }
 
 
 /**
- * Compile a list of all (historic) transactions performed
- * with the given coin (/refresh/melt, /deposit and /refund operations).
+ * Obtain shared secret and transfer public key from the public key of
+ * the coin.  This information and the link information returned by
+ * #postgres_get_link_data_list() enable the owner of an old coin to
+ * determine the private keys of the new coins after the melt.
  *
  * @param cls the `struct PostgresClosure` with the plugin-specific state
  * @param session database connection
- * @param coin_pub coin to investigate
- * @return list of transactions, NULL if coin is fresh
+ * @param coin_pub public key of the coin
+ * @param tdc function to call for each session the coin was melted into
+ * @param tdc_cls closure for @a tdc
+ * @return statement execution status
  */
-static struct TALER_EXCHANGEDB_TransactionList *
-postgres_get_coin_transactions (void *cls,
-                                struct TALER_EXCHANGEDB_Session *session,
-                                const struct TALER_CoinSpendPublicKeyP 
*coin_pub)
+static enum GNUNET_DB_QueryStatus
+postgres_get_transfer (void *cls,
+                       struct TALER_EXCHANGEDB_Session *session,
+                       const struct TALER_CoinSpendPublicKeyP *coin_pub,
+                       TALER_EXCHANGEDB_TransferDataCallback tdc,
+                       void *tdc_cls)
 {
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_auto_from_type (coin_pub),
+    GNUNET_PQ_query_param_end
+  };
+  struct AddLinkContext al_ctx;
+  enum GNUNET_DB_QueryStatus qs;
+
+  al_ctx.tdc = tdc;
+  al_ctx.tdc_cls = tdc_cls;
+  al_ctx.status = GNUNET_OK;
+  qs = GNUNET_PQ_eval_prepared_multi_select (session->conn,
+                                            "get_transfer",
+                                            params,
+                                            &add_link,
+                                            &al_ctx);
+  if (GNUNET_OK != al_ctx.status)
+    qs = GNUNET_DB_STATUS_HARD_ERROR;
+  return qs;
+}
+
+
+/**
+ * Closure for callbacks called from #postgres_get_coin_transactions()
+ */
+struct CoinHistoryContext
+{
+  /**
+   * Head of the coin's history list.
+   */
   struct TALER_EXCHANGEDB_TransactionList *head;
 
-  head = NULL;
-  /** #TALER_EXCHANGEDB_TT_DEPOSIT */
+  /**
+   * Public key of the coin we are building the history for.
+   */
+  const struct TALER_CoinSpendPublicKeyP *coin_pub;
+
+  /**
+   * Closure for all callbacks of this database plugin.
+   */
+  void *db_cls;
+
+  /**
+   * Database session we are using.
+   */
+  struct TALER_EXCHANGEDB_Session *session;
+  
+  /**
+   * Set to transaction status.
+   */ 
+  enum GNUNET_DB_QueryStatus status;
+};
+
+
+/**
+ * Function to be called with the results of a SELECT statement
+ * that has returned @a num_results results.
+ *
+ * @param cls closure of type `struct CoinHistoryContext`
+ * @param result the postgres result
+ * @param num_result the number of results in @a result
+ */
+static void
+add_coin_deposit (void *cls,
+                 PGresult *result,
+                 unsigned int num_results)
+{
+  struct CoinHistoryContext *chc = cls;
+
+  for (unsigned int i = 0; i < num_results; i++)
   {
-    struct GNUNET_PQ_QueryParam params[] = {
-      GNUNET_PQ_query_param_auto_from_type (coin_pub),
-      GNUNET_PQ_query_param_end
-    };
-    int nrows;
-    PGresult *result;
+    struct TALER_EXCHANGEDB_Deposit *deposit;
     struct TALER_EXCHANGEDB_TransactionList *tl;
-
-    result = GNUNET_PQ_exec_prepared (session->conn,
-                                      "get_deposit_with_coin_pub",
-                                      params);
-    if (PGRES_TUPLES_OK != PQresultStatus (result))
-    {
-      QUERY_ERR (result, session->conn);
-      PQclear (result);
-      goto cleanup;
-    }
-    nrows = PQntuples (result);
-    for (int i = 0; i < nrows; i++)
+    enum GNUNET_DB_QueryStatus qs;
+    
+    deposit = GNUNET_new (struct TALER_EXCHANGEDB_Deposit);
     {
-      struct TALER_EXCHANGEDB_Deposit *deposit;
-
-      deposit = GNUNET_new (struct TALER_EXCHANGEDB_Deposit);
-      {
-        struct GNUNET_PQ_ResultSpec rs[] = {
-          TALER_PQ_result_spec_amount ("amount_with_fee",
-                                       &deposit->amount_with_fee),
-          TALER_PQ_result_spec_amount ("fee_deposit",
-                                       &deposit->deposit_fee),
-          GNUNET_PQ_result_spec_absolute_time ("timestamp",
-                                               &deposit->timestamp),
-          GNUNET_PQ_result_spec_absolute_time ("refund_deadline",
-                                               &deposit->refund_deadline),
-          GNUNET_PQ_result_spec_auto_from_type ("merchant_pub",
-                                                &deposit->merchant_pub),
-          GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
-                                                &deposit->h_contract_terms),
-          GNUNET_PQ_result_spec_auto_from_type ("h_wire",
-                                                &deposit->h_wire),
-          TALER_PQ_result_spec_json ("wire",
-                                     &deposit->receiver_wire_account),
-          GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
-                                               &deposit->csig),
-          GNUNET_PQ_result_spec_end
-        };
-
-        if (GNUNET_OK !=
-            GNUNET_PQ_extract_result (result,
-                                      rs,
-                                      i))
-        {
-          GNUNET_break (0);
-          GNUNET_free (deposit);
-          PQclear (result);
-          goto cleanup;
-        }
-        deposit->coin.coin_pub = *coin_pub;
-      }
-      tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
-      tl->next = head;
-      tl->type = TALER_EXCHANGEDB_TT_DEPOSIT;
-      tl->details.deposit = deposit;
-      if (GNUNET_SYSERR == get_known_coin (cls,
-                                           session,
-                                           &deposit->coin.coin_pub,
-                                           &deposit->coin))
+      struct GNUNET_PQ_ResultSpec rs[] = {
+       TALER_PQ_result_spec_amount ("amount_with_fee",
+                                    &deposit->amount_with_fee),
+       TALER_PQ_result_spec_amount ("fee_deposit",
+                                    &deposit->deposit_fee),
+       GNUNET_PQ_result_spec_absolute_time ("timestamp",
+                                            &deposit->timestamp),
+       GNUNET_PQ_result_spec_absolute_time ("refund_deadline",
+                                            &deposit->refund_deadline),
+       GNUNET_PQ_result_spec_auto_from_type ("merchant_pub",
+                                             &deposit->merchant_pub),
+       GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
+                                             &deposit->h_contract_terms),
+       GNUNET_PQ_result_spec_auto_from_type ("h_wire",
+                                             &deposit->h_wire),
+       TALER_PQ_result_spec_json ("wire",
+                                  &deposit->receiver_wire_account),
+       GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
+                                             &deposit->csig),
+       GNUNET_PQ_result_spec_end
+      };
+      
+      if (GNUNET_OK !=
+         GNUNET_PQ_extract_result (result,
+                                   rs,
+                                   i))
       {
-        GNUNET_break (0);
-        GNUNET_free (deposit);
-        PQclear (result);
-        goto cleanup;
+       GNUNET_break (0);
+       GNUNET_free (deposit);
+       chc->status = GNUNET_DB_STATUS_HARD_ERROR;
+       return;
       }
-      head = tl;
-      continue;
+      deposit->coin.coin_pub = *chc->coin_pub;
     }
-    PQclear (result);
+    tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
+    tl->next = chc->head;
+    tl->type = TALER_EXCHANGEDB_TT_DEPOSIT;
+    tl->details.deposit = deposit;
+    qs = get_known_coin (chc->db_cls,
+                        chc->session,
+                        chc->coin_pub,
+                        &deposit->coin);
+    if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
+    {
+      GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+      GNUNET_free (deposit);
+      chc->status = qs;
+      return;
+    }
+    chc->head = tl;
   }
-  /** #TALER_EXCHANGEDB_TT_REFRESH_MELT */
+}
+
+
+/**
+ * Function to be called with the results of a SELECT statement
+ * that has returned @a num_results results.
+ *
+ * @param cls closure of type `struct CoinHistoryContext`
+ * @param result the postgres result
+ * @param num_result the number of results in @a result
+ */
+static void
+add_coin_melt (void *cls,
+              PGresult *result,
+              unsigned int num_results)
+{
+  struct CoinHistoryContext *chc = cls;
+
+  for (unsigned int i=0;i<num_results;i++)
   {
-    struct GNUNET_PQ_QueryParam params[] = {
-      GNUNET_PQ_query_param_auto_from_type (&coin_pub->eddsa_pub),
-      GNUNET_PQ_query_param_end
-    };
-    int nrows;
-    PGresult *result;
+    struct TALER_EXCHANGEDB_RefreshMelt *melt;
     struct TALER_EXCHANGEDB_TransactionList *tl;
+    enum GNUNET_DB_QueryStatus qs;
 
-    /* check if the melt records exist and get them */
-    result = GNUNET_PQ_exec_prepared (session->conn,
-                                      "get_refresh_session_by_coin",
-                                      params);
-    if (PGRES_TUPLES_OK != PQresultStatus (result))
-    {
-      BREAK_DB_ERR (result, session->conn);
-      PQclear (result);
-      goto cleanup;
-    }
-    nrows = PQntuples (result);
-    for (int i=0;i<nrows;i++)
+    melt = GNUNET_new (struct TALER_EXCHANGEDB_RefreshMelt);
     {
-      struct TALER_EXCHANGEDB_RefreshMelt *melt;
-
-      melt = GNUNET_new (struct TALER_EXCHANGEDB_RefreshMelt);
-      {
-        struct GNUNET_PQ_ResultSpec rs[] = {
-          GNUNET_PQ_result_spec_auto_from_type ("session_hash",
-                                                &melt->session_hash),
-          /* oldcoin_index not needed */
-          GNUNET_PQ_result_spec_auto_from_type ("old_coin_sig",
-                                                &melt->coin_sig),
-          TALER_PQ_result_spec_amount ("amount_with_fee",
-                                       &melt->amount_with_fee),
-          TALER_PQ_result_spec_amount ("fee_refresh",
-                                       &melt->melt_fee),
-          GNUNET_PQ_result_spec_end
-        };
-        if (GNUNET_OK !=
-            GNUNET_PQ_extract_result (result,
-                                      rs,
-                                      i))
-        {
-          GNUNET_break (0);
-          GNUNET_free (melt);
-          PQclear (result);
-          goto cleanup;
-        }
-       melt->coin.coin_pub = *coin_pub;
-      }
-      tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
-      tl->next = head;
-      tl->type = TALER_EXCHANGEDB_TT_REFRESH_MELT;
-      tl->details.melt = melt;
-      if (GNUNET_SYSERR == get_known_coin (cls,
-                                           session,
-                                           coin_pub,
-                                           &melt->coin))
+      struct GNUNET_PQ_ResultSpec rs[] = {
+       GNUNET_PQ_result_spec_auto_from_type ("session_hash",
+                                             &melt->session_hash),
+       /* oldcoin_index not needed */
+       GNUNET_PQ_result_spec_auto_from_type ("old_coin_sig",
+                                             &melt->coin_sig),
+       TALER_PQ_result_spec_amount ("amount_with_fee",
+                                    &melt->amount_with_fee),
+       TALER_PQ_result_spec_amount ("fee_refresh",
+                                    &melt->melt_fee),
+       GNUNET_PQ_result_spec_end
+      };
+      
+      if (GNUNET_OK !=
+         GNUNET_PQ_extract_result (result,
+                                   rs,
+                                   i))
       {
-        GNUNET_break (0);
-        GNUNET_free (melt);
-        PQclear (result);
-        goto cleanup;
+       GNUNET_break (0);
+       GNUNET_free (melt);
+       chc->status = GNUNET_DB_STATUS_HARD_ERROR;
+       return;
       }
-      head = tl;
-      continue;
+      melt->coin.coin_pub = *chc->coin_pub;
     }
-    PQclear (result);
+    tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
+    tl->next = chc->head;
+    tl->type = TALER_EXCHANGEDB_TT_REFRESH_MELT;
+    tl->details.melt = melt;
+    qs = get_known_coin (chc->db_cls,
+                        chc->session,
+                        chc->coin_pub,
+                        &melt->coin);
+    if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
+    {
+      GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+      GNUNET_free (melt);
+      chc->status = qs;
+      return;
+    }
+    chc->head = tl;
   }
-  /** #TALER_EXCHANGEDB_TT_REFUND */
+}
+
+
+/**
+ * Function to be called with the results of a SELECT statement
+ * that has returned @a num_results results.
+ *
+ * @param cls closure of type `struct CoinHistoryContext`
+ * @param result the postgres result
+ * @param num_result the number of results in @a result
+ */
+static void
+add_coin_refund (void *cls,
+                PGresult *result,
+                unsigned int num_results)
+{
+  struct CoinHistoryContext *chc = cls;
+
+  for (unsigned int i=0;i<num_results;i++)
   {
-    struct GNUNET_PQ_QueryParam params[] = {
-      GNUNET_PQ_query_param_auto_from_type (coin_pub),
-      GNUNET_PQ_query_param_end
-    };
-    int nrows;
-    PGresult *result;
+    struct TALER_EXCHANGEDB_Refund *refund;
     struct TALER_EXCHANGEDB_TransactionList *tl;
+    enum GNUNET_DB_QueryStatus qs;
 
-    /* check if a refund records exist and get them */
-    result = GNUNET_PQ_exec_prepared (session->conn,
-                                      "get_refunds_by_coin",
-                                      params);
-    if (PGRES_TUPLES_OK != PQresultStatus (result))
+    refund = GNUNET_new (struct TALER_EXCHANGEDB_Refund);
     {
-      BREAK_DB_ERR (result, session->conn);
-      PQclear (result);
-      goto cleanup;
-    }
-    nrows = PQntuples (result);
-    for (int i=0;i<nrows;i++)
-    {
-      struct TALER_EXCHANGEDB_Refund *refund;
-
-      refund = GNUNET_new (struct TALER_EXCHANGEDB_Refund);
-      {
-        struct GNUNET_PQ_ResultSpec rs[] = {
-          GNUNET_PQ_result_spec_auto_from_type ("merchant_pub",
-                                                &refund->merchant_pub),
-          GNUNET_PQ_result_spec_auto_from_type ("merchant_sig",
-                                                &refund->merchant_sig),
-          GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
-                                                &refund->h_contract_terms),
-          GNUNET_PQ_result_spec_uint64 ("rtransaction_id",
-                                        &refund->rtransaction_id),
-          TALER_PQ_result_spec_amount ("amount_with_fee",
-                                       &refund->refund_amount),
-          TALER_PQ_result_spec_amount ("fee_refund",
-                                       &refund->refund_fee),
-          GNUNET_PQ_result_spec_end
-        };
-        if (GNUNET_OK !=
-            GNUNET_PQ_extract_result (result,
-                                      rs,
-                                      i))
-        {
-          GNUNET_break (0);
-          GNUNET_free (refund);
-          PQclear (result);
-          goto cleanup;
-        }
-       refund->coin.coin_pub = *coin_pub;
-      }
-      tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
-      tl->next = head;
-      tl->type = TALER_EXCHANGEDB_TT_REFUND;
-      tl->details.refund = refund;
-      if (GNUNET_SYSERR ==
-          get_known_coin (cls,
-                          session,
-                          coin_pub,
-                          &refund->coin))
+      struct GNUNET_PQ_ResultSpec rs[] = {
+       GNUNET_PQ_result_spec_auto_from_type ("merchant_pub",
+                                             &refund->merchant_pub),
+       GNUNET_PQ_result_spec_auto_from_type ("merchant_sig",
+                                             &refund->merchant_sig),
+       GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
+                                             &refund->h_contract_terms),
+       GNUNET_PQ_result_spec_uint64 ("rtransaction_id",
+                                     &refund->rtransaction_id),
+       TALER_PQ_result_spec_amount ("amount_with_fee",
+                                    &refund->refund_amount),
+       TALER_PQ_result_spec_amount ("fee_refund",
+                                    &refund->refund_fee),
+       GNUNET_PQ_result_spec_end
+      };
+      
+      if (GNUNET_OK !=
+         GNUNET_PQ_extract_result (result,
+                                   rs,
+                                   i))
       {
-        GNUNET_break (0);
-        GNUNET_free (refund);
-        PQclear (result);
-        goto cleanup;
+       GNUNET_break (0);
+       GNUNET_free (refund);
+       chc->status = GNUNET_DB_STATUS_HARD_ERROR;
+       return;
       }
-      head = tl;
-      continue;
+      refund->coin.coin_pub = *chc->coin_pub;
     }
-    PQclear (result);
+    tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
+    tl->next = chc->head;
+    tl->type = TALER_EXCHANGEDB_TT_REFUND;
+    tl->details.refund = refund;
+    qs = get_known_coin (chc->db_cls,
+                        chc->session,
+                        chc->coin_pub,
+                        &refund->coin);
+    if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
+    {
+      GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+      GNUNET_free (refund);
+      chc->status = qs;
+      return;
+    }
+    chc->head = tl;
   }
-  /** #TALER_EXCHANGEDB_TT_PAYBACK */
+}
+
+
+/**
+ * Function to be called with the results of a SELECT statement
+ * that has returned @a num_results results.
+ *
+ * @param cls closure of type `struct CoinHistoryContext`
+ * @param result the postgres result
+ * @param num_result the number of results in @a result
+ */
+static void
+add_coin_payback (void *cls,
+                 PGresult *result,
+                 unsigned int num_results)
+{
+  struct CoinHistoryContext *chc = cls;
+
+  for (unsigned int i=0;i<num_results;i++)
   {
-    struct GNUNET_PQ_QueryParam params[] = {
-      GNUNET_PQ_query_param_auto_from_type (coin_pub),
-      GNUNET_PQ_query_param_end
-    };
-    int nrows;
-    PGresult *result;
+    struct TALER_EXCHANGEDB_Payback *payback;
     struct TALER_EXCHANGEDB_TransactionList *tl;
-
-    /* check if a refund records exist and get them */
-    result = GNUNET_PQ_exec_prepared (session->conn,
-                                      "payback_by_coin",
-                                      params);
-    if (PGRES_TUPLES_OK != PQresultStatus (result))
+    
+    payback = GNUNET_new (struct TALER_EXCHANGEDB_Payback);
     {
-      BREAK_DB_ERR (result, session->conn);
-      PQclear (result);
-      goto cleanup;
-    }
-    nrows = PQntuples (result);
-    for (int i=0;i<nrows;i++)
-    {
-      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 ("reserve_pub",
+                                             &payback->reserve_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,
+                                   i))
       {
-        struct GNUNET_PQ_ResultSpec rs[] = {
-          TALER_PQ_result_spec_amount ("amount",
-                                       &payback->value),
-          GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
-                                                &payback->reserve_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,
-                                      i))
-        {
-          GNUNET_break (0);
-          GNUNET_free (payback);
-          PQclear (result);
-          goto cleanup;
-        }
-       payback->coin.coin_pub = *coin_pub;
+       GNUNET_break (0);
+       GNUNET_free (payback);
+       chc->status = GNUNET_DB_STATUS_HARD_ERROR;
+       return;
       }
-      tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
-      tl->next = head;
-      tl->type = TALER_EXCHANGEDB_TT_PAYBACK;
-      tl->details.payback = payback;
-      head = tl;
-      continue;
+      payback->coin.coin_pub = *chc->coin_pub;
     }
-    PQclear (result);
+    tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
+    tl->next = chc->head;
+    tl->type = TALER_EXCHANGEDB_TT_PAYBACK;
+    tl->details.payback = payback;
+    chc->head = tl;
   }
-
-  return head;
- cleanup:
-  if (NULL != head)
-    common_free_coin_transaction_list (cls,
-                                       head);
-  return NULL;
 }
 
 
 /**
- * Lookup the list of Taler transactions that were aggregated
- * into a wire transfer by the respective @a wtid.
+ * Compile a list of all (historic) transactions performed
+ * with the given coin (/refresh/melt, /deposit and /refund operations).
  *
- * @param cls closure
+ * @param cls the `struct PostgresClosure` with the plugin-specific state
  * @param session database connection
- * @param wtid the raw wire transfer identifier we used
- * @param cb function to call on each transaction found
- * @param cb_cls closure for @a cb
- * @return #GNUNET_OK on success, #GNUNET_SYSERR on database errors,
- *         #GNUNET_NO if we found no results
+ * @param coin_pub coin to investigate
+ * @param[out] tlp set to list of transactions, NULL if coin is fresh
+ * @return database transaction status
  */
-static int
-postgres_lookup_wire_transfer (void *cls,
-                               struct TALER_EXCHANGEDB_Session *session,
-                               const struct TALER_WireTransferIdentifierRawP 
*wtid,
-                               TALER_EXCHANGEDB_WireTransferDataCallback cb,
-                               void *cb_cls)
+static enum GNUNET_DB_QueryStatus
+postgres_get_coin_transactions (void *cls,
+                                struct TALER_EXCHANGEDB_Session *session,
+                                const struct TALER_CoinSpendPublicKeyP 
*coin_pub,
+                               struct TALER_EXCHANGEDB_TransactionList **tlp)
 {
-  PGresult *result;
+  struct CoinHistoryContext chc;
   struct GNUNET_PQ_QueryParam params[] = {
-    GNUNET_PQ_query_param_auto_from_type (wtid),
+    GNUNET_PQ_query_param_auto_from_type (coin_pub),
     GNUNET_PQ_query_param_end
   };
-  int nrows;
+  enum GNUNET_DB_QueryStatus qs;
+  struct {
+    /**
+     * SQL prepared statement name.
+     */
+    const char *statement;
+
+    /**
+     * Function to call to handle the result(s).
+     */
+    GNUNET_PQ_PostgresResultHandler cb;
+  } work[] = {
+    /** #TALER_EXCHANGEDB_TT_DEPOSIT */
+    { "get_deposit_with_coin_pub",
+      &add_coin_deposit },
+    /** #TALER_EXCHANGEDB_TT_REFRESH_MELT */
+    { "get_refresh_session_by_coin",
+      &add_coin_melt },
+    /** #TALER_EXCHANGEDB_TT_REFUND */
+    { "get_refunds_by_coin",
+      &add_coin_refund },
+    /** #TALER_EXCHANGEDB_TT_PAYBACK */
+    { "payback_by_coin",
+      &add_coin_payback },
+    { NULL, NULL }
+  };
 
-  /* check if the melt record exists and get it */
-  result = GNUNET_PQ_exec_prepared (session->conn,
-                                    "lookup_transactions",
-                                    params);
-  if (PGRES_TUPLES_OK != PQresultStatus (result))
-  {
-    BREAK_DB_ERR (result, session->conn);
-    PQclear (result);
-    return GNUNET_SYSERR;
-  }
-  nrows = PQntuples (result);
-  if (0 == nrows)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "lookup_wire_transfer() returned 0 matching rows\n");
-    PQclear (result);
-    return GNUNET_NO;
+  chc.head = NULL;
+  chc.status = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
+  chc.coin_pub = coin_pub;
+  chc.session = session;
+  chc.db_cls = cls;
+  for (unsigned int i=0;NULL != work[i].statement; i++)
+  {
+    qs = GNUNET_PQ_eval_prepared_multi_select (session->conn,
+                                              work[i].statement,
+                                              params,
+                                              work[i].cb,
+                                              &chc);
+    if ( (0 > qs) ||
+        (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != chc.status) )
+    {
+      if (NULL != chc.head)
+       common_free_coin_transaction_list (cls,
+                                          chc.head);
+      *tlp = NULL;
+      if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != chc.status)
+       qs = chc.status;
+      return qs;
+    }
   }
-  for (int i=0;i<nrows;i++)
+  *tlp = chc.head;
+  if (NULL == chc.head)
+    return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
+  return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
+}
+
+
+/**
+ * Closure for #handle_wt_result.
+ */
+struct WireTransferResultContext
+{
+  /**
+   * Function to call on each result.
+   */
+  TALER_EXCHANGEDB_WireTransferDataCallback cb;
+
+  /**
+   * Closure for @e cb.
+   */
+  void *cb_cls;
+
+  /**
+   * Set to #GNUNET_SYSERR on serious errors.
+   */
+  int status;
+};
+
+
+/**
+ * Function to be called with the results of a SELECT statement
+ * that has returned @a num_results results.  Helper function
+ * for #postgres_lookup_wire_transfer().
+ *
+ * @param cls closure of type `struct WireTransferResultContext *`
+ * @param result the postgres result
+ * @param num_result the number of results in @a result
+ */
+static void
+handle_wt_result (void *cls,
+                 PGresult *result,
+                 unsigned int num_results)
+{
+  struct WireTransferResultContext *ctx = cls;
+  
+  for (unsigned int i=0;i<num_results;i++)
   {
     uint64_t rowid;
     struct GNUNET_HashCode h_contract_terms;
@@ -4591,37 +4159,75 @@ postgres_lookup_wire_transfer (void *cls,
                                   i))
     {
       GNUNET_break (0);
-      PQclear (result);
-      return GNUNET_SYSERR;
+      ctx->status = GNUNET_SYSERR;
+      return;
     }
     t = json_object_get (wire, "type");
     if (NULL == t)
     {
       GNUNET_break (0);
-      PQclear (result);
-      return GNUNET_SYSERR;
+      ctx->status = GNUNET_SYSERR;
+      return;
     }
     wire_method = json_string_value (t);
     if (NULL == wire_method)
     {
       GNUNET_break (0);
-      PQclear (result);
-      return GNUNET_SYSERR;
+      ctx->status = GNUNET_SYSERR;
+      return;
     }
-    cb (cb_cls,
-        rowid,
-        &merchant_pub,
-        wire_method,
-        &h_wire,
-        exec_time,
-        &h_contract_terms,
-        &coin_pub,
-        &amount_with_fee,
-        &deposit_fee);
+    ctx->cb (ctx->cb_cls,
+            rowid,
+            &merchant_pub,
+            wire_method,
+            &h_wire,
+            exec_time,
+            &h_contract_terms,
+            &coin_pub,
+            &amount_with_fee,
+            &deposit_fee);
     GNUNET_PQ_cleanup_result (rs);
   }
-  PQclear (result);
-  return GNUNET_OK;
+}
+
+
+/**
+ * Lookup the list of Taler transactions that were aggregated
+ * into a wire transfer by the respective @a wtid.
+ *
+ * @param cls closure
+ * @param session database connection
+ * @param wtid the raw wire transfer identifier we used
+ * @param cb function to call on each transaction found
+ * @param cb_cls closure for @a cb
+ * @return query status of the transaction
+ */
+static enum GNUNET_DB_QueryStatus
+postgres_lookup_wire_transfer (void *cls,
+                               struct TALER_EXCHANGEDB_Session *session,
+                               const struct TALER_WireTransferIdentifierRawP 
*wtid,
+                               TALER_EXCHANGEDB_WireTransferDataCallback cb,
+                               void *cb_cls)
+{
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_auto_from_type (wtid),
+    GNUNET_PQ_query_param_end
+  };
+  struct WireTransferResultContext ctx;
+  enum GNUNET_DB_QueryStatus qs;
+  
+  ctx.cb = cb;
+  ctx.cb_cls = cb_cls;
+  ctx.status = GNUNET_OK;
+  /* check if the melt record exists and get it */
+  qs = GNUNET_PQ_eval_prepared_multi_select (session->conn,
+                                            "lookup_transactions",
+                                            params,
+                                            &handle_wt_result,
+                                            &ctx);
+  if (GNUNET_OK != ctx.status)
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  return qs;
 }
 
 
@@ -4638,10 +4244,9 @@ postgres_lookup_wire_transfer (void *cls,
  * @param merchant_pub merchant public key
  * @param cb function to call with the result
  * @param cb_cls closure to pass to @a cb
- * @return #GNUNET_OK on success, #GNUNET_SYSERR on DB errors,
- *         #GNUNET_NO if nothing was found
+ * @return transaction status code
  */
-static int
+static enum GNUNET_DB_QueryStatus
 postgres_wire_lookup_deposit_wtid (void *cls,
                                    struct TALER_EXCHANGEDB_Session *session,
                                   const struct GNUNET_HashCode 
*h_contract_terms,
@@ -4651,7 +4256,7 @@ postgres_wire_lookup_deposit_wtid (void *cls,
                                   TALER_EXCHANGEDB_TrackTransactionCallback cb,
                                   void *cb_cls)
 {
-  PGresult *result;
+  enum GNUNET_DB_QueryStatus qs;
   struct GNUNET_PQ_QueryParam params[] = {
     GNUNET_PQ_query_param_auto_from_type (coin_pub),
     GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
@@ -4659,122 +4264,76 @@ postgres_wire_lookup_deposit_wtid (void *cls,
     GNUNET_PQ_query_param_auto_from_type (merchant_pub),
     GNUNET_PQ_query_param_end
   };
-  int nrows;
-
+  struct TALER_WireTransferIdentifierRawP wtid;
+  struct GNUNET_TIME_Absolute exec_time;
+  struct TALER_Amount amount_with_fee;
+  struct TALER_Amount deposit_fee;
+  struct GNUNET_PQ_ResultSpec rs[] = {
+    GNUNET_PQ_result_spec_auto_from_type ("wtid_raw", &wtid),
+    GNUNET_PQ_result_spec_absolute_time ("execution_date", &exec_time),
+    TALER_PQ_result_spec_amount ("amount_with_fee", &amount_with_fee),
+    TALER_PQ_result_spec_amount ("fee_deposit", &deposit_fee),
+    GNUNET_PQ_result_spec_end
+  };
+  
   /* check if the melt record exists and get it */
-  result = GNUNET_PQ_exec_prepared (session->conn,
-                                    "lookup_deposit_wtid",
-                                    params);
-  if (PGRES_TUPLES_OK != PQresultStatus (result))
+  qs = GNUNET_PQ_eval_prepared_singleton_select (session->conn,
+                                                "lookup_deposit_wtid",
+                                                params,
+                                                rs);
+  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
   {
-    BREAK_DB_ERR (result, session->conn);
-    PQclear (result);
-    return GNUNET_SYSERR;
+    cb (cb_cls,
+        &wtid,
+        &amount_with_fee,
+        &deposit_fee,
+        exec_time);
+    return qs;
   }
-  nrows = PQntuples (result);
-  if (0 == nrows)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "lookup_deposit_wtid returned 0 matching rows\n");
-    PQclear (result);
+  if (0 > qs)
+    return qs;
 
+  GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs);
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+             "lookup_deposit_wtid returned 0 matching rows\n");
+  {
     /* Check if transaction exists in deposits, so that we just
        do not have a WTID yet, if so, do call the CB with a NULL wtid
        and return #GNUNET_YES! */
-    {
-      struct GNUNET_PQ_QueryParam params2[] = {
-        GNUNET_PQ_query_param_auto_from_type (coin_pub),
-        GNUNET_PQ_query_param_auto_from_type (merchant_pub),
-        GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
-        GNUNET_PQ_query_param_auto_from_type (h_wire),
-        GNUNET_PQ_query_param_end
-      };
-
-      result = GNUNET_PQ_exec_prepared (session->conn,
-                                        "get_deposit_for_wtid",
-                                        params2);
-      if (PGRES_TUPLES_OK != PQresultStatus (result))
-      {
-        BREAK_DB_ERR (result, session->conn);
-        PQclear (result);
-        return GNUNET_SYSERR;
-      }
-    }
-    nrows = PQntuples (result);
-    if (0 == nrows)
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  "get_deposit_for_wtid returned 0 matching rows\n");
-      PQclear (result);
-      return GNUNET_NO;
-    }
-
-    /* Ok, we're aware of the transaction, but it has not yet been
-       executed */
-    {
-      struct GNUNET_TIME_Absolute exec_time;
-      struct TALER_Amount amount_with_fee;
-      struct TALER_Amount deposit_fee;
-      struct GNUNET_PQ_ResultSpec rs[] = {
-        TALER_PQ_result_spec_amount ("amount_with_fee", &amount_with_fee),
-        TALER_PQ_result_spec_amount ("fee_deposit", &deposit_fee),
-        GNUNET_PQ_result_spec_absolute_time ("wire_deadline", &exec_time),
-        GNUNET_PQ_result_spec_end
-      };
-
-      if (GNUNET_OK !=
-          GNUNET_PQ_extract_result (result,
-                                    rs,
-                                    0))
-      {
-        GNUNET_break (0);
-        PQclear (result);
-        return GNUNET_SYSERR;
-      }
-      cb (cb_cls,
-          NULL,
-          &amount_with_fee,
-          &deposit_fee,
-          exec_time);
-      PQclear (result);
-      return GNUNET_YES;
-    }
-  }
-  if (1 != nrows)
-  {
-    GNUNET_break (0);
-    PQclear (result);
-    return GNUNET_SYSERR;
-  }
-  {
-    struct TALER_WireTransferIdentifierRawP wtid;
+    struct GNUNET_PQ_QueryParam params2[] = {
+      GNUNET_PQ_query_param_auto_from_type (coin_pub),
+      GNUNET_PQ_query_param_auto_from_type (merchant_pub),
+      GNUNET_PQ_query_param_auto_from_type (h_contract_terms),
+      GNUNET_PQ_query_param_auto_from_type (h_wire),
+      GNUNET_PQ_query_param_end
+    };
     struct GNUNET_TIME_Absolute exec_time;
     struct TALER_Amount amount_with_fee;
     struct TALER_Amount deposit_fee;
-    struct GNUNET_PQ_ResultSpec rs[] = {
-      GNUNET_PQ_result_spec_auto_from_type ("wtid_raw", &wtid),
-      GNUNET_PQ_result_spec_absolute_time ("execution_date", &exec_time),
+    struct GNUNET_PQ_ResultSpec rs2[] = {
       TALER_PQ_result_spec_amount ("amount_with_fee", &amount_with_fee),
       TALER_PQ_result_spec_amount ("fee_deposit", &deposit_fee),
+      GNUNET_PQ_result_spec_absolute_time ("wire_deadline", &exec_time),
       GNUNET_PQ_result_spec_end
     };
-    if (GNUNET_OK !=
-        GNUNET_PQ_extract_result (result,
-                                  rs,
-                                  0))
+
+    qs = GNUNET_PQ_eval_prepared_singleton_select (session->conn,
+                                                  "get_deposit_for_wtid",
+                                                  params2,
+                                                  rs2);
+    if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
     {
-      GNUNET_break (0);
-      PQclear (result);
-      return GNUNET_SYSERR;
+      /* Ok, we're aware of the transaction, but it has not yet been
+        executed */
+      cb (cb_cls,
+          NULL,
+          &amount_with_fee,
+          &deposit_fee,
+          exec_time);
+      return qs;
     }
-    cb (cb_cls,
-        &wtid,
-        &amount_with_fee,
-        &deposit_fee,
-        exec_time);
+    return qs;
   }
-  PQclear (result);
-  return GNUNET_OK;
 }
 
 
@@ -4785,11 +4344,9 @@ postgres_wire_lookup_deposit_wtid (void *cls,
  * @param session database connection
  * @param wtid the raw wire transfer identifier we used
  * @param deposit_serial_id row in the deposits table for which this is 
aggregation data
- * @return #GNUNET_OK on success,
- *         #GNUNET_NO on transient errors
- *         #GNUNET_SYSERR on DB errors
+ * @return transaction status code
  */
-static int
+static enum GNUNET_DB_QueryStatus
 postgres_insert_aggregation_tracking (void *cls,
                                       struct TALER_EXCHANGEDB_Session *session,
                                       const struct 
TALER_WireTransferIdentifierRawP *wtid,
@@ -4802,9 +4359,9 @@ postgres_insert_aggregation_tracking (void *cls,
     GNUNET_PQ_query_param_end
   };
 
-  return execute_prepared_non_select (session,
-                                      "insert_aggregation_tracking",
-                                      params);
+  return GNUNET_PQ_eval_prepared_non_select (session->conn,
+                                            "insert_aggregation_tracking",
+                                            params);
 }
 
 
@@ -4819,10 +4376,9 @@ postgres_insert_aggregation_tracking (void *cls,
  * @param[out] end_date when does the fee end being valid
  * @param[out] wire_fee how high is the wire transfer fee
  * @param[out] master_sig signature over the above by the exchange master key
- * @return #GNUNET_OK on success, #GNUNET_NO if no fee is known
- *         #GNUNET_SYSERR on failure
+ * @return status of the transaction
  */
-static int
+static enum GNUNET_DB_QueryStatus
 postgres_get_wire_fee (void *cls,
                        struct TALER_EXCHANGEDB_Session *session,
                        const char *type,
@@ -4844,42 +4400,11 @@ postgres_get_wire_fee (void *cls,
     GNUNET_PQ_result_spec_auto_from_type ("master_sig", master_sig),
     GNUNET_PQ_result_spec_end
   };
-  PGresult *result;
-  int nrows;
 
-  result = GNUNET_PQ_exec_prepared (session->conn,
-                                    "get_wire_fee",
-                                    params);
-  if (PGRES_TUPLES_OK !=
-      PQresultStatus (result))
-  {
-    BREAK_DB_ERR (result, session->conn);
-    PQclear (result);
-    return GNUNET_SYSERR;
-  }
-  nrows = PQntuples (result);
-  if (0 == nrows)
-  {
-    /* no matches found */
-    PQclear (result);
-    return GNUNET_NO;
-  }
-  if (1 != nrows)
-  {
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
-  }
-  if (GNUNET_OK !=
-      GNUNET_PQ_extract_result (result,
-                                rs,
-                                0))
-  {
-    PQclear (result);
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
-  }
-  PQclear (result);
-  return GNUNET_OK;
+  return GNUNET_PQ_eval_prepared_singleton_select (session->conn,
+                                                  "get_wire_fee",
+                                                  params,
+                                                  rs);
 }
 
 
@@ -4893,11 +4418,9 @@ postgres_get_wire_fee (void *cls,
  * @param end_date when does the fee end being valid
  * @param wire_fee how high is the wire transfer fee
  * @param master_sig signature over the above by the exchange master key
- * @return #GNUNET_OK on success or if the record exists,
- *         #GNUNET_NO on transient errors
- *         #GNUNET_SYSERR on failure
+ * @return transaction status code
  */
-static int
+static enum GNUNET_DB_QueryStatus
 postgres_insert_wire_fee (void *cls,
                           struct TALER_EXCHANGEDB_Session *session,
                           const char *type,
@@ -4918,98 +4441,94 @@ postgres_insert_wire_fee (void *cls,
   struct TALER_MasterSignatureP sig;
   struct GNUNET_TIME_Absolute sd;
   struct GNUNET_TIME_Absolute ed;
-
-  if (GNUNET_OK ==
-      postgres_get_wire_fee (cls,
-                             session,
-                             type,
-                             start_date,
-                             &sd,
-                             &ed,
-                             &wf,
-                             &sig))
+  enum GNUNET_DB_QueryStatus qs;
+
+  qs = postgres_get_wire_fee (cls,
+                             session,
+                             type,
+                             start_date,
+                             &sd,
+                             &ed,
+                             &wf,
+                             &sig);
+  if (qs < 0)
+    return qs;
+  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
   {
     if (0 != memcmp (&sig,
                      master_sig,
                      sizeof (sig)))
     {
       GNUNET_break (0);
-      return GNUNET_SYSERR;
+      return GNUNET_DB_STATUS_HARD_ERROR;
     }
     if (0 != TALER_amount_cmp (wire_fee,
                                &wf))
     {
       GNUNET_break (0);
-      return GNUNET_SYSERR;
+      return GNUNET_DB_STATUS_HARD_ERROR;
     }
     if ( (sd.abs_value_us != start_date.abs_value_us) ||
          (ed.abs_value_us != end_date.abs_value_us) )
     {
       GNUNET_break (0);
-      return GNUNET_SYSERR;
+      return GNUNET_DB_STATUS_HARD_ERROR;
     }
     /* equal record already exists */
-    return GNUNET_OK;
+    return GNUNET_DB_STATUS_SUCCESS_NO_RESULTS;
   }
 
-  return execute_prepared_non_select (session,
-                                      "insert_wire_fee",
-                                      params);
+  return GNUNET_PQ_eval_prepared_non_select (session->conn,
+                                            "insert_wire_fee",
+                                            params);
 }
 
 
 /**
- * Obtain information about expired reserves and their
- * remaining balances.
- *
- * @param cls closure of the plugin
- * @param session database connection
- * @param now timestamp based on which we decide expiration
- * @param rec function to call on expired reserves
- * @param rec_cls closure for @a rec
- * @return #GNUNET_SYSERR on database error
- *         #GNUNET_NO if there are no expired non-empty reserves
- *         #GNUNET_OK on success
+ * Closure for #reserve_expired_cb().
  */
-static int
-postgres_get_expired_reserves (void *cls,
-                              struct TALER_EXCHANGEDB_Session *session,
-                              struct GNUNET_TIME_Absolute now,
-                              TALER_EXCHANGEDB_ReserveExpiredCallback rec,
-                              void *rec_cls)
+struct ExpiredReserveContext
 {
-  struct GNUNET_PQ_QueryParam params[] = {
-    GNUNET_PQ_query_param_absolute_time (&now),
-    GNUNET_PQ_query_param_end
-  };
-  PGresult *result;
-  int nrows;
+  /**
+   * Function to call for each expired reserve.
+   */
+  TALER_EXCHANGEDB_ReserveExpiredCallback rec;
 
-  result = GNUNET_PQ_exec_prepared (session->conn,
-                                    "get_expired_reserves",
-                                    params);
-  if (PGRES_TUPLES_OK !=
-      PQresultStatus (result))
-  {
-    BREAK_DB_ERR (result, session->conn);
-    PQclear (result);
-    return GNUNET_SYSERR;
-  }
-  nrows = PQntuples (result);
-  if (0 == nrows)
-  {
-    /* no matches found */
-    PQclear (result);
-    return GNUNET_NO;
-  }
+  /**
+   * Closure to give to @e rec.
+   */
+  void *rec_cls;
+
+  /**
+   * Set to #GNUNET_SYSERR on error.
+   */
+  int status;
+};
+
+
+/**
+ * Function to be called with the results of a SELECT statement
+ * that has returned @a num_results results.
+ *
+ * @param cls closure
+ * @param result the postgres result
+ * @param num_result the number of results in @a result
+ */
+static void
+reserve_expired_cb (void *cls,
+                   PGresult *result,
+                   unsigned int num_results)
+{
+  struct ExpiredReserveContext *erc = cls;
+  int ret;
 
-  for (int i=0;i<nrows;i++)
+  ret = GNUNET_OK;
+  for (unsigned int i=0;i<num_results;i++)
   {
     struct GNUNET_TIME_Absolute exp_date;
     json_t *account_details;
     struct TALER_ReservePublicKeyP reserve_pub;
     struct TALER_Amount remaining_balance;
-    int ret;
     struct GNUNET_PQ_ResultSpec rs[] = {
       GNUNET_PQ_result_spec_absolute_time ("expiration_date",
                                           &exp_date),
@@ -5027,21 +4546,59 @@ postgres_get_expired_reserves (void *cls,
                                  rs,
                                  i))
     {
-      PQclear (result);
       GNUNET_break (0);
-      return GNUNET_SYSERR;
+      ret = GNUNET_SYSERR;
+      break;
     }
-    ret = rec (rec_cls,
-              &reserve_pub,
-              &remaining_balance,
-              account_details,
-              exp_date);
+    ret = erc->rec (erc->rec_cls,
+                   &reserve_pub,
+                   &remaining_balance,
+                   account_details,
+                   exp_date);
     GNUNET_PQ_cleanup_result (rs);
     if (GNUNET_OK != ret)
       break;
   }
-  PQclear (result);
-  return GNUNET_OK;
+  erc->status = ret;
+}
+
+
+/**
+ * Obtain information about expired reserves and their
+ * remaining balances.
+ *
+ * @param cls closure of the plugin
+ * @param session database connection
+ * @param now timestamp based on which we decide expiration
+ * @param rec function to call on expired reserves
+ * @param rec_cls closure for @a rec
+ * @return transaction status
+ */
+static enum GNUNET_DB_QueryStatus
+postgres_get_expired_reserves (void *cls,
+                              struct TALER_EXCHANGEDB_Session *session,
+                              struct GNUNET_TIME_Absolute now,
+                              TALER_EXCHANGEDB_ReserveExpiredCallback rec,
+                              void *rec_cls)
+{
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_absolute_time (&now),
+    GNUNET_PQ_query_param_end
+  };
+  struct ExpiredReserveContext ectx;
+  enum GNUNET_DB_QueryStatus qs;
+  
+  ectx.rec = rec;
+  ectx.rec_cls = rec_cls;
+  ectx.status = GNUNET_OK;
+  qs = GNUNET_PQ_eval_prepared_multi_select (session->conn,
+                                            "get_expired_reserves",
+                                            params,
+                                            &reserve_expired_cb,
+                                            &ectx);
+  if (GNUNET_OK != ectx.status)
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  return qs;
 }
 
 
@@ -5056,11 +4613,9 @@ postgres_get_expired_reserves (void *cls,
  * @param wtid wire transfer details
  * @param amount_with_fee amount we charged to the reserve
  * @param closing_fee how high is the closing fee
- * @return #GNUNET_OK on success,
- *         #GNUNET_NO if the record exists or on transient errors
- *         #GNUNET_SYSERR on failure
+ * @return transaction status code
  */
-static int
+static enum GNUNET_DB_QueryStatus
 postgres_insert_reserve_closed (void *cls,
                                struct TALER_EXCHANGEDB_Session *session,
                                const struct TALER_ReservePublicKeyP 
*reserve_pub,
@@ -5081,23 +4636,26 @@ postgres_insert_reserve_closed (void *cls,
     GNUNET_PQ_query_param_end
   };
   int ret;
+  enum GNUNET_DB_QueryStatus qs;
 
-  ret = execute_prepared_non_select (session,
-                                     "reserves_close_insert",
-                                     params);
-  if (GNUNET_OK != ret)
-    return ret;
+  qs = GNUNET_PQ_eval_prepared_non_select (session->conn,
+                                          "reserves_close_insert",
+                                          params);
+  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
+    return qs;
 
   /* update reserve balance */
   reserve.pub = *reserve_pub;
-  if (GNUNET_OK !=
-      (ret = postgres_reserve_get (cls,
-                                   session,
-                                   &reserve)))
+  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+      (qs = postgres_reserve_get (cls,
+                                  session,
+                                  &reserve)))
   {
     /* Existence should have been checked before we got here... */
-    GNUNET_break (GNUNET_NO == ret);
-    return ret;
+    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+    if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
+      qs = GNUNET_DB_STATUS_HARD_ERROR;
+    return qs;
   }
   ret = TALER_amount_subtract (&reserve.balance,
                               &reserve.balance,
@@ -5110,18 +4668,12 @@ postgres_insert_reserve_closed (void *cls,
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                 "Closing of reserve `%s' refused due to balance missmatch. 
Retrying.\n",
                 TALER_B2S (reserve_pub));
-    return GNUNET_NO;
+    return GNUNET_DB_STATUS_HARD_ERROR;
   }
   GNUNET_break (GNUNET_NO == ret);
-  ret = reserves_update (cls,
-                         session,
-                         &reserve);
-  if (GNUNET_SYSERR == ret)
-  {
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
-  }
-  return ret;
+  return reserves_update (cls,
+                         session,
+                         &reserve);
 }
 
 
@@ -5133,9 +4685,9 @@ postgres_insert_reserve_closed (void *cls,
  * @param type type of the wire transfer (i.e. "sepa")
  * @param buf buffer with wire transfer preparation data
  * @param buf_size number of bytes in @a buf
- * @return #GNUNET_OK on success, #GNUNET_SYSERR on DB errors
+ * @return query status code
  */
-static int
+static enum GNUNET_DB_QueryStatus
 postgres_wire_prepare_data_insert (void *cls,
                                    struct TALER_EXCHANGEDB_Session *session,
                                    const char *type,
@@ -5148,9 +4700,9 @@ postgres_wire_prepare_data_insert (void *cls,
     GNUNET_PQ_query_param_end
   };
 
-  return execute_prepared_non_select (session,
-                                      "wire_prepare_data_insert",
-                                      params);
+  return GNUNET_PQ_eval_prepared_non_select (session->conn,
+                                            "wire_prepare_data_insert",
+                                            params);
 }
 
 
@@ -5160,9 +4712,9 @@ postgres_wire_prepare_data_insert (void *cls,
  * @param cls closure
  * @param session database connection
  * @param rowid which entry to mark as finished
- * @return #GNUNET_OK on success, #GNUNET_SYSERR on DB errors
+ * @return transaction status code
  */
-static int
+static enum GNUNET_DB_QueryStatus
 postgres_wire_prepare_data_mark_finished (void *cls,
                                           struct TALER_EXCHANGEDB_Session 
*session,
                                           uint64_t rowid)
@@ -5172,9 +4724,9 @@ postgres_wire_prepare_data_mark_finished (void *cls,
     GNUNET_PQ_query_param_end
   };
 
-  return execute_prepared_non_select (session,
-                                      "wire_prepare_data_mark_done",
-                                      params);
+  return GNUNET_PQ_eval_prepared_non_select (session->conn,
+                                            "wire_prepare_data_mark_done",
+                                            params);
 }
 
 
@@ -5186,76 +4738,46 @@ postgres_wire_prepare_data_mark_finished (void *cls,
  * @param session database connection
  * @param cb function to call for ONE unfinished item
  * @param cb_cls closure for @a cb
- * @return #GNUNET_OK on success,
- *         #GNUNET_NO if there are no entries,
- *         #GNUNET_SYSERR on DB errors
+ * @return transaction status code
  */
-static int
+static enum GNUNET_DB_QueryStatus
 postgres_wire_prepare_data_get (void *cls,
                                 struct TALER_EXCHANGEDB_Session *session,
                                 TALER_EXCHANGEDB_WirePreparationIterator cb,
                                 void *cb_cls)
 {
-  PGresult *result;
+  enum GNUNET_DB_QueryStatus qs;
   struct GNUNET_PQ_QueryParam params[] = {
     GNUNET_PQ_query_param_end
   };
+  uint64_t prewire_uuid;
+  char *type;
+  void *buf = NULL;
+  size_t buf_size;
+  struct GNUNET_PQ_ResultSpec rs[] = {
+    GNUNET_PQ_result_spec_uint64 ("prewire_uuid",
+                                 &prewire_uuid),
+    GNUNET_PQ_result_spec_string ("type",
+                                 &type),
+    GNUNET_PQ_result_spec_variable_size ("buf",
+                                        &buf,
+                                        &buf_size),
+    GNUNET_PQ_result_spec_end
+  };
 
-  result = GNUNET_PQ_exec_prepared (session->conn,
-                                   "wire_prepare_data_get",
-                                   params);
-  if (PGRES_TUPLES_OK != PQresultStatus (result))
-  {
-    QUERY_ERR (result, session->conn);
-    PQclear (result);
-    return GNUNET_SYSERR;
-  }
-  if (0 == PQntuples (result))
-  {
-    PQclear (result);
-    return GNUNET_NO;
-  }
-  if (1 != PQntuples (result))
-  {
-    GNUNET_break (0);
-    PQclear (result);
-    return GNUNET_SYSERR;
-  }
-
-  {
-    uint64_t prewire_uuid;
-    char *type;
-    void *buf = NULL;
-    size_t buf_size;
-    struct GNUNET_PQ_ResultSpec rs[] = {
-      GNUNET_PQ_result_spec_uint64 ("prewire_uuid",
-                                    &prewire_uuid),
-      GNUNET_PQ_result_spec_string ("type",
-                                    &type),
-      GNUNET_PQ_result_spec_variable_size ("buf",
-                                           &buf,
-                                           &buf_size),
-      GNUNET_PQ_result_spec_end
-    };
-
-    if (GNUNET_OK !=
-        GNUNET_PQ_extract_result (result,
-                                 rs,
-                                 0))
-    {
-      GNUNET_break (0);
-      PQclear (result);
-      return GNUNET_SYSERR;
-    }
-    cb (cb_cls,
-        prewire_uuid,
-        type,
-        buf,
-        buf_size);
-    GNUNET_PQ_cleanup_result (rs);
-  }
-  PQclear (result);
-  return GNUNET_OK;
+  qs = GNUNET_PQ_eval_prepared_singleton_select (session->conn,
+                                                "wire_prepare_data_get",
+                                                params,
+                                                rs);
+  if (0 >= qs)
+    return qs;
+  cb (cb_cls,
+      prewire_uuid,
+      type,
+      buf,
+      buf_size);
+  GNUNET_PQ_cleanup_result (rs);
+  return qs;
 }
 
 
@@ -5301,7 +4823,6 @@ postgres_start_deferred_wire_out (void *cls,
     return GNUNET_SYSERR;
   }
   PQclear (result);
-  session->state = GNUNET_OK;
   return GNUNET_OK;
 }
 
@@ -5315,10 +4836,9 @@ postgres_start_deferred_wire_out (void *cls,
  * @param wtid subject of the wire transfer
  * @param wire_account details about the receiver account of the wire transfer
  * @param amount amount that was transmitted
- * @return #GNUNET_OK on success
- *         #GNUNET_SYSERR on DB errors
+ * @return transaction status code
  */
-static int
+static enum GNUNET_DB_QueryStatus
 postgres_store_wire_transfer_out (void *cls,
                                   struct TALER_EXCHANGEDB_Session *session,
                                   struct GNUNET_TIME_Absolute date,
@@ -5334,9 +4854,9 @@ postgres_store_wire_transfer_out (void *cls,
     GNUNET_PQ_query_param_end
   };
 
-  return execute_prepared_non_select (session,
-                                      "insert_wire_out",
-                                      params);
+  return GNUNET_PQ_eval_prepared_non_select (session->conn,
+                                            "insert_wire_out",
+                                            params);
 }
 
 
@@ -5361,102 +4881,70 @@ postgres_gc (void *cls)
     GNUNET_PQ_query_param_end
   };
   PGconn *conn;
-  PGresult *result;
-
+  int ret;
+  
   now = GNUNET_TIME_absolute_get ();
   conn = GNUNET_PQ_connect (pc->connection_cfg_str);
   if (NULL == conn)
     return GNUNET_SYSERR;
-  if (GNUNET_OK !=
-      postgres_prepare (conn))
-  {
-    PQfinish (conn);
-    return GNUNET_SYSERR;
-  }
-  result = GNUNET_PQ_exec_prepared (conn,
-                                    "gc_prewire",
-                                    params_none);
-  if (PGRES_COMMAND_OK != PQresultStatus (result))
-  {
-    BREAK_DB_ERR (result, conn);
-    PQclear (result);
-    PQfinish (conn);
-    return GNUNET_SYSERR;
-  }
-  PQclear (result);
-  result = GNUNET_PQ_exec_prepared (conn,
-                                    "gc_denominations",
-                                    params_time);
-  if (PGRES_COMMAND_OK != PQresultStatus (result))
-  {
-    BREAK_DB_ERR (result, conn);
-    PQclear (result);
-    PQfinish (conn);
-    return GNUNET_SYSERR;
-  }
-  PQclear (result);
-  result = GNUNET_PQ_exec_prepared (conn,
-                                    "gc_reserves",
-                                    params_time);
-  if (PGRES_COMMAND_OK != PQresultStatus (result))
+  ret = postgres_prepare (conn);
+  if (GNUNET_OK == ret)
   {
-    BREAK_DB_ERR (result, conn);
-    PQclear (result);
-    PQfinish (conn);
-    return GNUNET_SYSERR;
+    if ( (0 > GNUNET_PQ_eval_prepared_non_select (conn,
+                                                 "gc_prewire",
+                                                 params_none)) ||
+        (0 > GNUNET_PQ_eval_prepared_non_select (conn,
+                                                 "gc_denominations",
+                                                 params_time)) ||
+        (0 > GNUNET_PQ_eval_prepared_non_select (conn,
+                                                 "gc_reserves",
+                                                 params_time)) )
+      ret = GNUNET_SYSERR;
   }
-  PQclear (result);
   PQfinish (conn);
-  return GNUNET_OK;
+  return ret;
 }
 
 
 /**
- * Select deposits above @a serial_id in monotonically increasing
- * order.
- *
- * @param cls closure
- * @param session database connection
- * @param serial_id highest serial ID to exclude (select strictly larger)
- * @param cb function to call on each result
- * @param cb_cls closure for @a cb
- * @return #GNUNET_OK on success,
- *         #GNUNET_SYSERR on DB errors
+ * Closure for #deposit_serial_helper_cb().
  */
-static int
-postgres_select_deposits_above_serial_id (void *cls,
-                                          struct TALER_EXCHANGEDB_Session 
*session,
-                                          uint64_t serial_id,
-                                          TALER_EXCHANGEDB_DepositCallback cb,
-                                          void *cb_cls)
+struct DepositSerialContext
 {
-  struct GNUNET_PQ_QueryParam params[] = {
-    GNUNET_PQ_query_param_uint64 (&serial_id),
-    GNUNET_PQ_query_param_end
-  };
-  PGresult *result;
-  result = GNUNET_PQ_exec_prepared (session->conn,
-                                    "audit_get_deposits_incr",
-                                    params);
-  if (PGRES_TUPLES_OK !=
-      PQresultStatus (result))
-  {
-    BREAK_DB_ERR (result, session->conn);
-    PQclear (result);
-    return GNUNET_SYSERR;
-  }
-  int nrows;
-  int ret;
 
-  nrows = PQntuples (result);
-  if (0 == nrows)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "select_deposits_above_serial_id() returned 0 matching 
rows\n");
-    PQclear (result);
-    return GNUNET_NO;
-  }
-  for (int i=0;i<nrows;i++)
+  /**
+   * Callback to call.
+   */
+  TALER_EXCHANGEDB_DepositCallback cb;
+  
+  /**
+   * Closure for @e cb.
+   */
+  void *cb_cls;
+  
+  /**
+   * Status code, set to #GNUNET_SYSERR on hard errors.
+   */ 
+  int status;
+};
+
+
+/**
+ * Helper function to be called with the results of a SELECT statement
+ * that has returned @a num_results results.
+ *
+ * @param cls closure of type `struct DepositSerialContext`
+ * @param result the postgres result
+ * @param num_result the number of results in @a result
+ */
+static void
+deposit_serial_helper_cb (void *cls,
+                         PGresult *result,
+                         unsigned int num_results)
+{
+  struct DepositSerialContext *dsc = cls;
+  
+  for (unsigned int i=0;i<num_results;i++)
   {
     struct TALER_EXCHANGEDB_Deposit deposit;
     struct TALER_DenominationPublicKey denom_pub;
@@ -5489,39 +4977,39 @@ postgres_select_deposits_above_serial_id (void *cls,
                                     &rowid),
       GNUNET_PQ_result_spec_end
     };
+    int ret;
+    
     if (GNUNET_OK !=
         GNUNET_PQ_extract_result (result,
                                   rs,
                                   i))
     {
       GNUNET_break (0);
-      PQclear (result);
-      return GNUNET_SYSERR;
+      dsc->status = GNUNET_SYSERR;
+      return;
     }
-    ret = cb (cb_cls,
-              rowid,
-              deposit.timestamp,
-              &deposit.merchant_pub,
-              &denom_pub,
-              &deposit.coin.coin_pub,
-              &deposit.csig,
-              &deposit.amount_with_fee,
-              &deposit.h_contract_terms,
-              deposit.refund_deadline,
-              deposit.wire_deadline,
-              deposit.receiver_wire_account,
-              done);
+    ret = dsc->cb (dsc->cb_cls,
+                  rowid,
+                  deposit.timestamp,
+                  &deposit.merchant_pub,
+                  &denom_pub,
+                  &deposit.coin.coin_pub,
+                  &deposit.csig,
+                  &deposit.amount_with_fee,
+                  &deposit.h_contract_terms,
+                  deposit.refund_deadline,
+                  deposit.wire_deadline,
+                  deposit.receiver_wire_account,
+                  done);
     GNUNET_PQ_cleanup_result (rs);
     if (GNUNET_OK != ret)
       break;
   }
-  PQclear (result);
-  return GNUNET_OK;
 }
 
 
 /**
- * Select refresh sessions above @a serial_id in monotonically increasing
+ * Select deposits above @a serial_id in monotonically increasing
  * order.
  *
  * @param cls closure
@@ -5529,47 +5017,76 @@ postgres_select_deposits_above_serial_id (void *cls,
  * @param serial_id highest serial ID to exclude (select strictly larger)
  * @param cb function to call on each result
  * @param cb_cls closure for @a cb
- * @return #GNUNET_OK on success,
- *         #GNUNET_SYSERR on DB errors
+ * @return transaction status code
  */
-static int
-postgres_select_refreshs_above_serial_id (void *cls,
+static enum GNUNET_DB_QueryStatus
+postgres_select_deposits_above_serial_id (void *cls,
                                           struct TALER_EXCHANGEDB_Session 
*session,
                                           uint64_t serial_id,
-                                          
TALER_EXCHANGEDB_RefreshSessionCallback cb,
+                                          TALER_EXCHANGEDB_DepositCallback cb,
                                           void *cb_cls)
 {
   struct GNUNET_PQ_QueryParam params[] = {
     GNUNET_PQ_query_param_uint64 (&serial_id),
     GNUNET_PQ_query_param_end
   };
-  PGresult *result;
-  int nrows;
-  int i;
-  int ret;
+  struct DepositSerialContext dsc = {
+    .cb = cb,
+    .cb_cls = cb_cls,
+    .status = GNUNET_OK
+  };
+  enum GNUNET_DB_QueryStatus qs;
+
+  qs = GNUNET_PQ_eval_prepared_multi_select (session->conn,
+                                            "audit_get_deposits_incr",
+                                            params,
+                                            &deposit_serial_helper_cb,
+                                            &dsc);
+  if (GNUNET_OK != dsc.status)
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  return qs;
+}
 
-  result = GNUNET_PQ_exec_prepared (session->conn,
-                                    "audit_get_refresh_sessions_incr",
-                                    params);
 
-  if (PGRES_TUPLES_OK !=
-      PQresultStatus (result))
-  {
-    BREAK_DB_ERR (result, session->conn);
-    PQclear (result);
-    return GNUNET_SYSERR;
-  }
+/**
+ * Closure for #refreshs_serial_helper_cb().
+ */
+struct RefreshsSerialContext
+{
+
+  /**
+   * Callback to call.
+   */
+  TALER_EXCHANGEDB_RefreshSessionCallback cb;
+  
+  /**
+   * Closure for @e cb.
+   */
+  void *cb_cls;
+  
+  /**
+   * Status code, set to #GNUNET_SYSERR on hard errors.
+   */ 
+  int status;
+};
 
-  nrows = PQntuples (result);
-  if (0 == nrows)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "select_refreshs_above_serial_id() returned 0 matching 
rows\n");
-    PQclear (result);
-    return GNUNET_NO;
-  }
 
-  for (i=0;i<nrows;i++)
+/**
+ * Helper function to be called with the results of a SELECT statement
+ * that has returned @a num_results results.
+ *
+ * @param cls closure of type `struct RefreshsSerialContext`
+ * @param result the postgres result
+ * @param num_result the number of results in @a result
+ */
+static void
+refreshs_serial_helper_cb (void *cls,
+                          PGresult *result,
+                          unsigned int num_results)
+{
+  struct RefreshsSerialContext *rsc = cls;
+  
+  for (unsigned int i=0;i<num_results;i++)
   {
     struct TALER_DenominationPublicKey denom_pub;
     struct TALER_CoinSpendPublicKeyP coin_pub;
@@ -5579,7 +5096,6 @@ postgres_select_refreshs_above_serial_id (void *cls,
     uint16_t noreveal_index;
     uint64_t rowid;
     struct GNUNET_HashCode session_hash;
-
     struct GNUNET_PQ_ResultSpec rs[] = {
       GNUNET_PQ_result_spec_rsa_public_key ("denom_pub",
                                             &denom_pub.rsa_public_key),
@@ -5599,35 +5115,35 @@ postgres_select_refreshs_above_serial_id (void *cls,
                                             &session_hash),
       GNUNET_PQ_result_spec_end
     };
+    int ret;
+
     if (GNUNET_OK !=
         GNUNET_PQ_extract_result (result,
                                   rs,
                                   i))
     {
       GNUNET_break (0);
-      PQclear (result);
-      return GNUNET_SYSERR;
+      rsc->status = GNUNET_SYSERR;
+      return;
     }
-    ret = cb (cb_cls,
-              rowid,
-              &denom_pub,
-              &coin_pub,
-              &coin_sig,
-              &amount_with_fee,
-              num_newcoins,
-              noreveal_index,
-              &session_hash);
+    ret = rsc->cb (rsc->cb_cls,
+                  rowid,
+                  &denom_pub,
+                  &coin_pub,
+                  &coin_sig,
+                  &amount_with_fee,
+                  num_newcoins,
+                  noreveal_index,
+                  &session_hash);
     GNUNET_PQ_cleanup_result (rs);
     if (GNUNET_OK != ret)
       break;
   }
-  PQclear (result);
-  return GNUNET_OK;
 }
 
 
 /**
- * Select refunds above @a serial_id in monotonically increasing
+ * Select refresh sessions above @a serial_id in monotonically increasing
  * order.
  *
  * @param cls closure
@@ -5635,49 +5151,80 @@ postgres_select_refreshs_above_serial_id (void *cls,
  * @param serial_id highest serial ID to exclude (select strictly larger)
  * @param cb function to call on each result
  * @param cb_cls closure for @a cb
- * @return #GNUNET_OK on success,
- *         #GNUNET_SYSERR on DB errors
+ * @return transaction status code
  */
-static int
-postgres_select_refunds_above_serial_id (void *cls,
-                                         struct TALER_EXCHANGEDB_Session 
*session,
-                                         uint64_t serial_id,
-                                         TALER_EXCHANGEDB_RefundCallback cb,
-                                         void *cb_cls)
+static enum GNUNET_DB_QueryStatus
+postgres_select_refreshs_above_serial_id (void *cls,
+                                          struct TALER_EXCHANGEDB_Session 
*session,
+                                          uint64_t serial_id,
+                                          
TALER_EXCHANGEDB_RefreshSessionCallback cb,
+                                          void *cb_cls)
 {
   struct GNUNET_PQ_QueryParam params[] = {
     GNUNET_PQ_query_param_uint64 (&serial_id),
     GNUNET_PQ_query_param_end
   };
-  PGresult *result;
-  int nrows;
-  int ret;
+  struct RefreshsSerialContext rsc = {
+    .cb = cb,
+    .cb_cls = cb_cls,
+    .status = GNUNET_OK
+  };
+  enum GNUNET_DB_QueryStatus qs;
+
+  qs = GNUNET_PQ_eval_prepared_multi_select (session->conn,
+                                            "audit_get_refresh_sessions_incr",
+                                            params,
+                                            &refreshs_serial_helper_cb,
+                                            &rsc);
+  if (GNUNET_OK != rsc.status)
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  return qs;
+}
 
-  result = GNUNET_PQ_exec_prepared (session->conn,
-                                    "audit_get_refunds_incr",
-                                    params);
-  if (PGRES_TUPLES_OK !=
-      PQresultStatus (result))
-  {
-    BREAK_DB_ERR (result, session->conn);
-    PQclear (result);
-    return GNUNET_SYSERR;
-  }
 
-  nrows = PQntuples (result);
-  if (0 == nrows)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "select_refunds_above_serial_id() returned 0 matching rows\n");
-    PQclear (result);
-    return GNUNET_NO;
-  }
-  for (int i=0;i<nrows;i++)
+/**
+ * Closure for #refunds_serial_helper_cb().
+ */
+struct RefundsSerialContext
+{
+
+  /**
+   * Callback to call.
+   */
+  TALER_EXCHANGEDB_RefundCallback cb;
+  
+  /**
+   * Closure for @e cb.
+   */
+  void *cb_cls;
+  
+  /**
+   * Status code, set to #GNUNET_SYSERR on hard errors.
+   */ 
+  int status;
+};
+
+
+/**
+ * Helper function to be called with the results of a SELECT statement
+ * that has returned @a num_results results.
+ *
+ * @param cls closure of type `struct RefundsSerialContext`
+ * @param result the postgres result
+ * @param num_result the number of results in @a result
+ */
+static void
+refunds_serial_helper_cb (void *cls,
+                         PGresult *result,
+                         unsigned int num_results)
+{
+  struct RefundsSerialContext *rsc = cls;
+  
+  for (unsigned int i=0;i<num_results;i++)
   {
     struct TALER_EXCHANGEDB_Refund refund;
     struct TALER_DenominationPublicKey denom_pub;
     uint64_t rowid;
-
     struct GNUNET_PQ_ResultSpec rs[] = {
       GNUNET_PQ_result_spec_auto_from_type ("merchant_pub",
                                             &refund.merchant_pub),
@@ -5697,80 +5244,112 @@ postgres_select_refunds_above_serial_id (void *cls,
                                     &rowid),
       GNUNET_PQ_result_spec_end
     };
+    int ret;
+    
     if (GNUNET_OK !=
         GNUNET_PQ_extract_result (result,
                                   rs,
                                   i))
     {
       GNUNET_break (0);
-      PQclear (result);
-      return GNUNET_SYSERR;
+      rsc->status = GNUNET_SYSERR;
+      return;
     }
-    ret = cb (cb_cls,
-              rowid,
-              &denom_pub,
-              &refund.coin.coin_pub,
-              &refund.merchant_pub,
-              &refund.merchant_sig,
-              &refund.h_contract_terms,
-              refund.rtransaction_id,
-              &refund.refund_amount);
+    ret = rsc->cb (rsc->cb_cls,
+                  rowid,
+                  &denom_pub,
+                  &refund.coin.coin_pub,
+                  &refund.merchant_pub,
+                  &refund.merchant_sig,
+                  &refund.h_contract_terms,
+                  refund.rtransaction_id,
+                  &refund.refund_amount);
     GNUNET_PQ_cleanup_result (rs);
     if (GNUNET_OK != ret)
       break;
   }
-  PQclear (result);
-  return GNUNET_OK;
 }
 
 
 /**
- * Select inbound wire transfers into reserves_in above @a serial_id
- * in monotonically increasing order.
+ * Select refunds above @a serial_id in monotonically increasing
+ * order.
  *
  * @param cls closure
  * @param session database connection
  * @param serial_id highest serial ID to exclude (select strictly larger)
  * @param cb function to call on each result
  * @param cb_cls closure for @a cb
- * @return #GNUNET_OK on success,
- *         #GNUNET_SYSERR on DB errors
+ * @return transaction status code
  */
-static int
-postgres_select_reserves_in_above_serial_id (void *cls,
-                                             struct TALER_EXCHANGEDB_Session 
*session,
-                                             uint64_t serial_id,
-                                             
TALER_EXCHANGEDB_ReserveInCallback cb,
-                                             void *cb_cls)
+static enum GNUNET_DB_QueryStatus
+postgres_select_refunds_above_serial_id (void *cls,
+                                         struct TALER_EXCHANGEDB_Session 
*session,
+                                         uint64_t serial_id,
+                                         TALER_EXCHANGEDB_RefundCallback cb,
+                                         void *cb_cls)
 {
   struct GNUNET_PQ_QueryParam params[] = {
     GNUNET_PQ_query_param_uint64 (&serial_id),
     GNUNET_PQ_query_param_end
   };
-  PGresult *result;
-  result = GNUNET_PQ_exec_prepared (session->conn,
-                                    "audit_reserves_in_get_transactions_incr",
-                                    params);
-  if (PGRES_TUPLES_OK !=
-      PQresultStatus (result))
-  {
-    BREAK_DB_ERR (result, session->conn);
-    PQclear (result);
-    return GNUNET_SYSERR;
-  }
-  int nrows;
-  int ret;
+  struct RefundsSerialContext rsc = {
+    .cb = cb,
+    .cb_cls = cb_cls,
+    .status = GNUNET_OK
+  };
+  enum GNUNET_DB_QueryStatus qs;
+
+  qs = GNUNET_PQ_eval_prepared_multi_select (session->conn,
+                                            "audit_get_refunds_incr",
+                                            params,
+                                            &refunds_serial_helper_cb,
+                                            &rsc);
+  if (GNUNET_OK != rsc.status)
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  return qs;
+}
 
-  nrows = PQntuples (result);
-  if (0 == nrows)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "select_reserves_in_above_serial_id() returned 0 matching 
rows\n");
-    PQclear (result);
-    return GNUNET_NO;
-  }
 
-  for (int i=0;i<nrows;i++)
+/**
+ * Closure for #reserves_in_serial_helper_cb().
+ */
+struct ReservesInSerialContext
+{
+
+  /**
+   * Callback to call.
+   */
+  TALER_EXCHANGEDB_ReserveInCallback cb;
+  
+  /**
+   * Closure for @e cb.
+   */
+  void *cb_cls;
+  
+  /**
+   * Status code, set to #GNUNET_SYSERR on hard errors.
+   */ 
+  int status;
+};
+
+
+/**
+ * Helper function to be called with the results of a SELECT statement
+ * that has returned @a num_results results.
+ *
+ * @param cls closure of type `struct ReservesInSerialContext`
+ * @param result the postgres result
+ * @param num_result the number of results in @a result
+ */
+static void
+reserves_in_serial_helper_cb (void *cls,
+                             PGresult *result,
+                             unsigned int num_results)
+{
+  struct ReservesInSerialContext *risc = cls;
+
+  for (unsigned int i=0;i<num_results;i++)
   {
     struct TALER_ReservePublicKeyP reserve_pub;
     struct TALER_Amount credit;
@@ -5779,7 +5358,6 @@ postgres_select_reserves_in_above_serial_id (void *cls,
     uint64_t rowid;
     void *wire_reference;
     size_t wire_reference_size;
-
     struct GNUNET_PQ_ResultSpec rs[] = {
       GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
                                             &reserve_pub),
@@ -5796,6 +5374,7 @@ postgres_select_reserves_in_above_serial_id (void *cls,
                                     &rowid),
       GNUNET_PQ_result_spec_end
     };
+    int ret;
 
     if (GNUNET_OK !=
         GNUNET_PQ_extract_result (result,
@@ -5803,29 +5382,26 @@ postgres_select_reserves_in_above_serial_id (void *cls,
                                   i))
     {
       GNUNET_break (0);
-      PQclear (result);
-      return GNUNET_SYSERR;
+      risc->status = GNUNET_SYSERR;
+      return;
     }
-    ret = cb (cb_cls,
-              rowid,
-              &reserve_pub,
-              &credit,
-              sender_account_details,
-              wire_reference,
-              wire_reference_size,
-              execution_date);
+    ret = risc->cb (risc->cb_cls,
+                   rowid,
+                   &reserve_pub,
+                   &credit,
+                   sender_account_details,
+                   wire_reference,
+                   wire_reference_size,
+                   execution_date);
     GNUNET_PQ_cleanup_result (rs);
     if (GNUNET_OK != ret)
       break;
   }
-
-  PQclear (result);
-  return GNUNET_OK;
 }
 
 
 /**
- * Select withdraw operations from reserves_out above @a serial_id
+ * Select inbound wire transfers into reserves_in above @a serial_id
  * in monotonically increasing order.
  *
  * @param cls closure
@@ -5833,44 +5409,76 @@ postgres_select_reserves_in_above_serial_id (void *cls,
  * @param serial_id highest serial ID to exclude (select strictly larger)
  * @param cb function to call on each result
  * @param cb_cls closure for @a cb
- * @return #GNUNET_OK on success,
- *         #GNUNET_NO if no records were found
- *         #GNUNET_SYSERR on DB errors
+ * @return transaction status code
  */
-static int
-postgres_select_reserves_out_above_serial_id (void *cls,
-                                              struct TALER_EXCHANGEDB_Session 
*session,
-                                              uint64_t serial_id,
-                                              
TALER_EXCHANGEDB_WithdrawCallback cb,
-                                              void *cb_cls)
+static enum GNUNET_DB_QueryStatus
+postgres_select_reserves_in_above_serial_id (void *cls,
+                                             struct TALER_EXCHANGEDB_Session 
*session,
+                                             uint64_t serial_id,
+                                             
TALER_EXCHANGEDB_ReserveInCallback cb,
+                                             void *cb_cls)
 {
   struct GNUNET_PQ_QueryParam params[] = {
     GNUNET_PQ_query_param_uint64 (&serial_id),
     GNUNET_PQ_query_param_end
   };
-  PGresult *result;
-  result = GNUNET_PQ_exec_prepared (session->conn,
-                                    "audit_get_reserves_out_incr",
-                                    params);
-  if (PGRES_TUPLES_OK !=
-      PQresultStatus (result))
-  {
-    BREAK_DB_ERR (result, session->conn);
-    PQclear (result);
-    return GNUNET_SYSERR;
-  }
-  int nrows;
-  int ret;
+  struct ReservesInSerialContext risc = {
+    .cb = cb,
+    .cb_cls = cb_cls,
+    .status = GNUNET_OK
+  };
+  enum GNUNET_DB_QueryStatus qs;
+  
+  qs = GNUNET_PQ_eval_prepared_multi_select (session->conn,
+                                            
"audit_reserves_in_get_transactions_incr",
+                                            params,
+                                            &reserves_in_serial_helper_cb,
+                                            &risc);
+  if (GNUNET_OK != risc.status)
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  return qs;
+}
 
-  nrows = PQntuples (result);
-  if (0 == nrows)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "select_reserves_out_above_serial_id() returned 0 matching 
rows\n");
-    PQclear (result);
-    return GNUNET_NO;
-  }
-  for (int i=0;i<nrows;i++)
+
+/**
+ * Closure for #reserves_out_serial_helper_cb().
+ */
+struct ReservesOutSerialContext
+{
+
+  /**
+   * Callback to call.
+   */
+  TALER_EXCHANGEDB_WithdrawCallback cb;
+  
+  /**
+   * Closure for @e cb.
+   */
+  void *cb_cls;
+  
+  /**
+   * Status code, set to #GNUNET_SYSERR on hard errors.
+   */ 
+  int status;
+};
+
+
+/**
+ * Helper function to be called with the results of a SELECT statement
+ * that has returned @a num_results results.
+ *
+ * @param cls closure of type `struct ReservesOutSerialContext`
+ * @param result the postgres result
+ * @param num_result the number of results in @a result
+ */
+static void
+reserves_out_serial_helper_cb (void *cls,
+                              PGresult *result,
+                              unsigned int num_results)
+{
+  struct ReservesOutSerialContext *rosc = cls;
+  
+  for (unsigned int i=0;i<num_results;i++)
   {
     struct GNUNET_HashCode h_blind_ev;
     struct TALER_DenominationPublicKey denom_pub;
@@ -5880,7 +5488,6 @@ postgres_select_reserves_out_above_serial_id (void *cls,
     struct GNUNET_TIME_Absolute execution_date;
     struct TALER_Amount amount_with_fee;
     uint64_t rowid;
-
     struct GNUNET_PQ_ResultSpec rs[] = {
       GNUNET_PQ_result_spec_auto_from_type ("h_blind_ev",
                                             &h_blind_ev),
@@ -5900,89 +5507,118 @@ postgres_select_reserves_out_above_serial_id (void 
*cls,
                                     &rowid),
       GNUNET_PQ_result_spec_end
     };
+    int ret;
+    
     if (GNUNET_OK !=
         GNUNET_PQ_extract_result (result,
                                   rs,
                                   i))
     {
       GNUNET_break (0);
-      PQclear (result);
-      return GNUNET_SYSERR;
+      rosc->status = GNUNET_SYSERR;
+      return;
     }
-    ret = cb (cb_cls,
-              rowid,
-              &h_blind_ev,
-              &denom_pub,
-              &denom_sig,
-              &reserve_pub,
-              &reserve_sig,
-              execution_date,
-              &amount_with_fee);
+    ret = rosc->cb (rosc->cb_cls,
+                   rowid,
+                   &h_blind_ev,
+                   &denom_pub,
+                   &denom_sig,
+                   &reserve_pub,
+                   &reserve_sig,
+                   execution_date,
+                   &amount_with_fee);
     GNUNET_PQ_cleanup_result (rs);
     if (GNUNET_OK != ret)
       break;
   }
-
-  PQclear (result);
-  return GNUNET_OK;
 }
 
 
 /**
- * Function called to select all wire transfers the exchange
- * executed.
+ * Select withdraw operations from reserves_out above @a serial_id
+ * in monotonically increasing order.
  *
  * @param cls closure
  * @param session database connection
  * @param serial_id highest serial ID to exclude (select strictly larger)
- * @param cb function to call for ONE unfinished item
+ * @param cb function to call on each result
  * @param cb_cls closure for @a cb
- * @return #GNUNET_OK on success,
- *         #GNUNET_NO if there are no entries,
- *         #GNUNET_SYSERR on DB errors
+ * @return transaction status code
  */
-static int
-postgres_select_wire_out_above_serial_id (void *cls,
-                                          struct TALER_EXCHANGEDB_Session 
*session,
-                                          uint64_t serial_id,
-                                          
TALER_EXCHANGEDB_WireTransferOutCallback cb,
-                                          void *cb_cls)
+static enum GNUNET_DB_QueryStatus
+postgres_select_reserves_out_above_serial_id (void *cls,
+                                              struct TALER_EXCHANGEDB_Session 
*session,
+                                              uint64_t serial_id,
+                                              
TALER_EXCHANGEDB_WithdrawCallback cb,
+                                              void *cb_cls)
 {
   struct GNUNET_PQ_QueryParam params[] = {
     GNUNET_PQ_query_param_uint64 (&serial_id),
     GNUNET_PQ_query_param_end
   };
-  PGresult *result;
-  int nrows;
-  int ret;
+  struct ReservesOutSerialContext rosc = {
+    .cb = cb,
+    .cb_cls = cb_cls,
+    .status = GNUNET_OK
+  };
+  enum GNUNET_DB_QueryStatus qs;
+
+  qs = GNUNET_PQ_eval_prepared_multi_select (session->conn,
+                                            "audit_get_reserves_out_incr",
+                                            params,
+                                            &reserves_out_serial_helper_cb,
+                                            &rosc);
+  if (GNUNET_OK != rosc.status)
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  return qs;
+}
 
-  result = GNUNET_PQ_exec_prepared (session->conn,
-                                    "audit_get_wire_incr",
-                                    params);
-  if (PGRES_TUPLES_OK !=
-      PQresultStatus (result))
-  {
-    BREAK_DB_ERR (result, session->conn);
-    PQclear (result);
-    return GNUNET_SYSERR;
-  }
 
-  nrows = PQntuples (result);
-  if (0 == nrows)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "select_prepare_above_serial_id() returned 0 matching rows\n");
-    PQclear (result);
-    return GNUNET_NO;
-  }
-  for (int i=0;i<nrows;i++)
+/**
+ * Closure for #wire_out_serial_helper_cb().
+ */
+struct WireOutSerialContext
+{
+
+  /**
+   * Callback to call.
+   */
+  TALER_EXCHANGEDB_WireTransferOutCallback cb;
+  
+  /**
+   * Closure for @e cb.
+   */
+  void *cb_cls;
+  
+  /**
+   * Status code, set to #GNUNET_SYSERR on hard errors.
+   */ 
+  int status;
+};
+
+
+/**
+ * Helper function to be called with the results of a SELECT statement
+ * that has returned @a num_results results.
+ *
+ * @param cls closure of type `struct WireOutSerialContext`
+ * @param result the postgres result
+ * @param num_result the number of results in @a result
+ */
+static void
+wire_out_serial_helper_cb (void *cls,
+                          PGresult *result,
+                          unsigned int num_results)
+{
+  struct WireOutSerialContext *wosc = cls;
+  
+  for (unsigned int i=0;i<num_results;i++)
   {
     uint64_t rowid;
     struct GNUNET_TIME_Absolute date;
     struct TALER_WireTransferIdentifierRawP wtid;
     json_t *wire;
     struct TALER_Amount amount;
-
     struct GNUNET_PQ_ResultSpec rs[] = {
       GNUNET_PQ_result_spec_uint64 ("wireout_uuid",
                                     &rowid),
@@ -5996,6 +5632,7 @@ postgres_select_wire_out_above_serial_id (void *cls,
                                    &amount),
       GNUNET_PQ_result_spec_end
     };
+    int ret;
 
     if (GNUNET_OK !=
         GNUNET_PQ_extract_result (result,
@@ -6003,73 +5640,101 @@ postgres_select_wire_out_above_serial_id (void *cls,
                                   i))
     {
       GNUNET_break (0);
-      PQclear (result);
-      return GNUNET_SYSERR;
+      wosc->status = GNUNET_SYSERR;
+      return;
     }
-
-    ret = cb (cb_cls,
-              rowid,
-              date,
-              &wtid,
-              wire,
-              &amount);
+    ret = wosc->cb (wosc->cb_cls,
+                   rowid,
+                   date,
+                   &wtid,
+                   wire,
+                   &amount);
     GNUNET_PQ_cleanup_result (rs);
     if (GNUNET_OK != ret)
       break;
   }
-
-  PQclear (result);
-  return GNUNET_OK;
 }
 
 
 /**
- * Function called to select payback requests the exchange
- * received, ordered by serial ID (monotonically increasing).
+ * Function called to select all wire transfers the exchange
+ * executed.
  *
  * @param cls closure
  * @param session database connection
- * @param serial_id lowest serial ID to include (select larger or equal)
+ * @param serial_id highest serial ID to exclude (select strictly larger)
  * @param cb function to call for ONE unfinished item
  * @param cb_cls closure for @a cb
- * @return #GNUNET_OK on success,
- *         #GNUNET_NO if there are no entries,
- *         #GNUNET_SYSERR on DB errors
+ * @return transaction status code
  */
-static int
-postgres_select_payback_above_serial_id (void *cls,
-                                         struct TALER_EXCHANGEDB_Session 
*session,
-                                         uint64_t serial_id,
-                                         TALER_EXCHANGEDB_PaybackCallback cb,
-                                         void *cb_cls)
+static enum GNUNET_DB_QueryStatus
+postgres_select_wire_out_above_serial_id (void *cls,
+                                          struct TALER_EXCHANGEDB_Session 
*session,
+                                          uint64_t serial_id,
+                                          
TALER_EXCHANGEDB_WireTransferOutCallback cb,
+                                          void *cb_cls)
 {
   struct GNUNET_PQ_QueryParam params[] = {
     GNUNET_PQ_query_param_uint64 (&serial_id),
     GNUNET_PQ_query_param_end
   };
-  PGresult *result;
-  result = GNUNET_PQ_exec_prepared (session->conn,
-                                    "payback_get_incr",
-                                    params);
-  if (PGRES_TUPLES_OK !=
-      PQresultStatus (result))
-  {
-    BREAK_DB_ERR (result, session->conn);
-    PQclear (result);
-    return GNUNET_SYSERR;
-  }
-  int nrows;
-  int ret;
+  struct WireOutSerialContext wosc = {
+    .cb = cb,
+    .cb_cls = cb_cls,
+    .status = GNUNET_OK
+  };
+  enum GNUNET_DB_QueryStatus qs;
+
+  qs = GNUNET_PQ_eval_prepared_multi_select (session->conn,
+                                            "audit_get_wire_incr",
+                                            params,
+                                            &wire_out_serial_helper_cb,
+                                            &wosc);
+  if (GNUNET_OK != wosc.status)
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  return qs;
+}
 
-  nrows = PQntuples (result);
-  if (0 == nrows)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "select_prepare_above_serial_id() returned 0 matching rows\n");
-    PQclear (result);
-    return GNUNET_NO;
-  }
-  for (int i=0;i<nrows;i++)
+
+/**
+ * Closure for #payback_serial_helper_cb().
+ */
+struct PaybackSerialContext
+{
+
+  /**
+   * Callback to call.
+   */
+  TALER_EXCHANGEDB_PaybackCallback cb;
+  
+  /**
+   * Closure for @e cb.
+   */
+  void *cb_cls;
+  
+  /**
+   * Status code, set to #GNUNET_SYSERR on hard errors.
+   */ 
+  int status;
+};
+
+
+/**
+ * Helper function to be called with the results of a SELECT statement
+ * that has returned @a num_results results.
+ *
+ * @param cls closure of type `struct PaybackSerialContext`
+ * @param result the postgres result
+ * @param num_result the number of results in @a result
+ */
+static void
+payback_serial_helper_cb (void *cls,
+                         PGresult *result,
+                         unsigned int num_results)
+{
+  struct PaybackSerialContext *psc = cls;
+  
+  for (unsigned int i=0;i<num_results;i++)
   {
     uint64_t rowid;
     struct TALER_ReservePublicKeyP reserve_pub;
@@ -6102,6 +5767,7 @@ postgres_select_payback_above_serial_id (void *cls,
                                    &amount),
       GNUNET_PQ_result_spec_end
     };
+    int ret;
 
     if (GNUNET_OK !=
         GNUNET_PQ_extract_result (result,
@@ -6109,74 +5775,103 @@ postgres_select_payback_above_serial_id (void *cls,
                                   i))
     {
       GNUNET_break (0);
-      PQclear (result);
-      return GNUNET_SYSERR;
+      psc->status = GNUNET_SYSERR;
+      return;
     }
-    ret = cb (cb_cls,
-              rowid,
-              timestamp,
-              &amount,
-              &reserve_pub,
-              &coin,
-              &coin_sig,
-              &coin_blind);
+    ret = psc->cb (psc->cb_cls,
+                  rowid,
+                  timestamp,
+                  &amount,
+                  &reserve_pub,
+                  &coin,
+                  &coin_sig,
+                  &coin_blind);
     GNUNET_PQ_cleanup_result (rs);
     if (GNUNET_OK != ret)
       break;
   }
-
-  PQclear (result);
-  return GNUNET_OK;
 }
 
 
 /**
- * Function called to select reserve close operations the aggregator
- * triggered, ordered by serial ID (monotonically increasing).
+ * Function called to select payback requests the exchange
+ * received, ordered by serial ID (monotonically increasing).
  *
  * @param cls closure
  * @param session database connection
  * @param serial_id lowest serial ID to include (select larger or equal)
  * @param cb function to call for ONE unfinished item
  * @param cb_cls closure for @a cb
- * @return #GNUNET_OK on success,
- *         #GNUNET_NO if there are no entries,
- *         #GNUNET_SYSERR on DB errors
+ * @return transaction status code
  */
-static int
-postgres_select_reserve_closed_above_serial_id (void *cls,
-                                               struct TALER_EXCHANGEDB_Session 
*session,
-                                               uint64_t serial_id,
-                                               
TALER_EXCHANGEDB_ReserveClosedCallback cb,
-                                               void *cb_cls)
+static enum GNUNET_DB_QueryStatus
+postgres_select_payback_above_serial_id (void *cls,
+                                         struct TALER_EXCHANGEDB_Session 
*session,
+                                         uint64_t serial_id,
+                                         TALER_EXCHANGEDB_PaybackCallback cb,
+                                         void *cb_cls)
 {
   struct GNUNET_PQ_QueryParam params[] = {
     GNUNET_PQ_query_param_uint64 (&serial_id),
     GNUNET_PQ_query_param_end
   };
-  PGresult *result;
-  result = GNUNET_PQ_exec_prepared (session->conn,
-                                    "reserves_close_get_incr",
-                                    params);
-  if (PGRES_TUPLES_OK !=
-      PQresultStatus (result))
-  {
-    BREAK_DB_ERR (result, session->conn);
-    PQclear (result);
-    return GNUNET_SYSERR;
-  }
-  int nrows;
-  int ret;
+  struct PaybackSerialContext psc = {
+    .cb = cb,
+    .cb_cls = cb_cls,
+    .status = GNUNET_OK
+  };
+  enum GNUNET_DB_QueryStatus qs;
+  
+  qs = GNUNET_PQ_eval_prepared_multi_select (session->conn,
+                                            "payback_get_incr",
+                                            params,
+                                            &payback_serial_helper_cb,
+                                            &psc);
+  if (GNUNET_OK != psc.status)
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  return qs;
+}
 
-  nrows = PQntuples (result);
-  if (0 == nrows)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "select_reserve_closed_above_serial_id() returned 0 matching 
rows\n");
-    PQclear (result);
-    return GNUNET_NO;
-  }
-  for (int i=0;i<nrows;i++)
+
+/**
+ * Closure for #reserve_closed_serial_helper_cb().
+ */
+struct ReserveClosedSerialContext
+{
+
+  /**
+   * Callback to call.
+   */
+  TALER_EXCHANGEDB_ReserveClosedCallback cb;
+  
+  /**
+   * Closure for @e cb.
+   */
+  void *cb_cls;
+  
+  /**
+   * Status code, set to #GNUNET_SYSERR on hard errors.
+   */ 
+  int status;
+};
+
+
+/**
+ * Helper function to be called with the results of a SELECT statement
+ * that has returned @a num_results results.
+ *
+ * @param cls closure of type `struct ReserveClosedSerialContext`
+ * @param result the postgres result
+ * @param num_result the number of results in @a result
+ */
+static void
+reserve_closed_serial_helper_cb (void *cls,
+                                PGresult *result,
+                                unsigned int num_results)
+{
+  struct ReserveClosedSerialContext *rcsc = cls;
+  
+  for (unsigned int i=0;i<num_results;i++)
   {
     uint64_t rowid;
     struct TALER_ReservePublicKeyP reserve_pub;
@@ -6202,6 +5897,7 @@ postgres_select_reserve_closed_above_serial_id (void *cls,
                                    &closing_fee),
       GNUNET_PQ_result_spec_end
     };
+    int ret;
 
     if (GNUNET_OK !=
         GNUNET_PQ_extract_result (result,
@@ -6209,24 +5905,61 @@ postgres_select_reserve_closed_above_serial_id (void 
*cls,
                                   i))
     {
       GNUNET_break (0);
-      PQclear (result);
-      return GNUNET_SYSERR;
+      rcsc->status = GNUNET_SYSERR;
+      return;
     }
-    ret = cb (cb_cls,
-              rowid,
-              execution_date,
-              &amount_with_fee,
-             &closing_fee,
-              &reserve_pub,
-             receiver_account,
-              &wtid);
+    ret = rcsc->cb (rcsc->cb_cls,
+                   rowid,
+                   execution_date,
+                   &amount_with_fee,
+                   &closing_fee,
+                   &reserve_pub,
+                   receiver_account,
+                   &wtid);
     GNUNET_PQ_cleanup_result (rs);
     if (GNUNET_OK != ret)
       break;
   }
+}
 
-  PQclear (result);
-  return GNUNET_OK;
+
+/**
+ * Function called to select reserve close operations the aggregator
+ * triggered, ordered by serial ID (monotonically increasing).
+ *
+ * @param cls closure
+ * @param session database connection
+ * @param serial_id lowest serial ID to include (select larger or equal)
+ * @param cb function to call for ONE unfinished item
+ * @param cb_cls closure for @a cb
+ * @return transaction status code
+ */
+static enum GNUNET_DB_QueryStatus
+postgres_select_reserve_closed_above_serial_id (void *cls,
+                                               struct TALER_EXCHANGEDB_Session 
*session,
+                                               uint64_t serial_id,
+                                               
TALER_EXCHANGEDB_ReserveClosedCallback cb,
+                                               void *cb_cls)
+{
+  struct GNUNET_PQ_QueryParam params[] = {
+    GNUNET_PQ_query_param_uint64 (&serial_id),
+    GNUNET_PQ_query_param_end
+  };
+  struct ReserveClosedSerialContext rcsc = {
+    .cb = cb,
+    .cb_cls = cb_cls,
+    .status = GNUNET_OK
+  };
+  enum GNUNET_DB_QueryStatus qs;
+  
+  qs = GNUNET_PQ_eval_prepared_multi_select (session->conn,
+                                            "reserves_close_get_incr",
+                                            params,
+                                            &reserve_closed_serial_helper_cb,
+                                            &rcsc);
+  if (GNUNET_OK != rcsc.status)
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  return qs;
 }
 
 
@@ -6245,11 +5978,9 @@ postgres_select_reserve_closed_above_serial_id (void 
*cls,
  * @param amount total amount to be paid back
  * @param h_blind_ev hash of the blinded coin's envelope (must match 
reserves_out entry)
  * @param timestamp current time (rounded)
- * @return #GNUNET_OK on success,
- *         #GNUNET_NO on transient error
- *         #GNUNET_SYSERR on DB errors
+ * @return transaction result status
  */
-static int
+static enum GNUNET_DB_QueryStatus
 postgres_insert_payback_request (void *cls,
                                  struct TALER_EXCHANGEDB_Session *session,
                                  const struct TALER_ReservePublicKeyP 
*reserve_pub,
@@ -6273,49 +6004,32 @@ postgres_insert_payback_request (void *cls,
     GNUNET_PQ_query_param_auto_from_type (h_blind_ev),
     GNUNET_PQ_query_param_end
   };
-  int ret;
+  enum GNUNET_DB_QueryStatus qs;
 
   /* check if the coin is already known */
-  ret = get_known_coin (cls,
-                        session,
-                        &coin->coin_pub,
-                        NULL);
-  if (GNUNET_SYSERR == ret)
-  {
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
-  }
-  if (GNUNET_NO == ret)         /* if not, insert it */
-  {
-    if (GNUNET_OK !=
-        (ret = insert_known_coin (cls,
-                                  session,
-                                  coin)))
-    {
-      GNUNET_break (GNUNET_NO == ret);
-      return ret;
-    }
-  }
-
+  if (0 > (qs = ensure_coin_known (cls,
+                                  session,
+                                  coin)))
+    return qs;
   /* now store actual payback information */
-  if (GNUNET_OK !=
-      (ret = execute_prepared_non_select (session,
-                                          "payback_insert",
-                                          params)))
+  qs = GNUNET_PQ_eval_prepared_non_select (session->conn,
+                                          "payback_insert",
+                                          params);
+  if (0 > qs)
   {
-    GNUNET_break (GNUNET_NO == ret);
-    return ret;
+    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+    return qs;
   }
 
   /* Update reserve balance */
   reserve.pub = *reserve_pub;
-  if (GNUNET_OK != postgres_reserve_get (cls,
-                                         session,
-                                         &reserve))
+  qs = postgres_reserve_get (cls,
+                            session,
+                            &reserve);
+  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
   {
-    /* Should have been checked before we got here... */
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
+    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+    return qs;
   }
   if (GNUNET_SYSERR ==
       TALER_amount_add (&reserve.balance,
@@ -6323,21 +6037,21 @@ postgres_insert_payback_request (void *cls,
                         amount))
   {
     GNUNET_break (0);
-    return GNUNET_SYSERR;
+    return GNUNET_DB_STATUS_HARD_ERROR;
   }
   expiry = GNUNET_TIME_absolute_add (timestamp,
                                      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;
+    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+    return qs;
   }
-  return ret;
+  return qs;
 }
 
 
@@ -6349,11 +6063,9 @@ postgres_insert_payback_request (void *cls,
  * @param session a session
  * @param h_blind_ev hash of the blinded coin
  * @param[out] reserve_pub set to information about the reserve (on success 
only)
- * @return #GNUNET_OK on success,
- *         #GNUNET_NO if there are no entries,
- *         #GNUNET_SYSERR on DB errors
+ * @return transaction status code
  */
-static int
+static enum GNUNET_DB_QueryStatus
 postgres_get_reserve_by_h_blind (void *cls,
                                  struct TALER_EXCHANGEDB_Session *session,
                                  const struct GNUNET_HashCode *h_blind_ev,
@@ -6363,47 +6075,16 @@ postgres_get_reserve_by_h_blind (void *cls,
     GNUNET_PQ_query_param_auto_from_type (h_blind_ev),
     GNUNET_PQ_query_param_end
   };
-  PGresult *result;
-
-  result = GNUNET_PQ_exec_prepared (session->conn,
-                                    "reserve_by_h_blind",
-                                    params);
-  if (PGRES_TUPLES_OK !=
-      PQresultStatus (result))
-  {
-    BREAK_DB_ERR (result, session->conn);
-    PQclear (result);
-    return GNUNET_SYSERR;
-  }
-  int nrows;
-
-  nrows = PQntuples (result);
-  if (0 == nrows)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "reserve_by_h_blind() returned 0 matching rows\n");
-    PQclear (result);
-    return GNUNET_NO;
-  }
-  {
-    struct GNUNET_PQ_ResultSpec rs[] = {
-      GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
-                                            reserve_pub),
-      GNUNET_PQ_result_spec_end
-    };
+  struct GNUNET_PQ_ResultSpec rs[] = {
+    GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
+                                         reserve_pub),
+    GNUNET_PQ_result_spec_end
+  };
 
-    if (GNUNET_OK !=
-        GNUNET_PQ_extract_result (result,
-                                  rs,
-                                  0))
-    {
-      GNUNET_break (0);
-      PQclear (result);
-      return GNUNET_SYSERR;
-    }
-  }
-  PQclear (result);
-  return GNUNET_OK;
+  return GNUNET_PQ_eval_prepared_singleton_select (session->conn,
+                                                  "reserve_by_h_blind",
+                                                  params,
+                                                  rs);
 }
 
 
@@ -6415,54 +6096,23 @@ postgres_get_reserve_by_h_blind (void *cls,
  * @param session a session
  * @param denom_pub_hash hash of the revoked denomination key
  * @param master_sig signature affirming the revocation
- * @return #GNUNET_OK on success,
- *         #GNUNET_NO if the entry already exists (transaction must be rolled 
back!)
- *         #GNUNET_SYSERR on DB errors
+ * @return transaction status code
  */
-static int
+static enum GNUNET_DB_QueryStatus
 postgres_insert_denomination_revocation (void *cls,
                                          struct TALER_EXCHANGEDB_Session 
*session,
                                          const struct GNUNET_HashCode 
*denom_pub_hash,
                                          const struct TALER_MasterSignatureP 
*master_sig)
 {
-  PGresult *result;
-  int ret;
   struct GNUNET_PQ_QueryParam params[] = {
     GNUNET_PQ_query_param_auto_from_type (denom_pub_hash),
     GNUNET_PQ_query_param_auto_from_type (master_sig),
     GNUNET_PQ_query_param_end
   };
 
-  result = GNUNET_PQ_exec_prepared (session->conn,
-                                   "denomination_revocation_insert",
-                                   params);
-  if (PGRES_COMMAND_OK != PQresultStatus (result))
-  {
-    const char *efield;
-
-    efield = PQresultErrorField (result,
-                                PG_DIAG_SQLSTATE);
-    /* FIXME: what about serialization errors? */
-    if ( (PGRES_FATAL_ERROR == PQresultStatus(result)) &&
-        (NULL != strstr (PQ_DIAG_SQLSTATE_UNIQUE_VIOLATION,
-                         efield)) )
-    {
-      /* This means we had the same reserve/justification/details
-        before */
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  "Uniqueness violation, revocation details already known\n");
-      PQclear (result);
-      return GNUNET_NO;
-    }
-    ret = GNUNET_SYSERR;
-    BREAK_DB_ERR (result, session->conn);
-  }
-  else
-  {
-    ret = GNUNET_OK;
-  }
-  PQclear (result);
-  return ret;
+  return GNUNET_PQ_eval_prepared_non_select (session->conn,
+                                            "denomination_revocation_insert",
+                                            params);
 }
 
 
@@ -6475,11 +6125,9 @@ postgres_insert_denomination_revocation (void *cls,
  * @param denom_pub_hash hash of the revoked denomination key
  * @param[out] master_sig signature affirming the revocation
  * @param[out] rowid row where the information is stored
- * @return #GNUNET_OK on success,
- *         #GNUNET_NO no such entry exists
- *         #GNUNET_SYSERR on DB errors
+ * @return transaction status code
  */
-static int
+static enum GNUNET_DB_QueryStatus
 postgres_get_denomination_revocation (void *cls,
                                       struct TALER_EXCHANGEDB_Session *session,
                                       const struct GNUNET_HashCode 
*denom_pub_hash,
@@ -6495,42 +6143,11 @@ postgres_get_denomination_revocation (void *cls,
     GNUNET_PQ_result_spec_uint64 ("denom_revocations_serial_id", rowid),
     GNUNET_PQ_result_spec_end
   };
-  PGresult *result;
-  int nrows;
 
-  result = GNUNET_PQ_exec_prepared (session->conn,
-                                    "denomination_revocation_get",
-                                    params);
-  if (PGRES_TUPLES_OK !=
-      PQresultStatus (result))
-  {
-    BREAK_DB_ERR (result, session->conn);
-    PQclear (result);
-    return GNUNET_SYSERR;
-  }
-  nrows = PQntuples (result);
-  if (0 == nrows)
-  {
-    /* no matches found */
-    PQclear (result);
-    return GNUNET_NO;
-  }
-  if (1 != nrows)
-  {
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
-  }
-  if (GNUNET_OK !=
-      GNUNET_PQ_extract_result (result,
-                                rs,
-                                0))
-  {
-    PQclear (result);
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
-  }
-  PQclear (result);
-  return GNUNET_OK;
+  return GNUNET_PQ_eval_prepared_singleton_select (session->conn,
+                                                  
"denomination_revocation_get",
+                                                  params,
+                                                  rs);
 }
 
 
@@ -6650,7 +6267,7 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
   plugin->select_reserves_out_above_serial_id = 
&postgres_select_reserves_out_above_serial_id;
   plugin->select_wire_out_above_serial_id = 
&postgres_select_wire_out_above_serial_id;
   plugin->select_payback_above_serial_id = 
&postgres_select_payback_above_serial_id;
-    plugin->select_reserve_closed_above_serial_id = 
&postgres_select_reserve_closed_above_serial_id;
+  plugin->select_reserve_closed_above_serial_id = 
&postgres_select_reserve_closed_above_serial_id;
   plugin->insert_payback_request = &postgres_insert_payback_request;
   plugin->get_reserve_by_h_blind = &postgres_get_reserve_by_h_blind;
   plugin->insert_denomination_revocation = 
&postgres_insert_denomination_revocation;
diff --git a/src/exchangedb/test_exchangedb.c b/src/exchangedb/test_exchangedb.c
index 232b58c..6b89577 100644
--- a/src/exchangedb/test_exchangedb.c
+++ b/src/exchangedb/test_exchangedb.c
@@ -98,7 +98,7 @@ mark_prepare_cb (void *cls,
   GNUNET_assert (0 == memcmp (buf,
                               "hello world",
                               buf_size));
-  GNUNET_break (GNUNET_OK ==
+  GNUNET_break (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
                 plugin->wire_prepare_data_mark_finished (plugin->cls,
                                                          session,
                                                          rowid));
@@ -114,23 +114,23 @@ mark_prepare_cb (void *cls,
 static int
 test_wire_prepare (struct TALER_EXCHANGEDB_Session *session)
 {
-  FAILIF (GNUNET_NO !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
           plugin->wire_prepare_data_get (plugin->cls,
                                          session,
                                          &dead_prepare_cb,
                                          NULL));
-  FAILIF (GNUNET_OK !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->wire_prepare_data_insert (plugin->cls,
                                             session,
                                             "testcase",
                                             "hello world",
                                             11));
-  FAILIF (GNUNET_OK !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->wire_prepare_data_get (plugin->cls,
                                          session,
                                          &mark_prepare_cb,
                                          session));
-  FAILIF (GNUNET_NO !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
           plugin->wire_prepare_data_get (plugin->cls,
                                          session,
                                          &dead_prepare_cb,
@@ -162,8 +162,7 @@ check_reserve (struct TALER_EXCHANGEDB_Session *session,
   struct TALER_EXCHANGEDB_Reserve reserve;
 
   reserve.pub = *pub;
-
-  FAILIF (GNUNET_OK !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->reserve_get (plugin->cls,
                                session,
                                &reserve));
@@ -259,7 +258,7 @@ create_denom_key_pair (unsigned int size,
 
   dki.issue.properties.purpose.size = htonl (sizeof (struct 
TALER_DenominationKeyValidityPS));
   dki.issue.properties.purpose.purpose = htonl 
(TALER_SIGNATURE_MASTER_DENOMINATION_KEY_VALIDITY);
-  if (GNUNET_OK !=
+  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
       plugin->insert_denomination_info (plugin->cls,
                                         session,
                                         &dki.denom_pub,
@@ -269,7 +268,7 @@ create_denom_key_pair (unsigned int size,
     destroy_denom_key_pair (dkp);
     return NULL;
   }
-  if (GNUNET_OK !=
+  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
       plugin->get_denomination_info (plugin->cls,
                                      session,
                                      &dki.denom_pub,
@@ -349,7 +348,7 @@ test_refresh_commit_coins (struct TALER_EXCHANGEDB_Session 
*session,
                                 ccoin->coin_ev,
                                 ccoin->coin_ev_size);
   }
-  FAILIF (GNUNET_OK !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->insert_refresh_commit_coins (plugin->cls,
                                                session,
                                                session_hash,
@@ -357,7 +356,7 @@ test_refresh_commit_coins (struct TALER_EXCHANGEDB_Session 
*session,
                                                commit_coins));
   ret_commit_coins = GNUNET_new_array (MELT_NEW_COINS,
                                        struct 
TALER_EXCHANGEDB_RefreshCommitCoin);
-  FAILIF (GNUNET_OK !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->get_refresh_commit_coins (plugin->cls,
                                             session,
                                             session_hash,
@@ -410,19 +409,19 @@ test_refresh_commit_links (struct 
TALER_EXCHANGEDB_Session *session,
   unsigned int i;
 
   ret = GNUNET_SYSERR;
-  FAILIF (GNUNET_NO !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
           plugin->get_refresh_transfer_public_key (plugin->cls,
                                                    session,
                                                    session_hash,
                                                    &tp));
   for (i=0;i<TALER_CNC_KAPPA;i++)
     RND_BLK (&rctp[i]);
-  FAILIF (GNUNET_OK !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->insert_refresh_transfer_public_key (plugin->cls,
                                                       session,
                                                       session_hash,
                                                       
&rctp[MELT_NOREVEAL_INDEX]));
-  FAILIF (GNUNET_OK !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->get_refresh_transfer_public_key (plugin->cls,
                                                    session,
                                                    session_hash,
@@ -530,6 +529,7 @@ test_melting (struct TALER_EXCHANGEDB_Session *session)
   struct TALER_DenominationSignature ev_sigs[MELT_NEW_COINS];
   unsigned int cnt;
   int ret;
+  enum GNUNET_DB_QueryStatus qs;
 
   ret = GNUNET_SYSERR;
   memset (ev_sigs, 0, sizeof (ev_sigs));
@@ -571,21 +571,24 @@ test_melting (struct TALER_EXCHANGEDB_Session *session)
     meltp->melt_fee = fee_refresh;
   }
 
-  FAILIF (GNUNET_OK != plugin->create_refresh_session (plugin->cls,
-                                                       session,
-                                                       &session_hash,
-                                                       &refresh_session));
-  FAILIF (GNUNET_OK != plugin->get_refresh_session (plugin->cls,
-                                                    session,
-                                                    &session_hash,
-                                                    &ret_refresh_session));
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+         plugin->create_refresh_session (plugin->cls,
+                                         session,
+                                         &session_hash,
+                                         &refresh_session));
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+         plugin->get_refresh_session (plugin->cls,
+                                      session,
+                                      &session_hash,
+                                      &ret_refresh_session));
 
   auditor_row_cnt = 0;
-  FAILIF (GNUNET_OK != plugin->select_refreshs_above_serial_id (plugin->cls,
-                                                                session,
-                                                               0,
-                                                               
&audit_refresh_session_cb,
-                                                               NULL));
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+         plugin->select_refreshs_above_serial_id (plugin->cls,
+                                                  session,
+                                                  0,
+                                                  &audit_refresh_session_cb,
+                                                  NULL));
   FAILIF (1 != auditor_row_cnt);
   FAILIF (ret_refresh_session.num_newcoins != refresh_session.num_newcoins);
   FAILIF (ret_refresh_session.noreveal_index != 
refresh_session.noreveal_index);
@@ -632,7 +635,7 @@ test_melting (struct TALER_EXCHANGEDB_Session *session)
                                          &fee_refund);
     new_denom_pubs[cnt] = new_dkp[cnt]->pub;
   }
-  FAILIF (GNUNET_OK !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->insert_refresh_order (plugin->cls,
                                         session,
                                         &session_hash,
@@ -640,7 +643,7 @@ test_melting (struct TALER_EXCHANGEDB_Session *session)
                                         new_denom_pubs));
   ret_denom_pubs = GNUNET_new_array (MELT_NEW_COINS,
                                      struct TALER_DenominationPublicKey);
-  FAILIF (GNUNET_OK !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->get_refresh_order (plugin->cls,
                                      session,
                                      &session_hash,
@@ -671,19 +674,19 @@ test_melting (struct TALER_EXCHANGEDB_Session *session)
       = GNUNET_CRYPTO_rsa_sign_fdh (new_dkp[cnt]->priv.rsa_private_key,
                                     &hc);
     GNUNET_assert (NULL != ev_sigs[cnt].rsa_signature);
-    FAILIF (GNUNET_NO !=
+    FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
             plugin->get_refresh_out (plugin->cls,
                                      session,
                                      &session_hash,
                                      cnt,
                                      &test_sig));
-    FAILIF (GNUNET_OK !=
+    FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
             plugin->insert_refresh_out (plugin->cls,
                                         session,
                                         &session_hash,
                                         cnt,
                                         &ev_sigs[cnt]));
-    FAILIF (GNUNET_OK !=
+    FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
             plugin->get_refresh_out (plugin->cls,
                                      session,
                                      &session_hash,
@@ -695,9 +698,11 @@ test_melting (struct TALER_EXCHANGEDB_Session *session)
     GNUNET_CRYPTO_rsa_signature_free (test_sig.rsa_signature);
   }
 
-  ldl = plugin->get_link_data_list (plugin->cls,
-                                    session,
-                                    &session_hash);
+  qs = plugin->get_link_data_list (plugin->cls,
+                                  session,
+                                  &session_hash,
+                                  &ldl);
+  FAILIF (0 >= qs);
   FAILIF (NULL == ldl);
   for (ldlp = ldl; NULL != ldlp; ldlp = ldlp->next)
   {
@@ -726,10 +731,13 @@ test_melting (struct TALER_EXCHANGEDB_Session *session)
   {
     /* Just to test fetching a coin with melt history */
     struct TALER_EXCHANGEDB_TransactionList *tl;
+    enum GNUNET_DB_QueryStatus qs;
 
-    tl = plugin->get_coin_transactions (plugin->cls,
+    qs = plugin->get_coin_transactions (plugin->cls,
                                         session,
-                                        &meltp->coin.coin_pub);
+                                        &meltp->coin.coin_pub,
+                                       &tl);
+    FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs);
     plugin->free_coin_transaction_list (plugin->cls,
                                         tl);
   }
@@ -739,7 +747,7 @@ test_melting (struct TALER_EXCHANGEDB_Session *session)
     int ok;
 
     ok = GNUNET_NO;
-    FAILIF (GNUNET_OK !=
+    FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
             plugin->get_transfer (plugin->cls,
                                   session,
                                   &meltp->coin.coin_pub,
@@ -906,10 +914,9 @@ static uint64_t deposit_rowid;
  * @param wire_deadline by which the merchant adviced that he would like the
  *        wire transfer to be executed
  * @param wire wire details for the merchant, NULL from 
iterate_matching_deposits()
- * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR if deposit does
- *         not match our expectations
+ * @return transaction status code, #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT to 
continue to iterate
  */
-static int
+static enum GNUNET_DB_QueryStatus
 deposit_cb (void *cls,
             uint64_t rowid,
             const struct TALER_MerchantPublicKeyP *merchant_pub,
@@ -945,10 +952,10 @@ deposit_cb (void *cls,
                        sizeof (struct GNUNET_HashCode))) ) )
   {
     GNUNET_break (0);
-    return GNUNET_SYSERR;
+    return GNUNET_DB_STATUS_HARD_ERROR;
   }
 
-  return GNUNET_OK;
+  return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
 }
 
 
@@ -1115,7 +1122,7 @@ test_gc (struct TALER_EXCHANGEDB_Session *session)
     destroy_denom_key_pair (dkp);
     return GNUNET_SYSERR;
   }
-  if (GNUNET_OK ==
+  if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
       plugin->get_denomination_info (plugin->cls,
                                      session,
                                      &dkp->pub,
@@ -1156,7 +1163,7 @@ test_wire_fees (struct TALER_EXCHANGEDB_Session *session)
   GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
                               &master_sig,
                               sizeof (master_sig));
-  if (GNUNET_OK !=
+  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
       plugin->insert_wire_fee (plugin->cls,
                                session,
                                "wire-method",
@@ -1168,7 +1175,7 @@ test_wire_fees (struct TALER_EXCHANGEDB_Session *session)
     GNUNET_break (0);
     return GNUNET_SYSERR;
   }
-  if (GNUNET_OK !=
+  if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
       plugin->insert_wire_fee (plugin->cls,
                                session,
                                "wire-method",
@@ -1182,7 +1189,7 @@ test_wire_fees (struct TALER_EXCHANGEDB_Session *session)
   }
   /* This must fail as 'end_date' is NOT in the
      half-open interval [start_date,end_date) */
-  if (GNUNET_OK ==
+  if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
       plugin->get_wire_fee (plugin->cls,
                             session,
                             "wire-method",
@@ -1195,7 +1202,7 @@ test_wire_fees (struct TALER_EXCHANGEDB_Session *session)
     GNUNET_break (0);
     return GNUNET_SYSERR;
   }
-  if (GNUNET_OK !=
+  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
       plugin->get_wire_fee (plugin->cls,
                             session,
                             "wire-method",
@@ -1299,7 +1306,7 @@ test_wire_out (struct TALER_EXCHANGEDB_Session *session,
                  TALER_amount_subtract (&transfer_value_wt,
                                         &coin_value_wt,
                                         &coin_fee_wt));
-  FAILIF (GNUNET_NO !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
           plugin->lookup_wire_transfer (plugin->cls,
                                         session,
                                         &wire_out_wtid,
@@ -1310,7 +1317,7 @@ test_wire_out (struct TALER_EXCHANGEDB_Session *session,
     struct GNUNET_HashCode h_contract_terms_wt2 = h_contract_terms_wt;
 
     h_contract_terms_wt2.bits[0]++;
-    FAILIF (GNUNET_NO !=
+    FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
             plugin->wire_lookup_deposit_wtid (plugin->cls,
                                               session,
                                               &h_contract_terms_wt2,
@@ -1321,7 +1328,7 @@ test_wire_out (struct TALER_EXCHANGEDB_Session *session,
                                               NULL));
   }
   /* insert WT data */
-  FAILIF (GNUNET_OK !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->insert_aggregation_tracking (plugin->cls,
                                                session,
                                                &wire_out_wtid,
@@ -1329,7 +1336,7 @@ test_wire_out (struct TALER_EXCHANGEDB_Session *session,
 
   /* Now let's fix the transient constraint violation by
      putting in the WTID into the wire_out table */
-  FAILIF (GNUNET_OK !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->store_wire_transfer_out (plugin->cls,
                                            session,
                                            wire_out_date,
@@ -1337,17 +1344,17 @@ test_wire_out (struct TALER_EXCHANGEDB_Session *session,
                                            wire_out_account,
                                            &wire_out_amount));
   /* And now the commit should still succeed! */
-  FAILIF (GNUNET_OK !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
           plugin->commit (plugin->cls,
                           session));
 
-  FAILIF (GNUNET_OK !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->lookup_wire_transfer (plugin->cls,
                                         session,
                                         &wire_out_wtid,
                                         &cb_wt_check,
                                         &cb_wt_never));
-  FAILIF (GNUNET_OK !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->wire_lookup_deposit_wtid (plugin->cls,
                                             session,
                                             &h_contract_terms_wt,
@@ -1356,7 +1363,7 @@ test_wire_out (struct TALER_EXCHANGEDB_Session *session,
                                             &merchant_pub_wt,
                                             &cb_wtid_check,
                                             &cb_wtid_never));
-  FAILIF (GNUNET_OK !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->select_wire_out_above_serial_id (plugin->cls,
                                                    session,
                                                    0,
@@ -1447,6 +1454,7 @@ run (void *cls)
   unsigned int cnt;
   void *rr;
   size_t rr_size;
+  enum GNUNET_DB_QueryStatus qs;
 
   dkp = NULL;
   rh = NULL;
@@ -1475,8 +1483,12 @@ run (void *cls)
     goto drop;
   }
 
+  FAILIF (GNUNET_OK !=
+          plugin->start (plugin->cls,
+                         session));
+
   /* test DB is empty */
-  FAILIF (GNUNET_NO !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
           plugin->select_payback_above_serial_id (plugin->cls,
                                                   session,
                                                   0,
@@ -1505,12 +1517,12 @@ run (void *cls)
   result = 4;
   sndr = json_loads ("{ \"account\":\"1\" }", 0, NULL);
   GNUNET_assert (NULL != sndr);
-  FAILIF (GNUNET_NO !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
           plugin->get_latest_reserve_in_reference (plugin->cls,
                                                    session,
                                                    &rr,
                                                    &rr_size));
-  FAILIF (GNUNET_OK !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->reserves_in_insert (plugin->cls,
                                       session,
                                       &reserve_pub,
@@ -1519,7 +1531,7 @@ run (void *cls)
                                       sndr,
                                       "TEST",
                                       4));
-  FAILIF (GNUNET_OK !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->get_latest_reserve_in_reference (plugin->cls,
                                                    session,
                                                    &rr,
@@ -1533,7 +1545,7 @@ run (void *cls)
                          value.value,
                          value.fraction,
                          value.currency));
-  FAILIF (GNUNET_OK !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->reserves_in_insert (plugin->cls,
                                       session,
                                       &reserve_pub,
@@ -1542,12 +1554,12 @@ run (void *cls)
                                      sndr,
                                       "TEST2",
                                       5));
-  FAILIF (GNUNET_OK !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->get_latest_reserve_in_reference (plugin->cls,
                                                    session,
                                                    &rr,
                                                    &rr_size));
-  FAILIF (GNUNET_OK !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->get_latest_reserve_in_reference (plugin->cls,
                                                    session,
                                                    &rr,
@@ -1583,7 +1595,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));
@@ -1594,7 +1606,7 @@ run (void *cls)
                          value.fraction,
                          value.currency));
 
-  FAILIF (GNUNET_YES !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->get_reserve_by_h_blind (plugin->cls,
                                           session,
                                           &cbc.h_coin_envelope,
@@ -1603,7 +1615,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,
@@ -1628,7 +1640,7 @@ run (void *cls)
   deposit.coin.denom_pub = dkp->pub;
   deposit.coin.denom_sig = cbc.sig;
   deadline = GNUNET_TIME_absolute_get ();
-  FAILIF (GNUNET_OK !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->insert_payback_request (plugin->cls,
                                           session,
                                           &reserve_pub,
@@ -1638,7 +1650,7 @@ run (void *cls)
                                           &value,
                                           &cbc.h_coin_envelope,
                                           deadline));
-  FAILIF (GNUNET_OK !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->select_payback_above_serial_id (plugin->cls,
                                                   session,
                                                   0,
@@ -1653,7 +1665,7 @@ run (void *cls)
   GNUNET_assert (GNUNET_OK ==
                  TALER_string_to_amount (CURRENCY ":0.000010",
                                          &fee_closing));
-  FAILIF (GNUNET_OK !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
          plugin->insert_reserve_closed (plugin->cls,
                                         session,
                                         &reserve_pub,
@@ -1671,9 +1683,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++)
@@ -1739,13 +1753,13 @@ run (void *cls)
   FAILIF (5 != cnt);
 
   auditor_row_cnt = 0;
-  FAILIF (GNUNET_OK !=
+  FAILIF (0 >=
           plugin->select_reserves_in_above_serial_id (plugin->cls,
                                                      session,
                                                      0,
                                                      &audit_reserve_in_cb,
                                                      NULL));
-  FAILIF (GNUNET_OK !=
+  FAILIF (0 >=
           plugin->select_reserves_out_above_serial_id (plugin->cls,
                                                       session,
                                                       0,
@@ -1768,16 +1782,16 @@ 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));
   auditor_row_cnt = 0;
-  FAILIF (GNUNET_OK !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->select_deposits_above_serial_id (plugin->cls,
                                                   session,
                                                   0,
@@ -1785,7 +1799,7 @@ run (void *cls)
                                                   NULL));
   FAILIF (1 != auditor_row_cnt);
   result = 9;
-  FAILIF (1 !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->iterate_matching_deposits (plugin->cls,
                                              session,
                                              &deposit.h_wire,
@@ -1793,26 +1807,29 @@ run (void *cls)
                                              &deposit_cb, &deposit,
                                              2));
 
-  FAILIF (1 !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->get_ready_deposit (plugin->cls,
                                      session,
                                      &deposit_cb,
                                      &deposit));
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
+          plugin->commit (plugin->cls,
+                          session));
   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,
+                                    session,
                                      deposit_rowid));
-  FAILIF (0 !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
           plugin->get_ready_deposit (plugin->cls,
                                      session,
                                      &deposit_cb,
                                      &deposit));
   plugin->rollback (plugin->cls,
                     session);
-  FAILIF (1 !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->get_ready_deposit (plugin->cls,
                                      session,
                                      &deposit_cb,
@@ -1820,18 +1837,18 @@ run (void *cls)
   FAILIF (GNUNET_OK !=
           plugin->start (plugin->cls,
                          session));
-  FAILIF (GNUNET_NO !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
           plugin->test_deposit_done (plugin->cls,
                                      session,
                                      &deposit));
-  FAILIF (GNUNET_OK !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->mark_deposit_done (plugin->cls,
                                      session,
                                      deposit_rowid));
-  FAILIF (GNUNET_OK !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
           plugin->commit (plugin->cls,
                           session));
-  FAILIF (GNUNET_YES !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->test_deposit_done (plugin->cls,
                                      session,
                                      &deposit));
@@ -1839,17 +1856,18 @@ run (void *cls)
   result = 10;
   deposit2 = deposit;
   RND_BLK (&deposit2.merchant_pub); /* should fail if merchant is different */
-  FAILIF (GNUNET_NO !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
           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 (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
           plugin->have_deposit (plugin->cls,
                                 session,
                                 &deposit2));
-  FAILIF (GNUNET_OK != test_melting (session));
+  FAILIF (GNUNET_OK !=
+         test_melting (session));
 
 
   /* test insert_refund! */
@@ -1860,7 +1878,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));
@@ -1868,18 +1886,18 @@ run (void *cls)
 
   /* test payback / revocation */
   RND_BLK (&master_sig);
-  FAILIF (GNUNET_OK !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->insert_denomination_revocation (plugin->cls,
                                                   session,
                                                   &dkp_pub_hash,
                                                   &master_sig));
-  FAILIF (GNUNET_OK !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
           plugin->commit (plugin->cls,
                           session));
   FAILIF (GNUNET_OK !=
           plugin->start (plugin->cls,
                          session));
-  FAILIF (GNUNET_NO !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
           plugin->insert_denomination_revocation (plugin->cls,
                                                   session,
                                                   &dkp_pub_hash,
@@ -1893,7 +1911,7 @@ run (void *cls)
     struct TALER_MasterSignatureP msig;
     uint64_t rev_rowid;
 
-    FAILIF (GNUNET_OK !=
+    FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
             plugin->get_denomination_revocation (plugin->cls,
                                                  session,
                                                  &dkp_pub_hash,
@@ -1907,7 +1925,7 @@ run (void *cls)
 
   RND_BLK (&coin_sig);
   RND_BLK (&coin_blind);
-  FAILIF (GNUNET_OK !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->insert_payback_request (plugin->cls,
                                           session,
                                           &reserve_pub,
@@ -1919,7 +1937,7 @@ run (void *cls)
                                           deadline));
 
   auditor_row_cnt = 0;
-  FAILIF (GNUNET_OK !=
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->select_refunds_above_serial_id (plugin->cls,
                                                  session,
                                                  0,
@@ -1927,9 +1945,11 @@ run (void *cls)
                                                  NULL));
 
   FAILIF (1 != auditor_row_cnt);
-  tl = plugin->get_coin_transactions (plugin->cls,
+  qs = plugin->get_coin_transactions (plugin->cls,
                                       session,
-                                      &refund.coin.coin_pub);
+                                      &refund.coin.coin_pub,
+                                     &tl);
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs);
   GNUNET_assert (NULL != tl);
   matched = 0;
   for (tlp = tl; NULL != tlp; tlp = tlp->next)
@@ -2111,3 +2131,5 @@ main (int argc,
   GNUNET_free (testname);
   return result;
 }
+
+/* end of test_exchangedb.c */
diff --git a/src/include/taler_auditordb_plugin.h 
b/src/include/taler_auditordb_plugin.h
index 170a68f..3141ad6 100644
--- a/src/include/taler_auditordb_plugin.h
+++ b/src/include/taler_auditordb_plugin.h
@@ -24,6 +24,7 @@
 
 #include <jansson.h>
 #include <gnunet/gnunet_util_lib.h>
+#include <gnunet/gnunet_db_lib.h>
 #include "taler_auditordb_lib.h"
 #include "taler_signatures.h"
 
@@ -236,10 +237,9 @@ struct TALER_AUDITORDB_Plugin
    *
    * @param cls the @e cls of this struct with the plugin-specific state
    * @param session connection to use
-   * @return #GNUNET_OK on success, #GNUNET_NO if the transaction
-   *         can be retried, #GNUNET_SYSERR on hard failures
+   * @return transaction status code
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*commit) (void *cls,
              struct TALER_AUDITORDB_Session *session);
 
@@ -275,9 +275,9 @@ struct TALER_AUDITORDB_Plugin
    * @param cls the @e cls of this struct with the plugin-specific state
    * @param session connection to use
    * @param issue issuing information with value, fees and other info about 
the denomination
-   * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+   * @return status of database operation
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*insert_denomination_info)(void *cls,
                               struct TALER_AUDITORDB_Session *session,
                               const struct TALER_DenominationKeyValidityPS 
*issue);
@@ -291,9 +291,9 @@ struct TALER_AUDITORDB_Plugin
    * @param master_pub master public key of the exchange
    * @param cb function to call with the results
    * @param cb_cls closure for @a cb
-   * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+   * @return transaction status code
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*select_denomination_info)(void *cls,
                               struct TALER_AUDITORDB_Session *session,
                               const struct TALER_MasterPublicKeyP *master_pub,
@@ -309,9 +309,9 @@ struct TALER_AUDITORDB_Plugin
    * @param session connection to use
    * @param master_pub master key of the exchange
    * @param pp where is the auditor in processing
-   * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+   * @return transaction status code
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*insert_auditor_progress)(void *cls,
                              struct TALER_AUDITORDB_Session *session,
                              const struct TALER_MasterPublicKeyP *master_pub,
@@ -326,9 +326,9 @@ struct TALER_AUDITORDB_Plugin
    * @param session connection to use
    * @param master_pub master key of the exchange
    * @param pp where is the auditor in processing
-   * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+   * @return transaction status code
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*update_auditor_progress)(void *cls,
                              struct TALER_AUDITORDB_Session *session,
                              const struct TALER_MasterPublicKeyP *master_pub,
@@ -342,10 +342,9 @@ struct TALER_AUDITORDB_Plugin
    * @param session connection to use
    * @param master_pub master key of the exchange
    * @param[out] pp set to where the auditor is in processing
-   * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure;
-   *         #GNUNET_NO if we have no records for the @a master_pub
+   * @return transaction status code
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*get_auditor_progress)(void *cls,
                           struct TALER_AUDITORDB_Session *session,
                           const struct TALER_MasterPublicKeyP *master_pub,
@@ -364,9 +363,9 @@ struct TALER_AUDITORDB_Plugin
    * @param withdraw_fee_balance amount the exchange gained in withdraw fees
    *                             due to withdrawals from this reserve
    * @param expiration_date expiration date of the reserve
-   * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+   * @return transaction status code
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*insert_reserve_info)(void *cls,
                          struct TALER_AUDITORDB_Session *session,
                          const struct TALER_ReservePublicKeyP *reserve_pub,
@@ -388,9 +387,9 @@ struct TALER_AUDITORDB_Plugin
    * @param withdraw_fee_balance amount the exchange gained in withdraw fees
    *                             due to withdrawals from this reserve
    * @param expiration_date expiration date of the reserve
-   * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+   * @return transaction status code
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*update_reserve_info)(void *cls,
                          struct TALER_AUDITORDB_Session *session,
                          const struct TALER_ReservePublicKeyP *reserve_pub,
@@ -412,10 +411,9 @@ struct TALER_AUDITORDB_Plugin
    * @param[out] withdraw_fee_balance amount the exchange gained in withdraw 
fees
    *                             due to withdrawals from this reserve
    * @param[out] expiration_date expiration date of the reserve
-   * @return #GNUNET_OK on success; #GNUNET_NO if there is no known
-   *         record about this reserve; #GNUNET_SYSERR on failure
+   * @return transaction status code
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*get_reserve_info)(void *cls,
                       struct TALER_AUDITORDB_Session *session,
                       const struct TALER_ReservePublicKeyP *reserve_pub,
@@ -433,10 +431,9 @@ struct TALER_AUDITORDB_Plugin
    * @param session connection to use
    * @param reserve_pub public key of the reserve
    * @param master_pub master public key of the exchange
-   * @return #GNUNET_OK on success; #GNUNET_NO if there is no known
-   *         record about this reserve; #GNUNET_SYSERR on failure
+   * @return transaction status code
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*del_reserve_info)(void *cls,
                       struct TALER_AUDITORDB_Session *session,
                       const struct TALER_ReservePublicKeyP *reserve_pub,
@@ -453,9 +450,9 @@ struct TALER_AUDITORDB_Plugin
    * @param reserve_balance amount stored in the reserve
    * @param withdraw_fee_balance amount the exchange gained in withdraw fees
    *                             due to withdrawals from this reserve
-   * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+   * @return transaction status code
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*insert_reserve_summary)(void *cls,
                             struct TALER_AUDITORDB_Session *session,
                             const struct TALER_MasterPublicKeyP *master_pub,
@@ -473,9 +470,9 @@ struct TALER_AUDITORDB_Plugin
    * @param reserve_balance amount stored in the reserve
    * @param withdraw_fee_balance amount the exchange gained in withdraw fees
    *                             due to withdrawals from this reserve
-   * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+   * @return transaction status code
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*update_reserve_summary)(void *cls,
                             struct TALER_AUDITORDB_Session *session,
                             const struct TALER_MasterPublicKeyP *master_pub,
@@ -492,10 +489,9 @@ struct TALER_AUDITORDB_Plugin
    * @param[out] reserve_balance amount stored in the reserve
    * @param[out] withdraw_fee_balance amount the exchange gained in withdraw 
fees
    *                             due to withdrawals from this reserve
-   * @return #GNUNET_OK on success; #GNUNET_NO if there is no known
-   *         record about this exchange; #GNUNET_SYSERR on failure
+   * @return transaction status code
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*get_reserve_summary)(void *cls,
                          struct TALER_AUDITORDB_Session *session,
                          const struct TALER_MasterPublicKeyP *master_pub,
@@ -511,9 +507,9 @@ struct TALER_AUDITORDB_Plugin
    * @param session connection to use
    * @param master_pub master public key of the exchange
    * @param wire_fee_balance amount the exchange gained in wire fees
-   * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+   * @return transaction status code
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*insert_wire_fee_summary)(void *cls,
                              struct TALER_AUDITORDB_Session *session,
                              const struct TALER_MasterPublicKeyP *master_pub,
@@ -528,9 +524,9 @@ struct TALER_AUDITORDB_Plugin
    * @param session connection to use
    * @param master_pub master public key of the exchange
    * @param wire_fee_balance amount the exchange gained in wire fees
-   * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+   * @return transaction status code
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*update_wire_fee_summary)(void *cls,
                              struct TALER_AUDITORDB_Session *session,
                              const struct TALER_MasterPublicKeyP *master_pub,
@@ -544,10 +540,9 @@ struct TALER_AUDITORDB_Plugin
    * @param session connection to use
    * @param master_pub master public key of the exchange
    * @param[out] wire_fee_balance set amount the exchange gained in wire fees
-   * @return #GNUNET_OK on success; #GNUNET_NO if there is no known
-   *         record about this exchange; #GNUNET_SYSERR on failure
+   * @return transaction status code
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*get_wire_fee_summary)(void *cls,
                           struct TALER_AUDITORDB_Session *session,
                           const struct TALER_MasterPublicKeyP *master_pub,
@@ -563,9 +558,9 @@ struct TALER_AUDITORDB_Plugin
    * @param denom_pub_hash hash of the denomination public key
    * @param denom_balance value of coins outstanding with this denomination key
    * @param denom_risk value of coins issued with this denomination key
-   * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+   * @return transaction status code
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*insert_denomination_balance)(void *cls,
                                  struct TALER_AUDITORDB_Session *session,
                                  const struct GNUNET_HashCode *denom_pub_hash,
@@ -582,9 +577,9 @@ struct TALER_AUDITORDB_Plugin
    * @param denom_pub_hash hash of the denomination public key
    * @param denom_balance value of coins outstanding with this denomination key
    * @param denom_risk value of coins issued with this denomination key
-   * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+   * @return transaction status code
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*update_denomination_balance)(void *cls,
                                  struct TALER_AUDITORDB_Session *session,
                                  const struct GNUNET_HashCode *denom_pub_hash,
@@ -600,9 +595,9 @@ struct TALER_AUDITORDB_Plugin
    * @param denom_pub_hash hash of the denomination public key
    * @param[out] denom_balance value of coins outstanding with this 
denomination key
    * @param[out] denom_risk value of coins issued with this denomination key
-   * @return #GNUNET_OK on success; #GNUNET_NO if no record found, 
#GNUNET_SYSERR on failure
+   * @return transaction status code
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*get_denomination_balance)(void *cls,
                               struct TALER_AUDITORDB_Session *session,
                               const struct GNUNET_HashCode *denom_pub_hash,
@@ -616,9 +611,9 @@ struct TALER_AUDITORDB_Plugin
    * @param cls the @e cls of this struct with the plugin-specific state
    * @param session connection to use
    * @param denom_pub_hash hash of the denomination public key
-   * @return #GNUNET_OK on success; #GNUNET_NO if no record found, 
#GNUNET_SYSERR on failure
+   * @return transaction status code
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*del_denomination_balance)(void *cls,
                               struct TALER_AUDITORDB_Session *session,
                               const struct GNUNET_HashCode *denom_pub_hash);
@@ -636,9 +631,9 @@ struct TALER_AUDITORDB_Plugin
    * @param melt_fee_balance total melt fees collected for this DK
    * @param refund_fee_balance total refund fees collected for this DK
    * @param risk maximum risk exposure of the exchange
-   * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+   * @return transaction status code
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*insert_balance_summary)(void *cls,
                             struct TALER_AUDITORDB_Session *session,
                             const struct TALER_MasterPublicKeyP *master_pub,
@@ -661,9 +656,9 @@ struct TALER_AUDITORDB_Plugin
    * @param melt_fee_balance total melt fees collected for this DK
    * @param refund_fee_balance total refund fees collected for this DK
    * @param risk maximum risk exposure of the exchange
-   * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+   * @return transaction status code
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*update_balance_summary)(void *cls,
                             struct TALER_AUDITORDB_Session *session,
                             const struct TALER_MasterPublicKeyP *master_pub,
@@ -685,10 +680,9 @@ struct TALER_AUDITORDB_Plugin
    * @param[out] melt_fee_balance total melt fees collected for this DK
    * @param[out] refund_fee_balance total refund fees collected for this DK
    * @param[out] risk maximum risk exposure of the exchange
-   * @return #GNUNET_OK on success; #GNUNET_NO if there is no entry
-   *           for this @a master_pub; #GNUNET_SYSERR on failure
+   * @return transaction status code
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*get_balance_summary)(void *cls,
                          struct TALER_AUDITORDB_Session *session,
                          const struct TALER_MasterPublicKeyP *master_pub,
@@ -711,9 +705,9 @@ struct TALER_AUDITORDB_Plugin
    * @param revenue_balance what was the total profit made from
    *                        deposit fees, melting fees, refresh fees
    *                        and coins that were never returned?
-     * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+   * @return transaction status code
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*insert_historic_denom_revenue)(void *cls,
                                    struct TALER_AUDITORDB_Session *session,
                                    const struct TALER_MasterPublicKeyP 
*master_pub,
@@ -731,9 +725,9 @@ struct TALER_AUDITORDB_Plugin
    * @param master_pub master key of the exchange
    * @param cb function to call with the results
    * @param cb_cls closure for @a cb
-   * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
+   * @return transaction status code
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*select_historic_denom_revenue)(void *cls,
                                    struct TALER_AUDITORDB_Session *session,
                                    const struct TALER_MasterPublicKeyP 
*master_pub,
@@ -754,9 +748,9 @@ struct TALER_AUDITORDB_Plugin
    * @param denom_pub_hash hash of the denomination key
    * @param loss_timestamp when did this profit get realized
    * @param loss_balance what was the total loss
-   * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+   * @return transaction status code
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*insert_historic_losses)(void *cls,
                             struct TALER_AUDITORDB_Session *session,
                             const struct TALER_MasterPublicKeyP *master_pub,
@@ -773,9 +767,9 @@ struct TALER_AUDITORDB_Plugin
    * @param master_pub master key of the exchange
    * @param cb function to call with the results
    * @param cb_cls closure for @a cb
-   * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
+   * @return transaction status code
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*select_historic_losses)(void *cls,
                             struct TALER_AUDITORDB_Session *session,
                             const struct TALER_MasterPublicKeyP *master_pub,
@@ -792,9 +786,9 @@ struct TALER_AUDITORDB_Plugin
    * @param start_time beginning of aggregated time interval
    * @param end_time end of aggregated time interval
    * @param reserve_profits total profits made
-   * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+   * @return transaction status code
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*insert_historic_reserve_revenue)(void *cls,
                                      struct TALER_AUDITORDB_Session *session,
                                      const struct TALER_MasterPublicKeyP 
*master_pub,
@@ -811,9 +805,9 @@ struct TALER_AUDITORDB_Plugin
    * @param master_pub master key of the exchange
    * @param cb function to call with results
    * @param cb_cls closure for @a cb
-   * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+   * @return transaction status code
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*select_historic_reserve_revenue)(void *cls,
                                      struct TALER_AUDITORDB_Session *session,
                                      const struct TALER_MasterPublicKeyP 
*master_pub,
@@ -830,9 +824,9 @@ struct TALER_AUDITORDB_Plugin
    * @param session connection to use
    * @param master_pub master key of the exchange
    * @param balance what the bank account balance of the exchange should show
-   * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+   * @return transaction status code
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*insert_predicted_result)(void *cls,
                              struct TALER_AUDITORDB_Session *session,
                              const struct TALER_MasterPublicKeyP *master_pub,
@@ -847,9 +841,9 @@ struct TALER_AUDITORDB_Plugin
    * @param session connection to use
    * @param master_pub master key of the exchange
    * @param balance what the bank account balance of the exchange should show
-   * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+   * @return transaction status code
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*update_predicted_result)(void *cls,
                              struct TALER_AUDITORDB_Session *session,
                              const struct TALER_MasterPublicKeyP *master_pub,
@@ -863,10 +857,9 @@ struct TALER_AUDITORDB_Plugin
    * @param session connection to use
    * @param master_pub master key of the exchange
    * @param[out] balance expected bank account balance of the exchange
-   * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure;
-   *         #GNUNET_NO if we have no records for the @a master_pub
+   * @return transaction status code
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*get_predicted_balance)(void *cls,
                            struct TALER_AUDITORDB_Session *session,
                            const struct TALER_MasterPublicKeyP *master_pub,
diff --git a/src/include/taler_error_codes.h b/src/include/taler_error_codes.h
index 40a9b1a..907be84 100644
--- a/src/include/taler_error_codes.h
+++ b/src/include/taler_error_codes.h
@@ -137,6 +137,14 @@ enum TALER_ErrorCode
    */
   TALER_EC_COIN_HISTORY_DB_ERROR_INSUFFICIENT_FUNDS = 1010,
 
+  /**
+   * Internal logic error.  Some server-side function
+   * failed that really should not.
+   * This response is provided with HTTP status code
+   * MHD_HTTP_INTERNAL_SERVER_ERROR.
+   */
+  TALER_EC_INTERNAL_LOGIC_ERROR = 1011,
+
 
 
   /* ********** request-specific error codes ************* */
@@ -441,14 +449,6 @@ enum TALER_ErrorCode
   TALER_EC_REFRESH_MELT_DB_STORE_SESSION_ERROR = 1304,
 
   /**
-   * The exchange failed to store refresh order data in the
-   * database.
-   * This response is provided with HTTP status code
-   * MHD_HTTP_INTERNAL_ERROR.
-   */
-  TALER_EC_REFRESH_MELT_DB_STORE_ORDER_ERROR = 1305,
-
-  /**
    * The exchange failed to store commit data in the
    * database.
    * This response is provided with HTTP status code
@@ -457,14 +457,6 @@ enum TALER_ErrorCode
   TALER_EC_REFRESH_MELT_DB_STORE_COMMIT_ERROR = 1306,
 
   /**
-   * The exchange failed to store transfer keys in the
-   * database.
-   * This response is provided with HTTP status code
-   * MHD_HTTP_INTERNAL_ERROR.
-   */
-  TALER_EC_REFRESH_MELT_DB_STORE_TRANSFER_ERROR = 1307,
-
-  /**
    * The exchange is unaware of the denomination key that was
    * requested for one of the fresh coins.  This response is provided
    * with HTTP status code MHD_HTTP_BAD_REQUEST.
@@ -903,7 +895,13 @@ enum TALER_ErrorCode
    */
   TALER_EC_PAYBACK_COIN_BALANCE_NEGATIVE = 1857,
 
-
+  /**
+   * The "have" parameter was not a natural number.
+   * This reponse is provied with an HTTP status code of
+   * MHD_HTTP_BAD_REQUEST.
+   */
+  TALER_EC_KEYS_HAVE_NOT_NUMERIC = 1900,
+  
 
   /* *********** Merchant backend error codes ********* */
 
@@ -1282,6 +1280,36 @@ enum TALER_ErrorCode
    */
   TALER_EC_PROPOSAL_LOOKUP_NOT_FOUND = 2503,
 
+  /**
+   * The client specified an unknown instance for any of the /refund operations
+   */
+  TALER_EC_REFUND_INSTANCE_UNKNOWN = 2600,
+
+  /**
+   * The frontend gave an unknown order id to issue the refund to.
+   */
+  TALER_EC_REFUND_ORDER_ID_UNKNOWN = 2601,
+
+  /**
+   * The amount to be refunded is inconsistent: either is lower than
+   * the previous amount being awarded, or it is too big to be paid back.
+   * In this second case, the fault stays on the business dept. side.
+   */
+  TALER_EC_REFUND_INCONSISTENT_AMOUNT = 2602,
+
+  /**
+   * The backend encountered an error while trying to retrieve the
+   * payment data from database.  Likely to be an internal error.
+   */
+  TALER_EC_REFUND_LOOKUP_DB_ERROR = 2603,
+
+  /**
+   * The backend encountered an error while trying to retrieve the
+   * payment data from database.  Likely to be an internal error.
+   */
+  TALER_EC_REFUND_MERCHANT_DB_COMMIT_ERROR = 2604,
+
+
   /* ********** /test API error codes ************* */
 
   /**
diff --git a/src/include/taler_exchange_service.h 
b/src/include/taler_exchange_service.h
index 9a004a4..487304c 100644
--- a/src/include/taler_exchange_service.h
+++ b/src/include/taler_exchange_service.h
@@ -206,6 +206,13 @@ struct TALER_EXCHANGE_Keys
   char *version;
 
   /**
+   * Timestamp indicating the /keys generation.  Monotonically
+   * increasing. Used to fetch /keys incrementally.  Set from
+   * the "list_issue_date" timestamp of /keys.
+   */
+  struct GNUNET_TIME_Absolute last_issue_date;
+  
+  /**
    * Length of the @e sign_keys array.
    */
   unsigned int num_sign_keys;
@@ -223,6 +230,58 @@ struct TALER_EXCHANGE_Keys
 };
 
 
+/** 
+ * How compatible are the protocol version of the exchange and this
+ * client?  The bits (1,2,4) can be used to test if the exchange's
+ * version is incompatible, older or newer respectively.
+ */
+enum TALER_EXCHANGE_VersionCompatibility
+{
+
+  /**
+   * The exchange runs exactly the same protocol version.
+   */
+  TALER_EXCHANGE_VC_MATCH = 0,
+
+  /**
+   * The exchange is too old or too new to be compatible with this
+   * implementation (bit)
+   */
+  TALER_EXCHANGE_VC_INCOMPATIBLE = 1,
+
+  /**
+   * The exchange is older than this implementation (bit)
+   */
+  TALER_EXCHANGE_VC_OLDER = 2,
+
+  /**
+   * The exchange is too old to be compatible with
+   * this implementation.
+   */
+  TALER_EXCHANGE_VC_INCOMPATIBLE_OUTDATED
+  = TALER_EXCHANGE_VC_INCOMPATIBLE
+  | TALER_EXCHANGE_VC_OLDER,
+
+  /**
+   * The exchange is more recent than this implementation (bit).
+   */
+  TALER_EXCHANGE_VC_NEWER = 4,
+
+  /**
+   * The exchange is too recent for this implementation.
+   */
+  TALER_EXCHANGE_VC_INCOMPATIBLE_NEWER 
+  = TALER_EXCHANGE_VC_INCOMPATIBLE
+  | TALER_EXCHANGE_VC_NEWER,
+
+  /**
+   * We could not even parse the version data.
+   */
+  TALER_EXCHANGE_VC_PROTOCOL_ERROR = 8
+  
+};
+
+
 /**
  * Function called with information about who is auditing
  * a particular exchange and what key the exchange is using.
@@ -230,10 +289,12 @@ struct TALER_EXCHANGE_Keys
  * @param cls closure
  * @param keys information about the various keys used
  *        by the exchange, NULL if /keys failed
+ * @param compat protocol compatibility information
  */
 typedef void
 (*TALER_EXCHANGE_CertificationCallback) (void *cls,
-                                         const struct TALER_EXCHANGE_Keys 
*keys);
+                                         const struct TALER_EXCHANGE_Keys 
*keys,
+                                        enum 
TALER_EXCHANGE_VersionCompatibility compat);
 
 
 /**
diff --git a/src/include/taler_exchangedb_plugin.h 
b/src/include/taler_exchangedb_plugin.h
index 5539d52..2cc2c75 100644
--- a/src/include/taler_exchangedb_plugin.h
+++ b/src/include/taler_exchangedb_plugin.h
@@ -24,6 +24,7 @@
 
 #include <jansson.h>
 #include <gnunet/gnunet_util_lib.h>
+#include <gnunet/gnunet_db_lib.h>
 #include "taler_exchangedb_lib.h"
 
 
@@ -673,9 +674,9 @@ struct TALER_EXCHANGEDB_Session;
  * @param wire_deadline by which the merchant adviced that he would like the
  *        wire transfer to be executed
  * @param receiver_wire_account wire details for the merchant, NULL from 
iterate_matching_deposits()
- * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
+ * @return transaction status code, #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT to 
continue to iterate
  */
-typedef int
+typedef enum GNUNET_DB_QueryStatus
 (*TALER_EXCHANGEDB_DepositIterator)(void *cls,
                                     uint64_t rowid,
                                     const struct TALER_MerchantPublicKeyP 
*merchant_pub,
@@ -1008,9 +1009,9 @@ typedef int
  * @param left amount left in the reserve
  * @param account_details information about the reserve's bank account
  * @param expiration_date when did the reserve expire
- * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
+ * @return transaction status code to pass on
  */
-typedef int
+typedef enum GNUNET_DB_QueryStatus
 (*TALER_EXCHANGEDB_ReserveExpiredCallback)(void *cls,
                                           const struct TALER_ReservePublicKeyP 
*reserve_pub,
                                           const struct TALER_Amount *left,
@@ -1106,10 +1107,9 @@ struct TALER_EXCHANGEDB_Plugin
    *
    * @param cls the @e cls of this struct with the plugin-specific state
    * @param session connection to use
-   * @return #GNUNET_OK on success, #GNUNET_NO if the transaction
-   *         can be retried, #GNUNET_SYSERR on hard failures
+   * @return transaction status
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*commit) (void *cls,
              struct TALER_EXCHANGEDB_Session *session);
 
@@ -1134,9 +1134,9 @@ struct TALER_EXCHANGEDB_Plugin
    * @param session connection to use
    * @param denom_pub the public key used for signing coins of this 
denomination
    * @param issue issuing information with value, fees and other info about 
the denomination
-   * @return #GNUNET_OK on success; #GNUNET_SYSERR on failure
+   * @return status of the query
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*insert_denomination_info) (void *cls,
                                struct TALER_EXCHANGEDB_Session *session,
                                const struct TALER_DenominationPublicKey 
*denom_pub,
@@ -1149,10 +1149,10 @@ struct TALER_EXCHANGEDB_Plugin
    * @param cls the @e cls of this struct with the plugin-specific state
    * @param session connection to use
    * @param denom_pub the public key used for signing coins of this 
denomination
-   * @param[out] issue set to issue information with value, fees and other 
info about the coin, can be NULL
-   * @return #GNUNET_OK on success; #GNUNET_NO if no record was found, 
#GNUNET_SYSERR on failure
+   * @param[out] issue set to issue information with value, fees and other 
info about the coin
+   * @return transaction status code
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*get_denomination_info) (void *cls,
                             struct TALER_EXCHANGEDB_Session *session,
                             const struct TALER_DenominationPublicKey 
*denom_pub,
@@ -1167,9 +1167,9 @@ struct TALER_EXCHANGEDB_Plugin
    * @param[in,out] reserve the reserve data.  The public key of the reserve 
should be set
    *          in this structure; it is used to query the database.  The balance
    *          and expiration are then filled accordingly.
-   * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure
+   * @return transaction status
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*reserve_get) (void *cls,
                   struct TALER_EXCHANGEDB_Session *db,
                   struct TALER_EXCHANGEDB_Reserve *reserve);
@@ -1189,11 +1189,9 @@ struct TALER_EXCHANGEDB_Plugin
    * @param sender_account_details information about the sender's bank account
    * @param wire_reference unique reference identifying the wire transfer 
(binary blob)
    * @param wire_reference_size number of bytes in @a wire_reference
-   * @return #GNUNET_OK upon success; #GNUNET_NO if the given
-   *         @a details are already known for this @a reserve_pub,
-   *         #GNUNET_SYSERR upon failures (DB error, incompatible currency)
+   * @return transaction status code
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*reserves_in_insert) (void *cls,
                          struct TALER_EXCHANGEDB_Session *db,
                          const struct TALER_ReservePublicKeyP *reserve_pub,
@@ -1211,10 +1209,9 @@ struct TALER_EXCHANGEDB_Plugin
    * @param db the database connection handle
    * @param[out] wire_reference set to unique reference identifying the wire 
transfer (binary blob)
    * @param[out] wire_reference_size set to number of bytes in @a 
wire_reference
-   * @return #GNUNET_OK upon success; #GNUNET_NO if we never got any incoming 
transfers
-   *         #GNUNET_SYSERR upon failures (DB error)
+   * @return transaction status code
    */
-  int
+  enum GNUNET_DB_QueryStatus 
   (*get_latest_reserve_in_reference)(void *cls,
                                      struct TALER_EXCHANGEDB_Session *db,
                                      void **wire_reference,
@@ -1231,11 +1228,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,
@@ -1250,11 +1245,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);
@@ -1267,12 +1260,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);
 
 
   /**
@@ -1292,11 +1287,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);
@@ -1308,11 +1303,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);
@@ -1324,11 +1317,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);
@@ -1342,27 +1333,26 @@ 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);
 
 
   /**
-   * Test if a deposit was marked as done, thereby declaring that it cannot be
-   * refunded anymore.
+   * Test if a deposit was marked as done, thereby declaring that it
+   * cannot be refunded anymore.
    *
    * @param cls the @e cls of this struct with the plugin-specific state
    * @param session connection to the database
    * @param deposit the deposit to check
-   * @return #GNUNET_YES if is is marked done done, #GNUNET_NO if not,
-   *         #GNUNET_SYSERR on error (deposit unknown)
+   * @return #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT if is is marked done,
+   *         #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if not,
+   *         otherwise transaction error status (incl. deposit unknown)
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*test_deposit_done) (void *cls,
                         struct TALER_EXCHANGEDB_Session *session,
                         const struct TALER_EXCHANGEDB_Deposit *deposit);
@@ -1376,11 +1366,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);
@@ -1395,10 +1383,9 @@ struct TALER_EXCHANGEDB_Plugin
    * @param session connection to the database
    * @param deposit_cb function to call for ONE such deposit
    * @param deposit_cb_cls closure for @a deposit_cb
-   * @return number of rows processed, 0 if none exist,
-   *         #GNUNET_SYSERR on error
+   * @return transaction status code
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*get_ready_deposit) (void *cls,
                         struct TALER_EXCHANGEDB_Session *session,
                         TALER_EXCHANGEDB_DepositIterator deposit_cb,
@@ -1430,9 +1417,9 @@ struct TALER_EXCHANGEDB_Plugin
    *        be #TALER_EXCHANGEDB_MATCHING_DEPOSITS_LIMIT, larger values
    *        are not supported, smaller values would be inefficient.
    * @return number of rows processed, 0 if none exist,
-   *         #GNUNET_SYSERR on error
+   *         transaction status code on error
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*iterate_matching_deposits) (void *cls,
                                 struct TALER_EXCHANGEDB_Session *session,
                                 const struct GNUNET_HashCode *h_wire,
@@ -1449,11 +1436,9 @@ struct TALER_EXCHANGEDB_Plugin
    * @param session database handle to use
    * @param session_hash hash over the melt to use for the lookup
    * @param[out] refresh_session where to store the result
-   * @return #GNUNET_YES on success,
-   *         #GNUNET_NO if not found,
-   *         #GNUNET_SYSERR on DB failure
+   * @return transaction status
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*get_refresh_session) (void *cls,
                           struct TALER_EXCHANGEDB_Session *session,
                           const struct GNUNET_HashCode *session_hash,
@@ -1467,11 +1452,9 @@ struct TALER_EXCHANGEDB_Plugin
    * @param session database handle to use
    * @param session_hash hash over the melt to use to locate the session
    * @param refresh_session session data to store
-   * @return #GNUNET_YES on success,
-   *         #GNUNET_NO on transient error
-   *         #GNUNET_SYSERR on DB failure
+   * @return query status for the transaction
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*create_refresh_session) (void *cls,
                              struct TALER_EXCHANGEDB_Session *session,
                              const struct GNUNET_HashCode *session_hash,
@@ -1487,11 +1470,9 @@ struct TALER_EXCHANGEDB_Plugin
    * @param session_hash hash to identify refresh session
    * @param num_newcoins number of coins to generate, size of the @a 
denom_pubs array
    * @param denom_pubs array denominations of the coins to create
-   * @return #GNUNET_OK on success
-   *         #GNUNET_NO on transient error
-   *         #GNUNET_SYSERR on internal error
+   * @return query status for the transaction
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*insert_refresh_order) (void *cls,
                            struct TALER_EXCHANGEDB_Session *session,
                            const struct GNUNET_HashCode *session_hash,
@@ -1508,10 +1489,9 @@ struct TALER_EXCHANGEDB_Plugin
    * @param session_hash hash to identify refresh session
    * @param num_newcoins size of the @a denom_pubs array
    * @param[out] denom_pubs where to write @a num_newcoins denomination keys
-   * @return #GNUNET_OK on success
-   *         #GNUNET_SYSERR on internal error
+   * @return transaction status
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*get_refresh_order) (void *cls,
                         struct TALER_EXCHANGEDB_Session *session,
                         const struct GNUNET_HashCode *session_hash,
@@ -1528,11 +1508,9 @@ struct TALER_EXCHANGEDB_Plugin
    * @param session_hash hash to identify refresh session
    * @param num_newcoins coin index size of the @a commit_coins array
    * @param commit_coin array of coin commitments to store
-   * @return #GNUNET_OK on success
-   *         #GNUNET_NO on transient error
-   *         #GNUNET_SYSERR on error
+   * @return query status for the transaction
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*insert_refresh_commit_coins) (void *cls,
                                   struct TALER_EXCHANGEDB_Session *session,
                                   const struct GNUNET_HashCode *session_hash,
@@ -1549,11 +1527,9 @@ struct TALER_EXCHANGEDB_Plugin
    * @param session_hash hash to identify refresh session
    * @param num_coins size of the @a commit_coins array
    * @param[out] commit_coins array of coin commitments to return
-   * @return #GNUNET_OK on success
-   *         #GNUNET_NO if not found
-   *         #GNUNET_SYSERR on error
+   * @return transaction status
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*get_refresh_commit_coins) (void *cls,
                                struct TALER_EXCHANGEDB_Session *session,
                                const struct GNUNET_HashCode *session_hash,
@@ -1581,11 +1557,9 @@ struct TALER_EXCHANGEDB_Plugin
    * @param session database connection to use
    * @param session_hash hash to identify refresh session
    * @param tp public key to store
-   * @return #GNUNET_SYSERR on internal error
-   *         #GNUNET_NO on transient errors
-   *         #GNUNET_OK on success
+   * @return query status for the transaction
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*insert_refresh_transfer_public_key) (void *cls,
                                          struct TALER_EXCHANGEDB_Session 
*session,
                                          const struct GNUNET_HashCode 
*session_hash,
@@ -1599,11 +1573,9 @@ struct TALER_EXCHANGEDB_Plugin
    * @param session database connection to use
    * @param session_hash hash to identify refresh session
    * @param[out] tp information to return
-   * @return #GNUNET_SYSERR on internal error,
-   *         #GNUNET_NO if commitment was not found
-   *         #GNUNET_OK on success
+   * @return transaction status
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*get_refresh_transfer_public_key) (void *cls,
                                       struct TALER_EXCHANGEDB_Session *session,
                                       const struct GNUNET_HashCode 
*session_hash,
@@ -1620,10 +1592,9 @@ struct TALER_EXCHANGEDB_Plugin
    * @param session_hash hash to identify refresh session
    * @param newcoin_index coin index
    * @param[out] ev_sig coin signature
-   * @return #GNUNET_OK on success, #GNUNET_NO if we have no such entry,
-   *         #GNUNET_SYSERR on error
+   * @return transaction result status
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*get_refresh_out) (void *cls,
                       struct TALER_EXCHANGEDB_Session *session,
                       const struct GNUNET_HashCode *session_hash,
@@ -1642,10 +1613,9 @@ struct TALER_EXCHANGEDB_Plugin
    * @param session_hash hash to identify refresh session
    * @param newcoin_index coin index
    * @param ev_sig coin signature
-   * @return #GNUNET_OK on success
-   *         #GNUNET_SYSERR on error
+   * @return transaction result status
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*insert_refresh_out) (void *cls,
                          struct TALER_EXCHANGEDB_Session *session,
                          const struct GNUNET_HashCode *session_hash,
@@ -1660,12 +1630,14 @@ struct TALER_EXCHANGEDB_Plugin
    * @param cls the @e cls of this struct with the plugin-specific state
    * @param session database connection
    * @param session_hash session to get linkage data for
-   * @return all known link data for the session
+   * @param[out] ldldp set to all known link data for the session
+   * @return status of the transaction
    */
-  struct TALER_EXCHANGEDB_LinkDataList *
+  enum GNUNET_DB_QueryStatus
   (*get_link_data_list) (void *cls,
                          struct TALER_EXCHANGEDB_Session *session,
-                         const struct GNUNET_HashCode *session_hash);
+                         const struct GNUNET_HashCode *session_hash,
+                        struct TALER_EXCHANGEDB_LinkDataList **ldlp);
 
 
   /**
@@ -1690,11 +1662,9 @@ struct TALER_EXCHANGEDB_Plugin
    * @param coin_pub public key of the coin
    * @param tdc function to call for each session the coin was melted into
    * @param tdc_cls closure for @a tdc
-   * @return #GNUNET_OK on success,
-   *         #GNUNET_NO on failure (not found)
-   *         #GNUNET_SYSERR on internal failure (database issue)
+   * @return statement execution status
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*get_transfer) (void *cls,
                    struct TALER_EXCHANGEDB_Session *session,
                    const struct TALER_CoinSpendPublicKeyP *coin_pub,
@@ -1709,12 +1679,14 @@ struct TALER_EXCHANGEDB_Plugin
    * @param cls the @e cls of this struct with the plugin-specific state
    * @param session database connection
    * @param coin_pub coin to investigate
-   * @return list of transactions, NULL if coin is fresh
+   * @param[out] tlp set to list of transactions, NULL if coin is fresh
+   * @return database transaction status
    */
-  struct TALER_EXCHANGEDB_TransactionList *
+  enum GNUNET_DB_QueryStatus
   (*get_coin_transactions) (void *cls,
                             struct TALER_EXCHANGEDB_Session *session,
-                            const struct TALER_CoinSpendPublicKeyP *coin_pub);
+                            const struct TALER_CoinSpendPublicKeyP *coin_pub,
+                           struct TALER_EXCHANGEDB_TransactionList **tlp);
 
 
   /**
@@ -1737,10 +1709,9 @@ struct TALER_EXCHANGEDB_Plugin
    * @param wtid the raw wire transfer identifier we used
    * @param cb function to call on each transaction found
    * @param cb_cls closure for @a cb
-   * @return #GNUNET_OK on success, #GNUNET_SYSERR on database errors,
-   *         #GNUNET_NO if we found no results
+   * @return query status of the transaction
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*lookup_wire_transfer) (void *cls,
                            struct TALER_EXCHANGEDB_Session *session,
                            const struct TALER_WireTransferIdentifierRawP *wtid,
@@ -1761,10 +1732,9 @@ struct TALER_EXCHANGEDB_Plugin
    * @param merchant_pub merchant public key
    * @param cb function to call with the result
    * @param cb_cls closure to pass to @a cb
-   * @return #GNUNET_OK on success, #GNUNET_SYSERR on DB errors,
-   *         #GNUNET_NO if nothing was found
+   * @return transaction status code
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*wire_lookup_deposit_wtid)(void *cls,
                               struct TALER_EXCHANGEDB_Session *session,
                              const struct GNUNET_HashCode *h_contract_terms,
@@ -1782,11 +1752,9 @@ struct TALER_EXCHANGEDB_Plugin
    * @param session database connection
    * @param wtid the raw wire transfer identifier we used
    * @param deposit_serial_id row in the deposits table for which this is 
aggregation data
-   * @return #GNUNET_OK on success
-   *         #GNUNET_NO on transient errors
-   *         #GNUNET_SYSERR on DB errors
+   * @return transaction status code
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*insert_aggregation_tracking)(void *cls,
                                  struct TALER_EXCHANGEDB_Session *session,
                                  const struct TALER_WireTransferIdentifierRawP 
*wtid,
@@ -1803,11 +1771,9 @@ struct TALER_EXCHANGEDB_Plugin
    * @param end_date when does the fee end being valid
    * @param wire_fee how high is the wire transfer fee
    * @param master_sig signature over the above by the exchange master key
-   * @return #GNUNET_OK on success or if the record exists,
-   *         #GNUNET_NO on transient errors,
-   *         #GNUNET_SYSERR on failure
+   * @return transaction status code
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*insert_wire_fee)(void *cls,
                      struct TALER_EXCHANGEDB_Session *session,
                      const char *wire_method,
@@ -1816,7 +1782,7 @@ struct TALER_EXCHANGEDB_Plugin
                      const struct TALER_Amount *wire_fee,
                      const struct TALER_MasterSignatureP *master_sig);
 
-
+  
   /**
    * Obtain wire fee from database.
    *
@@ -1828,10 +1794,9 @@ struct TALER_EXCHANGEDB_Plugin
    * @param[out] end_date when does the fee end being valid
    * @param[out] wire_fee how high is the wire transfer fee
    * @param[out] master_sig signature over the above by the exchange master key
-   * @return #GNUNET_OK on success, #GNUNET_NO if no fee is known
-   *         #GNUNET_SYSERR on failure
+   * @return query status of the transaction
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*get_wire_fee) (void *cls,
                    struct TALER_EXCHANGEDB_Session *session,
                    const char *type,
@@ -1851,11 +1816,9 @@ struct TALER_EXCHANGEDB_Plugin
    * @param now timestamp based on which we decide expiration
    * @param rec function to call on expired reserves
    * @param rec_cls closure for @a rec
-   * @return #GNUNET_SYSERR on database error
-   *         #GNUNET_NO if there are no expired non-empty reserves
-   *         #GNUNET_OK on success
+   * @return transaction status
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*get_expired_reserves)(void *cls,
                          struct TALER_EXCHANGEDB_Session *session,
                          struct GNUNET_TIME_Absolute now,
@@ -1874,10 +1837,9 @@ struct TALER_EXCHANGEDB_Plugin
    * @param wtid identifier for the wire transfer
    * @param amount_with_fee amount we charged to the reserve
    * @param closing_fee how high is the closing fee
-   * @return #GNUNET_OK on success, #GNUNET_NO if the record exists,
-   *         #GNUNET_SYSERR on failure
+   * @return transaction status code
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*insert_reserve_closed)(void *cls,
                           struct TALER_EXCHANGEDB_Session *session,
                           const struct TALER_ReservePublicKeyP *reserve_pub,
@@ -1896,9 +1858,9 @@ struct TALER_EXCHANGEDB_Plugin
    * @param type type of the wire transfer (i.e. "sepa")
    * @param buf buffer with wire transfer preparation data
    * @param buf_size number of bytes in @a buf
-   * @return #GNUNET_OK on success, #GNUNET_SYSERR on DB errors
+   * @return query status code
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*wire_prepare_data_insert)(void *cls,
                               struct TALER_EXCHANGEDB_Session *session,
                               const char *type,
@@ -1912,9 +1874,9 @@ struct TALER_EXCHANGEDB_Plugin
    * @param cls closure
    * @param session database connection
    * @param rowid which entry to mark as finished
-   * @return #GNUNET_OK on success, #GNUNET_SYSERR on DB errors
+   * @return transaction status code
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*wire_prepare_data_mark_finished)(void *cls,
                                      struct TALER_EXCHANGEDB_Session *session,
                                      uint64_t rowid);
@@ -1928,11 +1890,9 @@ struct TALER_EXCHANGEDB_Plugin
    * @param session database connection
    * @param cb function to call for ONE unfinished item
    * @param cb_cls closure for @a cb
-   * @return #GNUNET_OK on success,
-   *         #GNUNET_NO if there are no entries,
-   *         #GNUNET_SYSERR on DB errors
+   * @return transaction status code
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*wire_prepare_data_get)(void *cls,
                            struct TALER_EXCHANGEDB_Session *session,
                            TALER_EXCHANGEDB_WirePreparationIterator cb,
@@ -1963,10 +1923,9 @@ struct TALER_EXCHANGEDB_Plugin
    * @param wtid subject of the wire transfer
    * @param wire_account details about the receiver account of the wire 
transfer
    * @param amount amount that was transmitted
-   * @return #GNUNET_OK on success
-   *         #GNUNET_SYSERR on DB errors
+   * @return transaction status code
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*store_wire_transfer_out)(void *cls,
                              struct TALER_EXCHANGEDB_Session *session,
                              struct GNUNET_TIME_Absolute date,
@@ -2004,10 +1963,9 @@ struct TALER_EXCHANGEDB_Plugin
    * @param serial_id highest serial ID to exclude (select strictly larger)
    * @param cb function to call on each result
    * @param cb_cls closure for @a cb
-   * @return #GNUNET_OK on success,
-   *         #GNUNET_SYSERR on DB errors
+   * @return transaction status code
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*select_deposits_above_serial_id)(void *cls,
                                      struct TALER_EXCHANGEDB_Session *session,
                                      uint64_t serial_id,
@@ -2023,10 +1981,9 @@ struct TALER_EXCHANGEDB_Plugin
    * @param serial_id highest serial ID to exclude (select strictly larger)
    * @param cb function to call on each result
    * @param cb_cls closure for @a cb
-   * @return #GNUNET_OK on success,
-   *         #GNUNET_SYSERR on DB errors
+   * @return transaction status code
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*select_refreshs_above_serial_id)(void *cls,
                                      struct TALER_EXCHANGEDB_Session *session,
                                      uint64_t serial_id,
@@ -2043,10 +2000,9 @@ struct TALER_EXCHANGEDB_Plugin
    * @param serial_id highest serial ID to exclude (select strictly larger)
    * @param cb function to call on each result
    * @param cb_cls closure for @a cb
-   * @return #GNUNET_OK on success,
-   *         #GNUNET_SYSERR on DB errors
+   * @return transaction status code
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*select_refunds_above_serial_id)(void *cls,
                                     struct TALER_EXCHANGEDB_Session *session,
                                     uint64_t serial_id,
@@ -2063,10 +2019,9 @@ struct TALER_EXCHANGEDB_Plugin
    * @param serial_id highest serial ID to exclude (select strictly larger)
    * @param cb function to call on each result
    * @param cb_cls closure for @a cb
-   * @return #GNUNET_OK on success,
-   *         #GNUNET_SYSERR on DB errors
+   * @return transaction status code
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*select_reserves_in_above_serial_id)(void *cls,
                                         struct TALER_EXCHANGEDB_Session 
*session,
                                         uint64_t serial_id,
@@ -2082,11 +2037,9 @@ struct TALER_EXCHANGEDB_Plugin
    * @param serial_id highest serial ID to exclude (select strictly larger)
    * @param cb function to call on each result
    * @param cb_cls closure for @a cb
-   * @return #GNUNET_OK on success,
-   *         #GNUNET_NO if no records were found
-   *         #GNUNET_SYSERR on DB errors
+   * @return transaction status code
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*select_reserves_out_above_serial_id)(void *cls,
                                          struct TALER_EXCHANGEDB_Session 
*session,
                                          uint64_t serial_id,
@@ -2103,11 +2056,9 @@ struct TALER_EXCHANGEDB_Plugin
    * @param serial_id lowest serial ID to include (select larger or equal)
    * @param cb function to call for ONE unfinished item
    * @param cb_cls closure for @a cb
-   * @return #GNUNET_OK on success,
-   *         #GNUNET_NO if there are no entries,
-   *         #GNUNET_SYSERR on DB errors
+   * @return transaction status code
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*select_wire_out_above_serial_id)(void *cls,
                                      struct TALER_EXCHANGEDB_Session *session,
                                      uint64_t serial_id,
@@ -2124,11 +2075,9 @@ struct TALER_EXCHANGEDB_Plugin
    * @param serial_id lowest serial ID to include (select larger or equal)
    * @param cb function to call for ONE unfinished item
    * @param cb_cls closure for @a cb
-   * @return #GNUNET_OK on success,
-   *         #GNUNET_NO if there are no entries,
-   *         #GNUNET_SYSERR on DB errors
+   * @return transaction status code
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*select_payback_above_serial_id)(void *cls,
                                     struct TALER_EXCHANGEDB_Session *session,
                                     uint64_t serial_id,
@@ -2145,11 +2094,9 @@ struct TALER_EXCHANGEDB_Plugin
    * @param serial_id lowest serial ID to include (select larger or equal)
    * @param cb function to call
    * @param cb_cls closure for @a cb
-   * @return #GNUNET_OK on success,
-   *         #GNUNET_NO if there are no entries,
-   *         #GNUNET_SYSERR on DB errors
+   * @return transaction status code
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*select_reserve_closed_above_serial_id)(void *cls,
                                           struct TALER_EXCHANGEDB_Session 
*session,
                                           uint64_t serial_id,
@@ -2175,11 +2122,9 @@ struct TALER_EXCHANGEDB_Plugin
    * @param receiver_account_details who should receive the funds
    * @param h_blind_ev hash of the blinded coin's envelope (must match 
reserves_out entry)
    * @param now timestamp to store
-   * @return #GNUNET_OK on success,
-   *         #GNUNET_NO on transient error
-   *         #GNUNET_SYSERR on DB errors
+   * @return transaction result status
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*insert_payback_request)(void *cls,
                             struct TALER_EXCHANGEDB_Session *session,
                             const struct TALER_ReservePublicKeyP *reserve_pub,
@@ -2199,11 +2144,9 @@ struct TALER_EXCHANGEDB_Plugin
    * @param session a session
    * @param h_blind_ev hash of the blinded coin
    * @param[out] reserve_pub set to information about the reserve (on success 
only)
-   * @return #GNUNET_OK on success,
-   *         #GNUNET_NO if there are no entries,
-   *         #GNUNET_SYSERR on DB errors
+   * @return transaction status code
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*get_reserve_by_h_blind)(void *cls,
                             struct TALER_EXCHANGEDB_Session *session,
                             const struct GNUNET_HashCode *h_blind_ev,
@@ -2218,11 +2161,9 @@ struct TALER_EXCHANGEDB_Plugin
    * @param session a session
    * @param denom_pub_hash hash of the revoked denomination key
    * @param master_sig signature affirming the revocation
-   * @return #GNUNET_OK on success,
-   *         #GNUNET_NO if the entry already exists (transaction must be 
rolled back!)
-   *         #GNUNET_SYSERR on DB errors
+   * @return transaction status code
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*insert_denomination_revocation)(void *cls,
                                     struct TALER_EXCHANGEDB_Session *session,
                                     const struct GNUNET_HashCode 
*denom_pub_hash,
@@ -2238,11 +2179,9 @@ struct TALER_EXCHANGEDB_Plugin
    * @param denom_pub_hash hash of the revoked denomination key
    * @param[out] master_sig signature affirming the revocation
    * @param[out] rowid row where the information is stored
-   * @return #GNUNET_OK on success,
-   *         #GNUNET_NO no such entry exists
-   *         #GNUNET_SYSERR on DB errors
+   * @return transaction status code
    */
-  int
+  enum GNUNET_DB_QueryStatus
   (*get_denomination_revocation)(void *cls,
                                  struct TALER_EXCHANGEDB_Session *session,
                                  const struct GNUNET_HashCode *denom_pub_hash,
diff --git a/src/include/taler_fakebank_lib.h b/src/include/taler_fakebank_lib.h
index 7d8757d..3df1e60 100644
--- a/src/include/taler_fakebank_lib.h
+++ b/src/include/taler_fakebank_lib.h
@@ -62,8 +62,29 @@ TALER_FAKEBANK_check_empty (struct TALER_FAKEBANK_Handle *h);
 
 
 /**
+ * Tell the fakebank to create another wire transfer.
+ *
+ * @param h fake bank handle
+ * @param debit_account account to debit
+ * @param credit_account account to credit
+ * @param amount amount to transfer
+ * @param subject wire transfer subject to use
+ * @param exchange_base_url exchange URL
+ * @return serial_id of the transfer
+ */
+uint64_t
+TALER_FAKEBANK_make_transfer (struct TALER_FAKEBANK_Handle *h,
+                              uint64_t debit_account,
+                              uint64_t credit_account,
+                              const struct TALER_Amount *amount,
+                              const char *subject,
+                              const char *exchange_base_url);
+
+
+
+/**
  * Check that the @a want_amount was transferred from the @a
- * want_debit to the @a want_credit account.  If so, set the @a wtid
+ * want_debit to the @a want_credit account.  If so, set the @a subject
  * to the transfer identifier and remove the transaction from the
  * list.  If the transaction was not recorded, return #GNUNET_SYSERR.
  *
@@ -73,7 +94,7 @@ TALER_FAKEBANK_check_empty (struct TALER_FAKEBANK_Handle *h);
  * @param want_debit account that should have been credited
  * @param exchange_base_url expected base URL of the exchange,
  *        i.e. "https://example.com/";; may include a port
- * @param[out] wtid set to the wire transfer identifier
+ * @param[out] subject set to the wire transfer identifier
  * @return #GNUNET_OK on success
  */
 int
@@ -82,7 +103,7 @@ TALER_FAKEBANK_check (struct TALER_FAKEBANK_Handle *h,
                       uint64_t want_debit,
                       uint64_t want_credit,
                       const char *exchange_base_url,
-                      struct TALER_WireTransferIdentifierRawP *wtid);
+                      char **subject);
 
 
 /**
diff --git a/src/include/taler_signatures.h b/src/include/taler_signatures.h
index 9699e68..181c501 100644
--- a/src/include/taler_signatures.h
+++ b/src/include/taler_signatures.h
@@ -173,6 +173,11 @@
  */
 #define TALER_SIGNATURE_MERCHANT_PAYMENT_OK 1104
 
+/**
+ * Signature where the merchant confirms a refund increase
+ */
+#define TALER_SIGNATURE_MERCHANT_REFUND_OK 1105
+
 
 /*********************/
 /* Wallet signatures */
@@ -1270,6 +1275,25 @@ struct TALER_ReserveCloseConfirmationPS
 };
 
 
+/**
+ * Used by the merchant to confirm with a signature that the refund has
+ * been successfully done. Even though the frontend doesn't usually do crypto,
+ * this signature may turn useful in court.
+ */
+struct TALER_MerchantRefundConfirmationPS
+{
+  /**
+   * Set TALER_SIGNATURE_REFUND_OK.
+   */
+  struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
+
+  /**
+   * Hashed order id; in case frontend wants to check it.
+   */
+  struct GNUNET_HashCode h_order_id GNUNET_PACKED;
+
+};
+
 GNUNET_NETWORK_STRUCT_END
 
 #endif
diff --git a/src/json/Makefile.am b/src/json/Makefile.am
index 6f71b87..d26f731 100644
--- a/src/json/Makefile.am
+++ b/src/json/Makefile.am
@@ -13,7 +13,7 @@ libtalerjson_la_SOURCES = \
   json.c \
   json_helper.c
 libtalerjson_la_LDFLAGS = \
-  -version-info 0:0:0 \
+  -version-info 1:0:1 \
   -export-dynamic -no-undefined
 libtalerjson_la_LIBADD = \
   -lgnunetjson \
diff --git a/src/util/Makefile.am b/src/util/Makefile.am
index eb9ffe4..c05f756 100644
--- a/src/util/Makefile.am
+++ b/src/util/Makefile.am
@@ -37,7 +37,7 @@ libtalerutil_wallet_la_LIBADD = \
   $(XLIB)
 
 libtalerutil_wallet_la_LDFLAGS = \
-  -version-info 0:0:0 \
+  -version-info 1:0:1 \
   -export-dynamic -no-undefined
 
 libtalerutil_la_SOURCES = \
diff --git a/src/wire/Makefile.am b/src/wire/Makefile.am
index da1b8b5..f8fb9d0 100644
--- a/src/wire/Makefile.am
+++ b/src/wire/Makefile.am
@@ -17,7 +17,10 @@ EXTRA_DIST = \
   wire-sepa.conf \
   wire-test.conf \
   test_wire_plugin.conf \
-  test_wire_plugin_transactions_test.conf
+  test_wire_plugin_transactions_test.conf \
+  test_wire_plugin_key.priv \
+  test_wire_plugin_test.json \
+  test_wire_plugin_sepa.json 
 
 plugindir = $(libdir)/taler
 

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



reply via email to

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