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 (9041840 -> 265af18)


From: gnunet
Subject: [GNUnet-SVN] [taler-exchange] branch stable updated (9041840 -> 265af18)
Date: Mon, 11 Dec 2017 11:09:55 +0100

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

marcello pushed a change to branch stable
in repository exchange.

    from 9041840  round timestamp so that the exchange does not crash when 
converting to JSON
     add 499247a  fixing #5178
     add 78b77ee  properly document TALER_SIGNATURE_MERCHANT_REFUND_OK
     add 5540747  patch to address #5183: always round time before giving it to 
DB, tolerate DB answering without rounded time
     add 0426168  largely fix #5077
     add 40244c9  fix  reservemod issue with time rounding
     add d0a8065  comment out bank test cases until implemented in bank
     add 4872b61  bank uses row_id instead of serial_id now
     add e75afb3  use row_id instead of serial_id everywhere
     add 44aaaf9  set error code correctly, removes compiler warning
     add 1c3863f  swap account numbers so reject can work with auth
     add 16e2cd8  switch auth based on account number in test
     add 1d40d29  initialize rev_rowid always
     add 23cb1d4  fix enum confusion
     add af6e9f1  fix potential NPE
     add 2d658ce  need a few more error codes
     add 76769e9  fix test for '*' accepted encoding
     add ff452e6  check find_pk return value in test
     add d36de3b  add wirewatch command to testcase (not yet active), for #5077
     add 6623afa  add testcase for #5077 (passes), start to reduce use of 
exchange's admin/add/incoming (#5172, more to come)
     add 2152a69  eliminate use of exchange's /admin/add/incoming with fakebank 
in taler-exchange-benchmark
     add 749c02f  bump protocol version of C client, we do support the new 
refresh
     add 0b9ffa8  add -r command line option to wirewatch
     add 878c68f  better diagnostics from fakebank
     add 265af18  log rowids and amounts

No new revisions were added by this update.

Summary of changes:
 contrib/auditor-report.tex.j2                      |   39 +
 doc/taler-exchange-wirewatch.1                     |    3 +
 src/auditor/taler-auditor.c                        |  388 ++++--
 src/auditor/taler-wire-auditor.c                   |   18 +
 src/auditordb/plugin_auditordb_postgres.c          |   46 +-
 src/auditordb/test_auditordb.c                     |    3 +-
 src/bank-lib/Makefile.am                           |    3 +-
 src/bank-lib/bank_api_admin.c                      |   29 +-
 src/bank-lib/bank_api_common.c                     |   30 +-
 src/bank-lib/bank_api_common.h                     |   10 +
 src/bank-lib/bank_api_history.c                    |   99 +-
 src/bank-lib/bank_api_reject.c                     |  245 ++++
 src/bank-lib/fakebank.c                            |  356 ++++-
 src/bank-lib/test_bank_api.c                       |   39 +-
 src/bank-lib/test_bank_api_with_fakebank.c         |   37 +
 src/bank-lib/test_bank_interpreter.c               |  246 +++-
 src/bank-lib/test_bank_interpreter.h               |   31 +-
 src/benchmark/Makefile.am                          |    2 +
 src/benchmark/taler-exchange-benchmark.c           |  157 ++-
 src/benchmark/taler-exchange-benchmark.conf        |   22 +
 src/exchange-lib/Makefile.am                       |    1 +
 src/exchange-lib/exchange_api_common.c             |    6 +-
 src/exchange-lib/exchange_api_handle.c             |    4 +-
 src/exchange-lib/exchange_api_refresh.c            |  367 ++---
 src/exchange-lib/test_exchange_api.c               |  404 ++++--
 src/exchange-tools/taler-exchange-reservemod.c     |    5 +-
 src/exchange/taler-exchange-aggregator.c           |   16 +-
 src/exchange/taler-exchange-httpd_db.c             |    2 +-
 src/exchange/taler-exchange-httpd_keystate.c       |   40 +-
 src/exchange/taler-exchange-httpd_keystate.h       |   16 +
 src/exchange/taler-exchange-httpd_refresh_link.c   |  240 +---
 src/exchange/taler-exchange-httpd_refresh_melt.c   |  706 ++--------
 src/exchange/taler-exchange-httpd_refresh_reveal.c |  860 ++++++------
 src/exchange/taler-exchange-httpd_responses.c      |   28 +-
 src/exchange/taler-exchange-wirewatch.c            |   58 +-
 src/exchange/test_taler_exchange_aggregator.c      |    2 +
 src/exchangedb/perf_taler_exchangedb.c             |   40 +-
 src/exchangedb/perf_taler_exchangedb_init.c        |  133 +-
 src/exchangedb/perf_taler_exchangedb_init.h        |   60 +-
 src/exchangedb/perf_taler_exchangedb_interpreter.c |  546 +++-----
 src/exchangedb/perf_taler_exchangedb_interpreter.h |  163 +--
 src/exchangedb/plugin_exchangedb_common.c          |   33 +-
 src/exchangedb/plugin_exchangedb_postgres.c        | 1418 ++++++++------------
 src/exchangedb/test_exchangedb.c                   |  600 ++++-----
 src/include/taler_bank_service.h                   |   77 +-
 src/include/taler_crypto_lib.h                     |   82 +-
 src/include/taler_error_codes.h                    |  184 +--
 src/include/taler_exchange_service.h               |    8 +-
 src/include/taler_exchangedb_plugin.h              |  372 ++---
 src/include/taler_fakebank_lib.h                   |   17 +
 src/include/taler_pq_lib.h                         |   56 +
 src/include/taler_signatures.h                     |   17 +-
 src/include/taler_wire_plugin.h                    |    2 +
 src/pq/pq_query_helper.c                           |  123 ++
 src/pq/pq_result_helper.c                          |  160 +++
 src/util/crypto.c                                  |   87 +-
 src/wire/plugin_wire_test.c                        |   74 +-
 src/wire/test_wire_plugin_transactions_test.c      |    2 +
 58 files changed, 4637 insertions(+), 4175 deletions(-)
 create mode 100644 src/bank-lib/bank_api_reject.c

diff --git a/contrib/auditor-report.tex.j2 b/contrib/auditor-report.tex.j2
index 8eb0230..0fbf74c 100644
--- a/contrib/auditor-report.tex.j2
+++ b/contrib/auditor-report.tex.j2
@@ -753,6 +753,45 @@ reserve expired.
 {% endif %}
 
 
+\subsection{Hanging refresh operations}
+
+This section describes cases where the exchange booked a
+coin as spent from {\tt /refresh/melt} but where the
+wallet did not yet complete {\tt /refresh/reveal}.  This
+may happen even if the exchange is correct.
+
+
+{% if data.refresh_hanging|length() == 0 %}
+  {\bf All melted coins were refreshed.}
+{% else %}
+  \begin{longtable}{p{1.5cm}|c|rl}
+  {\bf Key} & {\bf row} & \multicolumn{2}{|c|}{ {\bf Amount}} \\
+  \hline \hline
+\endfirsthead
+  {\bf Key} & {\bf row} & \multicolumn{2}{|c|}{ {\bf Amount}} \\
+  \hline \hline
+\endhead
+  \hline \hline
+  {\bf Key} & {\bf row} & \multicolumn{2}{|c|}{ {\bf Amount}} \\
+\endfoot
+  \hline
+  {\bf Sum}     & &
+  {{ data.total_refresh_hanging.value}}.{{ 
data.total_refresh_hanging.fraction}} & {{ 
data.total_refresh_hanging.currency}} \\
+  \caption{Refresh operations hanging.}
+  \label{table:refresh:hanging}
+\endlastfoot
+{% for item in data.refresh_hanging %}
+  \multicolumn{4}{l}{ {\tt {{ item.coin_pub }} } } \\
+\nopagebreak
+  &
+  {{ item.row }} &
+  {{ item.amount.value }}.{{ item.amount.fraction }} &
+  {{ item.amount.currency }} \\ \hline
+{% endfor %}
+  \end{longtable}
+{% endif %}
+
+
 \subsection{Denomination key invalid at time of withdrawal}
 
 This section lists cases where a denomination key was not valid for
diff --git a/doc/taler-exchange-wirewatch.1 b/doc/taler-exchange-wirewatch.1
index 41a4afa..9b030ab 100644
--- a/doc/taler-exchange-wirewatch.1
+++ b/doc/taler-exchange-wirewatch.1
@@ -22,6 +22,9 @@ Print short help on options.
 .IP "\-T,  \-\-test"
 Run in test mode and exit when idle.
 .B
+.IP "\-r,  \-\-reset"
+Ignore our own database and start with transactions from the beginning of time.
+.B
 .IP "\-v, \-\-version"
 Print version information.
 .B
diff --git a/src/auditor/taler-auditor.c b/src/auditor/taler-auditor.c
index cd1ee0d..870a81a 100644
--- a/src/auditor/taler-auditor.c
+++ b/src/auditor/taler-auditor.c
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  Copyright (C) 2016, 2017 Inria
+  Copyright (C) 2016, 2017 Taler Systems SA
 
   TALER is free software; you can redistribute it and/or modify it under the
   terms of the GNU Affero Public License as published by the Free Software
@@ -275,6 +275,17 @@ static json_t *report_bad_sig_losses;
  */
 static struct TALER_Amount total_bad_sig_loss;
 
+/**
+ * Array of refresh transactions where the /refresh/reveal has not yet
+ * happened (and may of course never happen).
+ */
+static json_t *report_refreshs_hanging;
+
+/**
+ * Total amount lost by operations for which signatures were invalid.
+ */
+static struct TALER_Amount total_refresh_hanging;
+
 
 /* ***************************** Report logic **************************** */
 
@@ -1062,6 +1073,9 @@ handle_payback_by_reserve (void *cls,
       report_row_inconsistency ("payback",
                                rowid,
                                "denomination key not in revocation set");
+      /* FIXME: add amount involved to some loss statistic!?
+         It's kind-of not a loss (we just paid back), OTOH, it is
+         certainly irregular and involves some amount.  */
     }
     else
     {
@@ -1090,7 +1104,12 @@ handle_payback_by_reserve (void *cls,
                                                        
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
     }
   }
-  if (0 == strcmp (rev, "master signature invalid"))
+  else
+  {
+    rev_rowid = 0; /* reported elsewhere */
+  }
+  if ( (NULL != rev) &&
+       (0 == strcmp (rev, "master signature invalid")) )
   {
     report (report_bad_sig_losses,
             json_pack ("{s:s, s:I, s:o, s:o}",
@@ -1893,7 +1912,7 @@ check_transaction_history (const struct 
TALER_CoinSpendPublicKeyP *coin_pub,
       }
       break;
     case TALER_EXCHANGEDB_TT_REFRESH_MELT:
-      amount_with_fee = &tl->details.melt->amount_with_fee;
+      amount_with_fee = &tl->details.melt->session.amount_with_fee;
       fee = &tl->details.melt->melt_fee;
       fee_dki = &dki->properties.fee_refresh;
       if (GNUNET_OK !=
@@ -2104,7 +2123,7 @@ wire_transfer_information_cb (void *cls,
     coin = &tl->details.deposit->coin;
     break;
   case TALER_EXCHANGEDB_TT_REFRESH_MELT:
-    coin = &tl->details.melt->coin;
+    coin = &tl->details.melt->session.coin;
     break;
   case TALER_EXCHANGEDB_TT_REFUND:
     coin = &tl->details.refund->coin;
@@ -2538,11 +2557,9 @@ analyze_aggregations (void *cls)
 
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Analyzing aggregations\n");
-  ac.qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
-  ac.wire_head = NULL;
-  ac.wire_tail = NULL;
-  ac.fee_head = NULL;
-  ac.fee_tail = NULL;
+  memset (&ac,
+          0,
+          sizeof (ac));
   qsx = adb->get_wire_fee_summary (adb->cls,
                                   asession,
                                   &master_pub,
@@ -2968,6 +2985,54 @@ withdraw_cb (void *cls,
 
 
 /**
+ * Closure for #reveal_data_cb().
+ */
+struct RevealContext
+{
+
+  /**
+   * Denomination public keys of the new coins.
+   */
+  struct TALER_DenominationPublicKey *new_dps;
+
+  /**
+   * Size of the @a new_dp and @a new_dki arrays.
+   */
+  unsigned int num_newcoins;
+};
+
+
+/**
+ * Function called with information about a refresh order.
+ *
+ * @param cls closure
+ * @param rowid unique serial ID for the row in our database
+ * @param num_newcoins size of the @a rrcs array
+ * @param rrcs array of @a num_newcoins information about coins to be created
+ * @param num_tprivs number of entries in @a tprivs, should be 
#TALER_CNC_KAPPA - 1
+ * @param tprivs array of @e num_tprivs transfer private keys
+ * @param tp transfer public key information
+ */
+static void
+reveal_data_cb (void *cls,
+                uint32_t num_newcoins,
+                const struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrcs,
+                unsigned int num_tprivs,
+                const struct TALER_TransferPrivateKeyP *tprivs,
+                const struct TALER_TransferPublicKeyP *tp)
+{
+  struct RevealContext *rctx = cls;
+
+  rctx->num_newcoins = num_newcoins;
+  rctx->new_dps = GNUNET_new_array (num_newcoins,
+                                    struct TALER_DenominationPublicKey);
+  for (unsigned int i=0;i<num_newcoins;i++)
+    rctx->new_dps[i].rsa_public_key
+      = GNUNET_CRYPTO_rsa_public_key_dup (rrcs[i].denom_pub.rsa_public_key);
+}
+
+
+/**
  * Function called with details about coins that were melted, with the
  * goal of auditing the refresh's execution.  Verifies the signature
  * and updates our information about coins outstanding (the old coin's
@@ -2980,7 +3045,6 @@ withdraw_cb (void *cls,
  * @param coin_pub public key of the coin
  * @param coin_sig signature from the coin
  * @param amount_with_fee amount that was deposited including fee
- * @param num_newcoins how many coins were issued
  * @param noreveal_index which index was picked by the exchange in 
cut-and-choose
  * @param session_hash what is the session hash
  * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
@@ -2992,9 +3056,8 @@ refresh_session_cb (void *cls,
                     const struct TALER_CoinSpendPublicKeyP *coin_pub,
                     const struct TALER_CoinSpendSignatureP *coin_sig,
                     const struct TALER_Amount *amount_with_fee,
-                    uint16_t num_newcoins,
-                    uint16_t noreveal_index,
-                    const struct GNUNET_HashCode *session_hash)
+                    uint32_t noreveal_index,
+                    const struct TALER_RefreshCommitmentP *rc)
 {
   struct CoinContext *cc = cls;
   struct TALER_RefreshMeltCoinAffirmationPS rmc;
@@ -3020,7 +3083,7 @@ refresh_session_cb (void *cls,
   /* verify melt signature */
   rmc.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_MELT);
   rmc.purpose.size = htonl (sizeof (rmc));
-  rmc.session_hash = *session_hash;
+  rmc.rc = *rc;
   TALER_amount_hton (&rmc.amount_with_fee,
                      amount_with_fee);
   rmc.melt_fee = dki->properties.fee_refresh;
@@ -3050,161 +3113,189 @@ refresh_session_cb (void *cls,
               TALER_amount2s (amount_with_fee));
 
   {
-    struct TALER_DenominationPublicKey new_dp[num_newcoins];
-    const struct TALER_EXCHANGEDB_DenominationKeyInformationP 
*new_dki[num_newcoins];
+    struct RevealContext reveal_ctx;
     struct TALER_Amount refresh_cost;
     int err;
 
     GNUNET_assert (GNUNET_OK ==
                    TALER_amount_get_zero (amount_with_fee->currency,
                                           &refresh_cost));
-    qs = edb->get_refresh_order (edb->cls,
-                                esession,
-                                session_hash,
-                                num_newcoins,
-                                new_dp);
-    if (0 >= qs)
+    memset (&reveal_ctx,
+            0,
+            sizeof (reveal_ctx));
+    qs = edb->get_refresh_reveal (edb->cls,
+                                  esession,
+                                  rc,
+                                  &reveal_data_cb,
+                                  &reveal_ctx);
+    if (0 > qs)
     {
       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
-       that the resulting amounts are consistent with the value being 
refreshed. */
-    err = GNUNET_NO;
-    for (unsigned int i=0;i<num_newcoins;i++)
+    if ( (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) ||
+         (0 == reveal_ctx.num_newcoins) )
     {
-      /* lookup new coin denomination key */
-      qs = get_denomination_info (&new_dp[i],
-                                 &new_dki[i],
-                                 NULL);
-      if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
-      {
-        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);
-      new_dp[i].rsa_public_key = NULL;
-    }
-    if (err)
-      return GNUNET_SYSERR;
-
-    /* calculate total refresh cost */
-    for (unsigned int i=0;i<num_newcoins;i++)
-    {
-      /* update cost of refresh */
-      struct TALER_Amount fee;
-      struct TALER_Amount value;
-
-      TALER_amount_ntoh (&fee,
-                         &new_dki[i]->properties.fee_withdraw);
-      TALER_amount_ntoh (&value,
-                         &new_dki[i]->properties.value);
-      if ( (GNUNET_OK !=
-            TALER_amount_add (&refresh_cost,
-                              &refresh_cost,
-                              &fee)) ||
-           (GNUNET_OK !=
-            TALER_amount_add (&refresh_cost,
-                              &refresh_cost,
-                              &value)) )
-      {
-        GNUNET_break (0);
-       cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
-        return GNUNET_SYSERR;
-      }
+      /* This can happen if /refresh/reveal was not yet called or only
+         with invalid data, even if the exchange is correctly
+         operating. We still report it. */
+      report (report_refreshs_hanging,
+              json_pack ("{s:I, s:o, s:o}",
+                         "row", (json_int_t) rowid,
+                         "amount", TALER_JSON_from_amount (amount_with_fee),
+                         "coin_pub", GNUNET_JSON_from_data_auto (coin_pub)));
+      GNUNET_break (GNUNET_OK ==
+                    TALER_amount_add (&total_refresh_hanging,
+                                      &total_refresh_hanging,
+                                      amount_with_fee));
+      return GNUNET_OK;
     }
+    // FIXME: free reveal_ctx.num_newcoins later!
 
-    /* compute contribution of old coin */
     {
-      struct TALER_Amount melt_fee;
+      const struct TALER_EXCHANGEDB_DenominationKeyInformationP 
*new_dkis[reveal_ctx.num_newcoins];
 
-      TALER_amount_ntoh (&melt_fee,
-                         &dki->properties.fee_refresh);
-      if (GNUNET_OK !=
-          TALER_amount_subtract (&amount_without_fee,
-                                 amount_with_fee,
-                                 &melt_fee))
+      /* Update outstanding amounts for all new coin's denominations, and check
+         that the resulting amounts are consistent with the value being 
refreshed. */
+      err = GNUNET_NO;
+      for (unsigned int i=0;i<reveal_ctx.num_newcoins;i++)
       {
-        GNUNET_break (0);
-       cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
-        return GNUNET_SYSERR;
+        /* lookup new coin denomination key */
+        qs = get_denomination_info (&reveal_ctx.new_dps[i],
+                                    &new_dkis[i],
+                                    NULL);
+        if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
+        {
+          GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+          cc->qs = qs;
+          err = GNUNET_YES;
+        }
+        GNUNET_CRYPTO_rsa_public_key_free 
(reveal_ctx.new_dps[i].rsa_public_key);
+        reveal_ctx.new_dps[i].rsa_public_key = NULL;
       }
-    }
+      GNUNET_free (reveal_ctx.new_dps);
+      reveal_ctx.new_dps = NULL;
 
-    /* check old coin covers complete expenses */
-    if (1 == TALER_amount_cmp (&refresh_cost,
-                               &amount_without_fee))
-    {
-      /* refresh_cost > amount_without_fee */
-      report_amount_arithmetic_inconsistency ("melt (fee)",
-                                              rowid,
-                                              &amount_without_fee,
-                                              &refresh_cost,
-                                              -1);
-      return GNUNET_OK;
-    }
-
-    /* update outstanding denomination amounts */
-    for (unsigned int i=0;i<num_newcoins;i++)
-    {
-      struct DenominationSummary *dsi;
-      struct TALER_Amount value;
+      if (err)
+        return GNUNET_SYSERR;
 
-      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,
-                  "Created fresh coin in denomination `%s' of value %s\n",
-                  GNUNET_h2s (&new_dki[i]->properties.denom_hash),
-                  TALER_amount2s (&value));
-      if (GNUNET_OK !=
-          TALER_amount_add (&dsi->denom_balance,
-                            &dsi->denom_balance,
-                            &value))
+      /* calculate total refresh cost */
+      for (unsigned int i=0;i<reveal_ctx.num_newcoins;i++)
       {
-        GNUNET_break (0);
-       cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
-        return GNUNET_SYSERR;
+        /* update cost of refresh */
+        struct TALER_Amount fee;
+        struct TALER_Amount value;
+
+        TALER_amount_ntoh (&fee,
+                           &new_dkis[i]->properties.fee_withdraw);
+        TALER_amount_ntoh (&value,
+                           &new_dkis[i]->properties.value);
+        if ( (GNUNET_OK !=
+              TALER_amount_add (&refresh_cost,
+                                &refresh_cost,
+                                &fee)) ||
+             (GNUNET_OK !=
+              TALER_amount_add (&refresh_cost,
+                                &refresh_cost,
+                                &value)) )
+        {
+          GNUNET_break (0);
+          cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
+          return GNUNET_SYSERR;
+        }
       }
-      if (GNUNET_OK !=
-          TALER_amount_add (&dsi->denom_risk,
-                            &dsi->denom_risk,
-                            &value))
+
+      /* compute contribution of old coin */
       {
-        GNUNET_break (0);
-       cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
-        return GNUNET_SYSERR;
+        struct TALER_Amount melt_fee;
+
+        TALER_amount_ntoh (&melt_fee,
+                           &dki->properties.fee_refresh);
+        if (GNUNET_OK !=
+            TALER_amount_subtract (&amount_without_fee,
+                                   amount_with_fee,
+                                   &melt_fee))
+        {
+          GNUNET_break (0);
+          cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
+          return GNUNET_SYSERR;
+        }
       }
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  "New balance of denomination `%s' is %s\n",
-                  GNUNET_h2s (&new_dki[i]->properties.denom_hash),
-                  TALER_amount2s (&dsi->denom_balance));
-      if (GNUNET_OK !=
-          TALER_amount_add (&total_escrow_balance,
-                            &total_escrow_balance,
-                            &value))
+
+      /* check old coin covers complete expenses */
+      if (1 == TALER_amount_cmp (&refresh_cost,
+                                 &amount_without_fee))
       {
-        GNUNET_break (0);
-       cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
-        return GNUNET_SYSERR;
+        /* refresh_cost > amount_without_fee */
+        report_amount_arithmetic_inconsistency ("melt (fee)",
+                                                rowid,
+                                                &amount_without_fee,
+                                                &refresh_cost,
+                                                -1);
+        return GNUNET_OK;
       }
-      if (GNUNET_OK !=
-          TALER_amount_add (&total_risk,
-                            &total_risk,
-                            &value))
+
+      /* update outstanding denomination amounts */
+      for (unsigned int i=0;i<reveal_ctx.num_newcoins;i++)
       {
-        GNUNET_break (0);
-       cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
-        return GNUNET_SYSERR;
+        struct DenominationSummary *dsi;
+        struct TALER_Amount value;
+
+        dsi = get_denomination_summary (cc,
+                                        new_dkis[i],
+                                        &new_dkis[i]->properties.denom_hash);
+        if (NULL == dsi)
+        {
+          GNUNET_break (0);
+          return GNUNET_SYSERR;
+        }
+        TALER_amount_ntoh (&value,
+                           &new_dkis[i]->properties.value);
+        GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                    "Created fresh coin in denomination `%s' of value %s\n",
+                    GNUNET_h2s (&new_dkis[i]->properties.denom_hash),
+                    TALER_amount2s (&value));
+        if (GNUNET_OK !=
+            TALER_amount_add (&dsi->denom_balance,
+                              &dsi->denom_balance,
+                              &value))
+        {
+          GNUNET_break (0);
+          cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
+          return GNUNET_SYSERR;
+        }
+        if (GNUNET_OK !=
+            TALER_amount_add (&dsi->denom_risk,
+                              &dsi->denom_risk,
+                              &value))
+        {
+          GNUNET_break (0);
+          cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
+          return GNUNET_SYSERR;
+        }
+        GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+                    "New balance of denomination `%s' is %s\n",
+                    GNUNET_h2s (&new_dkis[i]->properties.denom_hash),
+                    TALER_amount2s (&dsi->denom_balance));
+        if (GNUNET_OK !=
+            TALER_amount_add (&total_escrow_balance,
+                              &total_escrow_balance,
+                              &value))
+        {
+          GNUNET_break (0);
+          cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
+          return GNUNET_SYSERR;
+        }
+        if (GNUNET_OK !=
+            TALER_amount_add (&total_risk,
+                              &total_risk,
+                              &value))
+        {
+          GNUNET_break (0);
+          cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
+          return GNUNET_SYSERR;
+        }
       }
     }
   }
@@ -4081,6 +4172,9 @@ run (void *cls,
   GNUNET_assert (GNUNET_OK ==
                  TALER_amount_get_zero (currency,
                                         &total_bad_sig_loss));
+  GNUNET_assert (GNUNET_OK ==
+                 TALER_amount_get_zero (currency,
+                                        &total_refresh_hanging));
   GNUNET_assert (NULL !=
                 (report_emergencies = json_array ()));
   GNUNET_assert (NULL !=
@@ -4104,6 +4198,8 @@ run (void *cls,
   GNUNET_assert (NULL !=
                 (report_bad_sig_losses = json_array ()));
   GNUNET_assert (NULL !=
+                (report_refreshs_hanging = json_array ()));
+  GNUNET_assert (NULL !=
                 (report_fee_time_inconsistencies = json_array ()));
   setup_sessions_and_run ();
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
@@ -4129,7 +4225,7 @@ run (void *cls,
                       " s:o, s:o, s:o, s:o, s:o,"
                       " s:o, s:o, s:o, s:o, s:o,"
                       " s:o, s:o, s:o, s:o, s:o,"
-                      " s:o }",
+                      " s:o, s:o, s:o }",
                       /* blocks of 5 for easier counting/matching to format 
string */
                       /* block */
                      "reserve_balance_insufficient_inconsistencies",
@@ -4199,7 +4295,11 @@ run (void *cls,
                       TALER_JSON_from_amount (&total_aggregation_fee_income),
                       /* block */
                       "wire_fee_time_inconsistencies",
-                      report_fee_time_inconsistencies);
+                      report_fee_time_inconsistencies,
+                      "total_refresh_hanging",
+                      TALER_JSON_from_amount (&total_refresh_hanging),
+                      "refresh_hanging",
+                      report_refreshs_hanging);
   GNUNET_break (NULL != report);
   json_dumpf (report,
              stdout,
diff --git a/src/auditor/taler-wire-auditor.c b/src/auditor/taler-wire-auditor.c
index 6d5085e..8ba38d3 100644
--- a/src/auditor/taler-wire-auditor.c
+++ b/src/auditor/taler-wire-auditor.c
@@ -830,6 +830,7 @@ check_exchange_wire_out ()
  * transactions).
  *
  * @param cls closure
+ * @param ec error code in case something went wrong
  * @param dir direction of the transfer
  * @param row_off identification of the position at which we are querying
  * @param row_off_size number of bytes in @a row_off
@@ -838,6 +839,7 @@ check_exchange_wire_out ()
  */
 static int
 history_debit_cb (void *cls,
+                  enum TALER_ErrorCode ec,
                  enum TALER_BANK_Direction dir,
                  const void *row_off,
                  size_t row_off_size,
@@ -848,6 +850,13 @@ history_debit_cb (void *cls,
 
   if (TALER_BANK_DIRECTION_NONE == dir)
   {
+    if (TALER_EC_NONE != ec)
+    {
+      /* FIXME: log properly to audit report! */
+      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                  "Error fetching history: %u!\n",
+                  (unsigned int) ec);
+    }
     /* end of iteration, now check wire_out to see
        if it matches #out_map */
     hh = NULL;
@@ -1069,6 +1078,7 @@ conclude_credit_history ()
  * transactions).
  *
  * @param cls closure
+ * @param ec error code in case something went wrong
  * @param dir direction of the transfer
  * @param row_off identification of the position at which we are querying
  * @param row_off_size number of bytes in @a row_off
@@ -1077,6 +1087,7 @@ conclude_credit_history ()
  */
 static int
 history_credit_cb (void *cls,
+                   enum TALER_ErrorCode ec,
                    enum TALER_BANK_Direction dir,
                    const void *row_off,
                    size_t row_off_size,
@@ -1087,6 +1098,13 @@ history_credit_cb (void *cls,
 
   if (TALER_BANK_DIRECTION_NONE == dir)
   {
+    if (TALER_EC_NONE != ec)
+    {
+      /* FIXME: log properly to audit report! */
+      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                  "Error fetching history: %u!\n",
+                  (unsigned int) ec);
+    }
     /* end of operation */
     hh = NULL;
     conclude_credit_history ();
diff --git a/src/auditordb/plugin_auditordb_postgres.c 
b/src/auditordb/plugin_auditordb_postgres.c
index b699a76..2f881ae 100644
--- a/src/auditordb/plugin_auditordb_postgres.c
+++ b/src/auditordb/plugin_auditordb_postgres.c
@@ -1014,7 +1014,7 @@ postgres_gc (void *cls)
   struct PostgresClosure *pc = cls;
   struct GNUNET_TIME_Absolute now;
   struct GNUNET_PQ_QueryParam params_time[] = {
-    GNUNET_PQ_query_param_absolute_time (&now),
+    TALER_PQ_query_param_absolute_time (&now),
     GNUNET_PQ_query_param_end
   };
   PGconn *conn;
@@ -1063,10 +1063,10 @@ postgres_insert_denomination_info (void *cls,
   struct GNUNET_PQ_QueryParam params[] = {
     GNUNET_PQ_query_param_auto_from_type (&issue->denom_hash),
     GNUNET_PQ_query_param_auto_from_type (&issue->master),
-    GNUNET_PQ_query_param_absolute_time_nbo (&issue->start),
-    GNUNET_PQ_query_param_absolute_time_nbo (&issue->expire_withdraw),
-    GNUNET_PQ_query_param_absolute_time_nbo (&issue->expire_deposit),
-    GNUNET_PQ_query_param_absolute_time_nbo (&issue->expire_legal),
+    TALER_PQ_query_param_absolute_time_nbo (&issue->start),
+    TALER_PQ_query_param_absolute_time_nbo (&issue->expire_withdraw),
+    TALER_PQ_query_param_absolute_time_nbo (&issue->expire_deposit),
+    TALER_PQ_query_param_absolute_time_nbo (&issue->expire_legal),
     TALER_PQ_query_param_amount_nbo (&issue->value),
     TALER_PQ_query_param_amount_nbo (&issue->fee_withdraw),
     TALER_PQ_query_param_amount_nbo (&issue->fee_deposit),
@@ -1145,10 +1145,10 @@ denomination_info_cb (void *cls,
     };
     struct GNUNET_PQ_ResultSpec rs[] = {
       GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash", 
&issue.denom_hash),
-      GNUNET_PQ_result_spec_absolute_time_nbo ("valid_from", &issue.start),
-      GNUNET_PQ_result_spec_absolute_time_nbo ("expire_withdraw", 
&issue.expire_withdraw),
-      GNUNET_PQ_result_spec_absolute_time_nbo ("expire_deposit", 
&issue.expire_deposit),
-      GNUNET_PQ_result_spec_absolute_time_nbo ("expire_legal", 
&issue.expire_legal),
+      TALER_PQ_result_spec_absolute_time_nbo ("valid_from", &issue.start),
+      TALER_PQ_result_spec_absolute_time_nbo ("expire_withdraw", 
&issue.expire_withdraw),
+      TALER_PQ_result_spec_absolute_time_nbo ("expire_deposit", 
&issue.expire_deposit),
+      TALER_PQ_result_spec_absolute_time_nbo ("expire_legal", 
&issue.expire_legal),
       TALER_PQ_result_spec_amount_nbo ("coin", &issue.value),
       TALER_PQ_result_spec_amount_nbo ("fee_withdraw", &issue.fee_withdraw),
       TALER_PQ_result_spec_amount_nbo ("fee_deposit", &issue.fee_deposit),
@@ -1358,7 +1358,7 @@ postgres_insert_wire_auditor_progress (void *cls,
     GNUNET_PQ_query_param_auto_from_type (master_pub),
     GNUNET_PQ_query_param_uint64 (&pp->last_reserve_in_serial_id),
     GNUNET_PQ_query_param_uint64 (&pp->last_wire_out_serial_id),
-    GNUNET_PQ_query_param_absolute_time (&pp->last_timestamp),
+    TALER_PQ_query_param_absolute_time (&pp->last_timestamp),
     GNUNET_PQ_query_param_fixed_size (in_wire_off,
                                       wire_off_size),
     GNUNET_PQ_query_param_fixed_size (out_wire_off,
@@ -1394,7 +1394,7 @@ postgres_update_wire_auditor_progress (void *cls,
   struct GNUNET_PQ_QueryParam params[] = {
     GNUNET_PQ_query_param_uint64 (&pp->last_reserve_in_serial_id),
     GNUNET_PQ_query_param_uint64 (&pp->last_wire_out_serial_id),
-    GNUNET_PQ_query_param_absolute_time (&pp->last_timestamp),
+    TALER_PQ_query_param_absolute_time (&pp->last_timestamp),
     GNUNET_PQ_query_param_fixed_size (in_wire_off,
                                       wire_off_size),
     GNUNET_PQ_query_param_fixed_size (out_wire_off,
@@ -1438,7 +1438,7 @@ postgres_get_wire_auditor_progress (void *cls,
                                   &pp->last_reserve_in_serial_id),
     GNUNET_PQ_result_spec_uint64 ("last_wire_wire_out_serial_id",
                                   &pp->last_wire_out_serial_id),
-    GNUNET_PQ_result_spec_absolute_time ("last_timestamp",
+    TALER_PQ_result_spec_absolute_time ("last_timestamp",
                                          &pp->last_timestamp),
     GNUNET_PQ_result_spec_variable_size ("wire_in_off",
                                          in_wire_off,
@@ -1491,7 +1491,7 @@ postgres_insert_reserve_info (void *cls,
     GNUNET_PQ_query_param_auto_from_type (master_pub),
     TALER_PQ_query_param_amount (reserve_balance),
     TALER_PQ_query_param_amount (withdraw_fee_balance),
-    GNUNET_PQ_query_param_absolute_time (&expiration_date),
+    TALER_PQ_query_param_absolute_time (&expiration_date),
     GNUNET_PQ_query_param_end
   };
 
@@ -1531,7 +1531,7 @@ postgres_update_reserve_info (void *cls,
   struct GNUNET_PQ_QueryParam params[] = {
     TALER_PQ_query_param_amount (reserve_balance),
     TALER_PQ_query_param_amount (withdraw_fee_balance),
-    GNUNET_PQ_query_param_absolute_time (&expiration_date),
+    TALER_PQ_query_param_absolute_time (&expiration_date),
     GNUNET_PQ_query_param_auto_from_type (reserve_pub),
     GNUNET_PQ_query_param_auto_from_type (master_pub),
     GNUNET_PQ_query_param_end
@@ -1606,7 +1606,7 @@ postgres_get_reserve_info (void *cls,
   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_absolute_time ("expiration_date", expiration_date),
+    TALER_PQ_result_spec_absolute_time ("expiration_date", expiration_date),
     GNUNET_PQ_result_spec_uint64 ("auditor_reserves_rowid", rowid),
     GNUNET_PQ_result_spec_end
   };
@@ -2066,7 +2066,7 @@ postgres_insert_historic_denom_revenue (void *cls,
   struct GNUNET_PQ_QueryParam params[] = {
     GNUNET_PQ_query_param_auto_from_type (master_pub),
     GNUNET_PQ_query_param_auto_from_type (denom_pub_hash),
-    GNUNET_PQ_query_param_absolute_time (&revenue_timestamp),
+    TALER_PQ_query_param_absolute_time (&revenue_timestamp),
     TALER_PQ_query_param_amount (revenue_balance),
     GNUNET_PQ_query_param_end
   };
@@ -2122,7 +2122,7 @@ historic_denom_revenue_cb (void *cls,
     struct TALER_Amount revenue_balance;
     struct GNUNET_PQ_ResultSpec rs[] = {
       GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash", &denom_pub_hash),
-      GNUNET_PQ_result_spec_absolute_time ("revenue_timestamp", 
&revenue_timestamp),
+      TALER_PQ_result_spec_absolute_time ("revenue_timestamp", 
&revenue_timestamp),
       TALER_PQ_result_spec_amount ("revenue_balance", &revenue_balance),
       GNUNET_PQ_result_spec_end
     };
@@ -2213,7 +2213,7 @@ postgres_insert_historic_losses (void *cls,
   struct GNUNET_PQ_QueryParam params[] = {
     GNUNET_PQ_query_param_auto_from_type (master_pub),
     GNUNET_PQ_query_param_auto_from_type (denom_pub_hash),
-    GNUNET_PQ_query_param_absolute_time (&loss_timestamp),
+    TALER_PQ_query_param_absolute_time (&loss_timestamp),
     TALER_PQ_query_param_amount (loss_balance),
     GNUNET_PQ_query_param_end
   };
@@ -2269,7 +2269,7 @@ losses_cb (void *cls,
     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_absolute_time ("loss_timestamp", &loss_timestamp),
+      TALER_PQ_result_spec_absolute_time ("loss_timestamp", &loss_timestamp),
       TALER_PQ_result_spec_amount ("loss_balance", &loss_balance),
       GNUNET_PQ_result_spec_end
     };
@@ -2354,8 +2354,8 @@ postgres_insert_historic_reserve_revenue (void *cls,
 {
   struct GNUNET_PQ_QueryParam params[] = {
     GNUNET_PQ_query_param_auto_from_type (master_pub),
-    GNUNET_PQ_query_param_absolute_time (&start_time),
-    GNUNET_PQ_query_param_absolute_time (&end_time),
+    TALER_PQ_query_param_absolute_time (&start_time),
+    TALER_PQ_query_param_absolute_time (&end_time),
     TALER_PQ_query_param_amount (reserve_profits),
     GNUNET_PQ_query_param_end
   };
@@ -2410,8 +2410,8 @@ historic_reserve_revenue_cb (void *cls,
     struct GNUNET_TIME_Absolute end_date;
     struct TALER_Amount reserve_profits;
     struct GNUNET_PQ_ResultSpec rs[] = {
-      GNUNET_PQ_result_spec_absolute_time ("start_date", &start_date),
-      GNUNET_PQ_result_spec_absolute_time ("end_date", &end_date),
+      TALER_PQ_result_spec_absolute_time ("start_date", &start_date),
+      TALER_PQ_result_spec_absolute_time ("end_date", &end_date),
       TALER_PQ_result_spec_amount ("reserve_profits", &reserve_profits),
       GNUNET_PQ_result_spec_end
     };
diff --git a/src/auditordb/test_auditordb.c b/src/auditordb/test_auditordb.c
index 04c6d23..a5e8ba7 100644
--- a/src/auditordb/test_auditordb.c
+++ b/src/auditordb/test_auditordb.c
@@ -164,6 +164,7 @@ run (void *cls)
 
   struct GNUNET_TIME_Absolute now, past, future, date;
   now = GNUNET_TIME_absolute_get ();
+  (void) GNUNET_TIME_round_abs (&now);
   past = GNUNET_TIME_absolute_subtract (now,
                                         GNUNET_TIME_relative_multiply 
(GNUNET_TIME_UNIT_HOURS,
                                                                        4));
@@ -357,7 +358,7 @@ run (void *cls)
            (0 != memcmp (&withdraw_fee_balance2,
                          &withdraw_fee_balance,
                          sizeof (withdraw_fee_balance))) ) );
-          
+
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
               "Test: insert_denomination_balance\n");
 
diff --git a/src/bank-lib/Makefile.am b/src/bank-lib/Makefile.am
index 7489fe7..da8dd9d 100644
--- a/src/bank-lib/Makefile.am
+++ b/src/bank-lib/Makefile.am
@@ -17,7 +17,8 @@ libtalerbank_la_LDFLAGS = \
 libtalerbank_la_SOURCES = \
   bank_api_admin.c \
   bank_api_common.c bank_api_common.h \
-  bank_api_history.c
+  bank_api_history.c \
+  bank_api_reject.c
 
 libtalerbank_la_LIBADD = \
   $(top_builddir)/src/json/libtalerjson.la \
diff --git a/src/bank-lib/bank_api_admin.c b/src/bank-lib/bank_api_admin.c
index cebd934..f89b014 100644
--- a/src/bank-lib/bank_api_admin.c
+++ b/src/bank-lib/bank_api_admin.c
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  Copyright (C) 2015, 2016, 2017 GNUnet e.V.
+  Copyright (C) 2015, 2016, 2017 Taler Systems SA
 
   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
@@ -78,18 +78,20 @@ handle_admin_add_incoming_finished (void *cls,
                                     const json_t *json)
 {
   struct TALER_BANK_AdminAddIncomingHandle *aai = cls;
-  uint64_t serial_id = UINT64_MAX;
+  uint64_t row_id = UINT64_MAX;
+  enum TALER_ErrorCode ec;
 
   aai->job = NULL;
   switch (response_code)
   {
   case 0:
+    ec = TALER_EC_INVALID_RESPONSE;
     break;
   case MHD_HTTP_OK:
     {
       struct GNUNET_JSON_Specification spec[] = {
-        GNUNET_JSON_spec_uint64 ("serial_id",
-                                 &serial_id),
+        GNUNET_JSON_spec_uint64 ("row_id",
+                                 &row_id),
         GNUNET_JSON_spec_end()
       };
 
@@ -100,29 +102,36 @@ handle_admin_add_incoming_finished (void *cls,
       {
         GNUNET_break_op (0);
         response_code = 0;
+        ec = TALER_EC_INVALID_RESPONSE;
         break;
       }
+      ec = TALER_EC_NONE;
     }
     break;
   case MHD_HTTP_BAD_REQUEST:
     /* This should never happen, either us or the bank is buggy
        (or API version conflict); just pass JSON reply to the application */
+    ec = TALER_BANK_parse_ec_ (json);
     break;
   case MHD_HTTP_FORBIDDEN:
     /* Access denied */
+    ec = TALER_BANK_parse_ec_ (json);
     break;
   case MHD_HTTP_UNAUTHORIZED:
     /* Nothing really to verify, bank says one of the signatures is
        invalid; as we checked them, this should never happen, we
        should pass the JSON reply to the application */
+    ec = TALER_BANK_parse_ec_ (json);
     break;
   case MHD_HTTP_NOT_FOUND:
     /* Nothing really to verify, this should never
        happen, we should pass the JSON reply to the application */
+    ec = TALER_BANK_parse_ec_ (json);
     break;
   case MHD_HTTP_INTERNAL_SERVER_ERROR:
     /* Server had an internal issue; we should retry, but this API
        leaves this to the application */
+    ec = TALER_BANK_parse_ec_ (json);
     break;
   default:
     /* unexpected response code */
@@ -130,12 +139,14 @@ handle_admin_add_incoming_finished (void *cls,
                 "Unexpected response code %u\n",
                 (unsigned int) response_code);
     GNUNET_break (0);
+    ec = TALER_BANK_parse_ec_ (json);
     response_code = 0;
     break;
   }
   aai->cb (aai->cb_cls,
            response_code,
-           serial_id,
+           ec,
+           row_id,
            json);
   TALER_BANK_admin_add_incoming_cancel (aai);
 }
@@ -151,7 +162,7 @@ handle_admin_add_incoming_finished (void *cls,
  * @param bank_base_url URL of the bank (used to execute this request)
  * @param auth authentication data to send to the bank
  * @param exchange_base_url base URL of the exchange (for tracking)
- * @param wtid wire transfer identifier for the transfer
+ * @param subject wire transfer subject for the transfer
  * @param amount amount that was deposited
  * @param debit_account_no account number to withdraw from (53 bits at most)
  * @param credit_account_no account number to deposit into (53 bits at most)
@@ -166,7 +177,7 @@ TALER_BANK_admin_add_incoming (struct GNUNET_CURL_Context 
*ctx,
                                const char *bank_base_url,
                                const struct TALER_BANK_AuthenticationData 
*auth,
                                const char *exchange_base_url,
-                               const struct TALER_WireTransferIdentifierRawP 
*wtid,
+                               const char *subject,
                                const struct TALER_Amount *amount,
                                uint64_t debit_account_no,
                                uint64_t credit_account_no,
@@ -182,10 +193,10 @@ TALER_BANK_admin_add_incoming (struct GNUNET_CURL_Context 
*ctx,
     GNUNET_break (0);
     return NULL;
   }
-  admin_obj = json_pack ("{s:{s:s}, s:s, s:o, s:o, s:I, s:I}",
+  admin_obj = json_pack ("{s:{s:s}, s:s, s:s, s:o, s:I, s:I}",
                          "auth", "type", "basic",
                          "exchange_url", exchange_base_url,
-                         "wtid", GNUNET_JSON_from_data_auto (wtid),
+                         "subject", subject,
                          "amount", TALER_JSON_from_amount (amount),
                          "debit_account", (json_int_t) debit_account_no,
                          "credit_account", (json_int_t) credit_account_no);
diff --git a/src/bank-lib/bank_api_common.c b/src/bank-lib/bank_api_common.c
index 738d2a5..b4b1974 100644
--- a/src/bank-lib/bank_api_common.c
+++ b/src/bank-lib/bank_api_common.c
@@ -80,7 +80,7 @@ TALER_BANK_make_auth_header_ (const struct 
TALER_BANK_AuthenticationData *auth)
     authh = append (authh,
                     "X-Taler-Bank-Password",
                     auth->details.basic.password);
-    return authh;    
+    return authh;
   }
   return NULL;
 }
@@ -111,5 +111,33 @@ TALER_BANK_path_to_url_ (const char *u,
 }
 
 
+/**
+ * Parse error code given in @a json.
+ *
+ * @param json the json to parse
+ * @return error code, or #TALER_EC_INVALID if not found
+ */
+enum TALER_ErrorCode
+TALER_BANK_parse_ec_ (const json_t *json)
+{
+  uint32_t ec;
+
+  struct GNUNET_JSON_Specification spec[] = {
+    GNUNET_JSON_spec_uint32 ("ec",
+                             &ec),
+    GNUNET_JSON_spec_end()
+  };
+
+  if (GNUNET_OK !=
+      GNUNET_JSON_parse (json,
+                         spec,
+                         NULL, NULL))
+  {
+    GNUNET_break_op (0);
+    return TALER_EC_INVALID;
+  }
+  return (enum TALER_ErrorCode) ec;
+}
+
 
 /* end of bank_api_common.c */
diff --git a/src/bank-lib/bank_api_common.h b/src/bank-lib/bank_api_common.h
index 9d7a780..5d6578c 100644
--- a/src/bank-lib/bank_api_common.h
+++ b/src/bank-lib/bank_api_common.h
@@ -51,4 +51,14 @@ TALER_BANK_path_to_url_ (const char *u,
                          const char *path);
 
 
+/**
+ * Parse error code given in @a json.
+ *
+ * @param json the json to parse
+ * @return error code, or #TALER_EC_INVALID if not found
+ */
+enum TALER_ErrorCode
+TALER_BANK_parse_ec_ (const json_t *json);
+
+
 #endif
diff --git a/src/bank-lib/bank_api_history.c b/src/bank-lib/bank_api_history.c
index e134f20..fdc1e2a 100644
--- a/src/bank-lib/bank_api_history.c
+++ b/src/bank-lib/bank_api_history.c
@@ -89,7 +89,7 @@ parse_account_history (struct TALER_BANK_HistoryHandle *hh,
     struct TALER_BANK_TransferDetails td;
     const char *sign;
     uint64_t other_account;
-    uint64_t serial_id;
+    uint64_t row_id;
     enum TALER_BANK_Direction direction;
     struct GNUNET_JSON_Specification hist_spec[] = {
       GNUNET_JSON_spec_string ("sign",
@@ -99,7 +99,7 @@ parse_account_history (struct TALER_BANK_HistoryHandle *hh,
       GNUNET_JSON_spec_absolute_time ("date",
                                       &td.execution_date),
       GNUNET_JSON_spec_uint64 ("row_id",
-                               &serial_id),
+                               &row_id),
       GNUNET_JSON_spec_string ("wt_subject",
                                (const char **) &td.wire_transfer_subject),
       GNUNET_JSON_spec_uint64 ("counterpart",
@@ -118,22 +118,37 @@ parse_account_history (struct TALER_BANK_HistoryHandle 
*hh,
       return GNUNET_SYSERR;
     }
 
+    if (0 == strcasecmp (sign,
+                         "+"))
+      direction = TALER_BANK_DIRECTION_CREDIT;
+    else if (0 == strcasecmp (sign,
+                              "-"))
+      direction = TALER_BANK_DIRECTION_DEBIT;
+    else if (0 == strcasecmp (sign,
+                              "cancel+"))
+      direction = TALER_BANK_DIRECTION_CREDIT | TALER_BANK_DIRECTION_CANCEL;
+    else if (0 == strcasecmp (sign,
+                              "cancel-"))
+      direction = TALER_BANK_DIRECTION_DEBIT | TALER_BANK_DIRECTION_CANCEL;
+    else
+    {
+      GNUNET_break_op (0);
+      GNUNET_JSON_parse_free (hist_spec);
+      return GNUNET_SYSERR;
+    }
     td.account_details = json_pack ("{s:s, s:s, s:I}",
                                     "type", "test",
                                     "bank_uri", hh->bank_base_url,
                                     "account_number", (json_int_t) 
other_account);
-    direction = (0 == strcasecmp (sign,
-                                  "+"))
-      ? TALER_BANK_DIRECTION_CREDIT
-      : TALER_BANK_DIRECTION_DEBIT;
     hh->hcb (hh->hcb_cls,
              MHD_HTTP_OK,
+             TALER_EC_NONE,
              direction,
-             serial_id,
+             row_id,
              &td,
              transaction);
-    GNUNET_JSON_parse_free (hist_spec);
     json_decref (td.account_details);
+    GNUNET_JSON_parse_free (hist_spec);
   }
   return GNUNET_OK;
 }
@@ -153,11 +168,13 @@ handle_history_finished (void *cls,
                          const json_t *json)
 {
   struct TALER_BANK_HistoryHandle *hh = cls;
+  enum TALER_ErrorCode ec;
 
   hh->job = NULL;
   switch (response_code)
   {
   case 0:
+    ec = TALER_EC_INTERNAL_INVARIANT_FAILURE;
     break;
   case MHD_HTTP_OK:
     if (GNUNET_OK !=
@@ -166,31 +183,39 @@ handle_history_finished (void *cls,
     {
       GNUNET_break_op (0);
       response_code = 0;
+      ec = TALER_EC_INVALID_RESPONSE;
       break;
     }
     response_code = MHD_HTTP_NO_CONTENT; /* signal end of list */
+    ec = TALER_EC_NONE;
     break;
   case MHD_HTTP_NO_CONTENT:
+    ec = TALER_EC_NONE;
     break;
   case MHD_HTTP_BAD_REQUEST:
     /* This should never happen, either us or the bank is buggy
        (or API version conflict); just pass JSON reply to the application */
+    ec = TALER_BANK_parse_ec_ (json);
     break;
   case MHD_HTTP_FORBIDDEN:
     /* Access denied */
+    ec = TALER_BANK_parse_ec_ (json);
     break;
   case MHD_HTTP_UNAUTHORIZED:
     /* Nothing really to verify, bank says one of the signatures is
        invalid; as we checked them, this should never happen, we
        should pass the JSON reply to the application */
+    ec = TALER_BANK_parse_ec_ (json);
     break;
   case MHD_HTTP_NOT_FOUND:
     /* Nothing really to verify, this should never
        happen, we should pass the JSON reply to the application */
+    ec = TALER_BANK_parse_ec_ (json);
     break;
   case MHD_HTTP_INTERNAL_SERVER_ERROR:
     /* Server had an internal issue; we should retry, but this API
        leaves this to the application */
+    ec = TALER_BANK_parse_ec_ (json);
     break;
   default:
     /* unexpected response code */
@@ -198,11 +223,13 @@ handle_history_finished (void *cls,
                 "Unexpected response code %u\n",
                 (unsigned int) response_code);
     GNUNET_break (0);
+    ec = TALER_BANK_parse_ec_ (json);
     response_code = 0;
     break;
   }
   hh->hcb (hh->hcb_cls,
            response_code,
+           ec,
            TALER_BANK_DIRECTION_NONE,
            0LLU,
            NULL,
@@ -243,6 +270,8 @@ TALER_BANK_history (struct GNUNET_CURL_Context *ctx,
   struct TALER_BANK_HistoryHandle *hh;
   CURL *eh;
   char *url;
+  const char *dir;
+  const char *can;
 
   if (0 == num_results)
   {
@@ -254,36 +283,42 @@ TALER_BANK_history (struct GNUNET_CURL_Context *ctx,
     GNUNET_break (0);
     return NULL;
   }
+
+  dir = NULL;
+  if (TALER_BANK_DIRECTION_BOTH == (TALER_BANK_DIRECTION_BOTH & direction))
+    dir = "both";
+  else if (TALER_BANK_DIRECTION_CREDIT == (TALER_BANK_DIRECTION_CREDIT & 
direction))
+    dir = "credit";
+  else if (TALER_BANK_DIRECTION_DEBIT == (TALER_BANK_DIRECTION_BOTH & 
direction))
+    dir = "debit";
+  if (NULL == dir)
+  {
+    GNUNET_break (0);
+    return NULL;
+  }
+  if (TALER_BANK_DIRECTION_CANCEL == (TALER_BANK_DIRECTION_CANCEL & direction))
+    can = "show";
+  else
+    can = "omit";
   if (UINT64_MAX == start_row)
   {
-    if (TALER_BANK_DIRECTION_BOTH == direction)
-      GNUNET_asprintf (&url,
-                       "/history?auth=basic&account_number=%llu&delta=%lld",
-                       (unsigned long long) account_number,
-                       (long long) num_results);
-    else
-      GNUNET_asprintf (&url,
-                       
"/history?auth=basic&account_number=%llu&delta=%lld&direction=%s",
-                       (unsigned long long) account_number,
-                       (long long) num_results,
-                       (TALER_BANK_DIRECTION_CREDIT == direction) ? "credit" : 
"debit");
+    GNUNET_asprintf (&url,
+                     
"/history?auth=basic&account_number=%llu&delta=%lld&direction=%s&cancelled=%s",
+                     (unsigned long long) account_number,
+                     (long long) num_results,
+                     dir,
+                     can);
 
   }
   else
   {
-    if (TALER_BANK_DIRECTION_BOTH == direction)
-      GNUNET_asprintf (&url,
-                       
"/history?auth=basic&account_number=%llu&delta=%lld&start=%llu",
-                       (unsigned long long) account_number,
-                       (long long) num_results,
-                       (unsigned long long) start_row);
-    else
-      GNUNET_asprintf (&url,
-                       
"/history?auth=basic&account_number=%llu&delta=%lld&start=%llu&direction=%s",
-                       (unsigned long long) account_number,
-                       (long long) num_results,
-                       (unsigned long long) start_row,
-                       (TALER_BANK_DIRECTION_CREDIT == direction) ? "credit" : 
"debit");
+    GNUNET_asprintf (&url,
+                     
"/history?auth=basic&account_number=%llu&delta=%lld&start=%llu&direction=%s&cancelled=%s",
+                     (unsigned long long) account_number,
+                     (long long) num_results,
+                     (unsigned long long) start_row,
+                     dir,
+                     can);
   }
 
   hh = GNUNET_new (struct TALER_BANK_HistoryHandle);
diff --git a/src/bank-lib/bank_api_reject.c b/src/bank-lib/bank_api_reject.c
new file mode 100644
index 0000000..c630ccd
--- /dev/null
+++ b/src/bank-lib/bank_api_reject.c
@@ -0,0 +1,245 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2015, 2016, 2017 Taler Systems SA
+
+  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 bank-lib/bank_api_reject.c
+ * @brief Implementation of the /reject request of the bank's HTTP API
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "bank_api_common.h"
+#include <microhttpd.h> /* just for HTTP status codes */
+#include "taler_signatures.h"
+
+
+/**
+ * @brief A /reject Handle
+ */
+struct TALER_BANK_RejectHandle
+{
+
+  /**
+   * The url for this request.
+   */
+  char *request_url;
+
+  /**
+   * JSON encoding of the request to POST.
+   */
+  char *json_enc;
+
+  /**
+   * Handle for the request.
+   */
+  struct GNUNET_CURL_Job *job;
+
+  /**
+   * HTTP authentication-related headers for the request.
+   */
+  struct curl_slist *authh;
+
+  /**
+   * Function to call with the result.
+   */
+  TALER_BANK_RejectResultCallback cb;
+
+  /**
+   * Closure for @a cb.
+   */
+  void *cb_cls;
+
+};
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP /reject request.
+ *
+ * @param cls the `struct TALER_BANK_RejectHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param json parsed JSON result, NULL on error
+ */
+static void
+handle_reject_finished (void *cls,
+                        long response_code,
+                        const json_t *json)
+{
+  struct TALER_BANK_RejectHandle *rh = cls;
+  enum TALER_ErrorCode ec;
+
+  rh->job = NULL;
+  switch (response_code)
+  {
+  case 0:
+    ec = TALER_EC_INVALID_RESPONSE;
+    break;
+  case MHD_HTTP_OK:
+    GNUNET_break_op (0);
+    response_code = 0;
+    ec = TALER_EC_INVALID_RESPONSE;
+    break;
+  case MHD_HTTP_NO_CONTENT:
+    ec = TALER_EC_NONE;
+    break;
+  case MHD_HTTP_BAD_REQUEST:
+    /* This should never happen, either us or the bank is buggy
+       (or API version conflict); just pass JSON reply to the application */
+    ec = TALER_BANK_parse_ec_ (json);
+    break;
+  case MHD_HTTP_FORBIDDEN:
+    /* Access denied */
+    ec = TALER_BANK_parse_ec_ (json);
+    break;
+  case MHD_HTTP_UNAUTHORIZED:
+    /* Nothing really to verify, bank says one of the signatures is
+       invalid; as we checked them, this should never happen, we
+       should pass the JSON reply to the application */
+    ec = TALER_BANK_parse_ec_ (json);
+    break;
+  case MHD_HTTP_NOT_FOUND:
+    /* Nothing really to verify, this should never
+       happen, we should pass the JSON reply to the application */
+    ec = TALER_BANK_parse_ec_ (json);
+    break;
+  case MHD_HTTP_INTERNAL_SERVER_ERROR:
+    /* Server had an internal issue; we should retry, but this API
+       leaves this to the application */
+    ec = TALER_BANK_parse_ec_ (json);
+    break;
+  default:
+    /* unexpected response code */
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Unexpected response code %u\n",
+                (unsigned int) response_code);
+    GNUNET_break (0);
+    ec = TALER_BANK_parse_ec_ (json);
+    response_code = 0;
+    break;
+  }
+  rh->cb (rh->cb_cls,
+          response_code,
+          ec);
+  TALER_BANK_reject_cancel (rh);
+}
+
+
+/**
+ * Request rejection of a wire transfer, marking it as cancelled and voiding
+ * its effects.
+ *
+ * @param ctx curl context for the event loop
+ * @param bank_base_url URL of the bank (used to execute this request)
+ * @param auth authentication data to use
+ * @param account_number which account number should we query
+ * @param rowid transfer to reject
+ * @param rcb the callback to call with the operation result
+ * @param rcb_cls closure for @a rcb
+ * @return NULL
+ *         if the inputs are invalid.
+ *         In this case, the callback is not called.
+ */
+struct TALER_BANK_RejectHandle *
+TALER_BANK_reject (struct GNUNET_CURL_Context *ctx,
+                   const char *bank_base_url,
+                   const struct TALER_BANK_AuthenticationData *auth,
+                   uint64_t account_number,
+                   uint64_t rowid,
+                   TALER_BANK_RejectResultCallback rcb,
+                   void *rcb_cls)
+{
+  struct TALER_BANK_RejectHandle *rh;
+  json_t *reject_obj;
+  CURL *eh;
+
+  reject_obj = json_pack ("{s:{s:s}, s:I, s:I}",
+                          "auth", "type", "basic",
+                          "row_id", (json_int_t) rowid,
+                          "credit_account", (json_int_t) account_number);
+  if (NULL == reject_obj)
+  {
+    GNUNET_break (0);
+    return NULL;
+  }
+  rh = GNUNET_new (struct TALER_BANK_RejectHandle);
+  rh->cb = rcb;
+  rh->cb_cls = rcb_cls;
+  rh->request_url = TALER_BANK_path_to_url_ (bank_base_url,
+                                             "/reject");
+  rh->authh = TALER_BANK_make_auth_header_ (auth);
+  /* Append content type header here, can't do it in GNUNET_CURL_job_add
+     as that would override the CURLOPT_HTTPHEADER instead of appending. */
+  {
+    struct curl_slist *ext;
+
+    ext = curl_slist_append (rh->authh,
+                             "Content-Type: application/json");
+    if (NULL == ext)
+      GNUNET_break (0);
+    else
+      rh->authh = ext;
+  }
+  eh = curl_easy_init ();
+  GNUNET_assert (NULL != (rh->json_enc =
+                          json_dumps (reject_obj,
+                                      JSON_COMPACT)));
+  json_decref (reject_obj);
+  GNUNET_assert (CURLE_OK ==
+                 curl_easy_setopt (eh,
+                                   CURLOPT_HTTPHEADER,
+                                   rh->authh));
+  GNUNET_assert (CURLE_OK ==
+                 curl_easy_setopt (eh,
+                                   CURLOPT_URL,
+                                   rh->request_url));
+  GNUNET_assert (CURLE_OK ==
+                 curl_easy_setopt (eh,
+                                   CURLOPT_POSTFIELDS,
+                                   rh->json_enc));
+  GNUNET_assert (CURLE_OK ==
+                 curl_easy_setopt (eh,
+                                   CURLOPT_POSTFIELDSIZE,
+                                   strlen (rh->json_enc)));
+  rh->job = GNUNET_CURL_job_add (ctx,
+                                 eh,
+                                 GNUNET_NO,
+                                 &handle_reject_finished,
+                                 rh);
+  return rh;
+}
+
+
+/**
+ * Cancel an reject request.  This function cannot be used on a request
+ * handle if the response was is already served for it.
+ *
+ * @param rh the reject request handle
+ */
+void
+TALER_BANK_reject_cancel (struct TALER_BANK_RejectHandle *rh)
+{
+  if (NULL != rh->job)
+  {
+    GNUNET_CURL_job_cancel (rh->job);
+    rh->job = NULL;
+  }
+  curl_slist_free_all (rh->authh);
+  GNUNET_free (rh->request_url);
+  GNUNET_free (rh->json_enc);
+  GNUNET_free (rh);
+}
+
+
+/* end of bank_api_reject.c */
diff --git a/src/bank-lib/fakebank.c b/src/bank-lib/fakebank.c
index 2689241..d83c22a 100644
--- a/src/bank-lib/fakebank.c
+++ b/src/bank-lib/fakebank.c
@@ -80,7 +80,19 @@ struct Transaction
   /**
    * Number of this transaction.
    */
-  uint64_t serial_id;
+  uint64_t row_id;
+
+  /**
+   * Flag set if the transfer was rejected.
+   */
+  int rejected;
+
+  /**
+   * Has this transaction been subjected to #TALER_FAKEBANK_check()
+   * and should thus no longer be counted in
+   * #TALER_FAKEBANK_check_empty()?
+   */
+  int checked;
 };
 
 
@@ -145,15 +157,12 @@ TALER_FAKEBANK_check (struct TALER_FAKEBANK_Handle *h,
          (want_credit == t->credit_account) &&
          (0 == TALER_amount_cmp (want_amount,
                                  &t->amount)) &&
+         (GNUNET_NO == t->checked) &&
          (0 == strcasecmp (exchange_base_url,
                            t->exchange_base_url)) )
     {
-      GNUNET_CONTAINER_DLL_remove (h->transactions_head,
-                                   h->transactions_tail,
-                                   t);
-      *subject = t->subject;
-      GNUNET_free (t->exchange_base_url);
-      GNUNET_free (t);
+      *subject = GNUNET_strdup (t->subject);
+      t->checked = GNUNET_YES;
       return GNUNET_OK;
     }
   }
@@ -161,17 +170,21 @@ TALER_FAKEBANK_check (struct TALER_FAKEBANK_Handle *h,
            "Did not find matching transaction!\nI have:\n");
   for (struct Transaction *t = h->transactions_head; NULL != t; t = t->next)
   {
-    char *s;
-
-    s = TALER_amount_to_string (&t->amount);
+    if (GNUNET_YES == t->checked)
+      continue;
     fprintf (stderr,
              "%llu -> %llu (%s) from %s\n",
              (unsigned long long) t->debit_account,
              (unsigned long long) t->credit_account,
-             s,
+             TALER_amount2s (&t->amount),
              t->exchange_base_url);
-    GNUNET_free (s);
   }
+  fprintf (stderr,
+           "I wanted:\n%llu -> %llu (%s) from %s\n",
+           (unsigned long long) want_debit,
+           (unsigned long long) want_credit,
+           TALER_amount2s (want_amount),
+           exchange_base_url);
   return GNUNET_SYSERR;
 }
 
@@ -185,7 +198,7 @@ TALER_FAKEBANK_check (struct TALER_FAKEBANK_Handle *h,
  * @param amount amount to transfer
  * @param subject wire transfer subject to use
  * @param exchange_base_url exchange URL
- * @return serial_id of the transfer
+ * @return row_id of the transfer
  */
 uint64_t
 TALER_FAKEBANK_make_transfer (struct TALER_FAKEBANK_Handle *h,
@@ -208,14 +221,39 @@ TALER_FAKEBANK_make_transfer (struct 
TALER_FAKEBANK_Handle *h,
   t->credit_account = credit_account;
   t->amount = *amount;
   t->exchange_base_url = GNUNET_strdup (exchange_base_url);
-  t->serial_id = ++h->serial_counter;
+  t->row_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;
+  return t->row_id;
+}
+
+
+/**
+ * Reject incoming wire transfer to account @a credit_account
+ * as identified by @a rowid.
+ *
+ * @param h fake bank handle
+ * @param rowid identifies transfer to reject
+ * @param credit_account account number of owner of credited account
+ * @return #GNUNET_YES on success, #GNUNET_NO if the wire transfer was not 
found
+ */
+int
+TALER_FAKEBANK_reject_transfer (struct TALER_FAKEBANK_Handle *h,
+                                uint64_t rowid,
+                                uint64_t credit_account)
+{
+  for (struct Transaction *t = h->transactions_head; NULL != t; t = t->next)
+    if ( (t->row_id == rowid) &&
+         (t->credit_account == credit_account) )
+    {
+      t->rejected = GNUNET_YES;
+      return GNUNET_YES;
+    }
+  return GNUNET_NO;
 }
 
 
@@ -232,23 +270,35 @@ TALER_FAKEBANK_check_empty (struct TALER_FAKEBANK_Handle 
*h)
 {
   struct Transaction *t;
 
-  if (NULL == h->transactions_head)
+  t = h->transactions_head;
+  while (NULL != t)
+  {
+    if ( (GNUNET_YES != t->checked) &&
+         (GNUNET_YES != t->rejected) )
+      break;
+    t = t->next;
+  }
+  if (NULL == t)
     return GNUNET_OK;
-
   fprintf (stderr,
            "Expected empty transaction set, but I have:\n");
-  for (t = h->transactions_head; NULL != t; t = t->next)
+  while (NULL != t)
   {
-    char *s;
-
-    s = TALER_amount_to_string (&t->amount);
-    fprintf (stderr,
-             "%llu -> %llu (%s) from %s\n",
-             (unsigned long long) t->debit_account,
-             (unsigned long long) t->credit_account,
-             s,
-             t->exchange_base_url);
-    GNUNET_free (s);
+    if ( (GNUNET_YES != t->checked) &&
+         (GNUNET_YES != t->rejected) )
+    {
+      char *s;
+
+      s = TALER_amount_to_string (&t->amount);
+      fprintf (stderr,
+               "%llu -> %llu (%s) from %s\n",
+               (unsigned long long) t->debit_account,
+               (unsigned long long) t->credit_account,
+               s,
+               t->exchange_base_url);
+      GNUNET_free (s);
+    }
+    t = t->next;
   }
   return GNUNET_SYSERR;
 }
@@ -288,6 +338,62 @@ TALER_FAKEBANK_stop (struct TALER_FAKEBANK_Handle *h)
 
 
 /**
+ * Create and queue a bank error message with the HTTP response
+ * code @a response_code on connection @a connection.
+ *
+ * @param connection where to queue the reply
+ * @param response_code http status code to use
+ * @param ec taler error code to use
+ * @param message human readable error message
+ * @return MHD status code
+ */
+static int
+create_bank_error (struct MHD_Connection *connection,
+                   unsigned int response_code,
+                   enum TALER_ErrorCode ec,
+                   const char *message)
+{
+  json_t *json;
+  struct MHD_Response *resp;
+  void *json_str;
+  size_t json_len;
+  int ret;
+
+  json = json_pack ("{s:s, s:I}",
+                    "error",
+                    message,
+                    "ec",
+                    (json_int_t) ec);
+  json_str = json_dumps (json,
+                         JSON_INDENT(2));
+  json_decref (json);
+  if (NULL == json_str)
+  {
+    GNUNET_break (0);
+    return MHD_NO;
+  }
+  json_len = strlen (json_str);
+  resp = MHD_create_response_from_buffer (json_len,
+                                          json_str,
+                                          MHD_RESPMEM_MUST_FREE);
+  if (NULL == resp)
+  {
+    GNUNET_break (0);
+    free (json_str);
+    return MHD_NO;
+  }
+  (void) MHD_add_response_header (resp,
+                                  MHD_HTTP_HEADER_CONTENT_TYPE,
+                                  "application/json");
+  ret = MHD_queue_response (connection,
+                            response_code,
+                            resp);
+  MHD_destroy_response (resp);
+  return ret;
+}
+
+
+/**
  * Function called whenever MHD is done with a request.  If the
  * request was a POST, we may have stored a `struct Buffer *` in the
  * @a con_cls that might still need to be cleaned up.  Call the
@@ -335,7 +441,7 @@ handle_admin_add_incoming (struct TALER_FAKEBANK_Handle *h,
   json_t *json;
   struct MHD_Response *resp;
   int ret;
-  uint64_t serial_id;
+  uint64_t row_id;
 
   pr = GNUNET_JSON_post_parser (REQUEST_BUFFER_MAX,
                                 con_cls,
@@ -359,13 +465,13 @@ handle_admin_add_incoming (struct TALER_FAKEBANK_Handle 
*h,
     break;
   }
   {
-    const char *wtid;
+    const char *subject;
     uint64_t debit_account;
     uint64_t credit_account;
     const char *base_url;
     struct TALER_Amount amount;
     struct GNUNET_JSON_Specification spec[] = {
-      GNUNET_JSON_spec_string ("wtid", &wtid),
+      GNUNET_JSON_spec_string ("subject", &subject),
       GNUNET_JSON_spec_uint64 ("debit_account", &debit_account),
       GNUNET_JSON_spec_uint64 ("credit_account", &credit_account),
       TALER_JSON_spec_amount ("amount", &amount),
@@ -381,11 +487,11 @@ handle_admin_add_incoming (struct TALER_FAKEBANK_Handle 
*h,
       json_decref (json);
       return MHD_NO;
     }
-    serial_id = TALER_FAKEBANK_make_transfer (h,
+    row_id = TALER_FAKEBANK_make_transfer (h,
                                               debit_account,
                                               credit_account,
                                               &amount,
-                                              wtid,
+                                              subject,
                                               base_url);
     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
                 "Receiving incoming wire transfer: %llu->%llu from %s\n",
@@ -401,8 +507,8 @@ handle_admin_add_incoming (struct TALER_FAKEBANK_Handle *h,
     size_t json_len;
 
     json = json_pack ("{s:I}",
-                      "serial_id",
-                      (json_int_t) serial_id);
+                      "row_id",
+                      (json_int_t) row_id);
     json_str = json_dumps (json,
                            JSON_INDENT(2));
     json_decref (json);
@@ -434,6 +540,94 @@ handle_admin_add_incoming (struct TALER_FAKEBANK_Handle *h,
 
 
 /**
+ * Handle incoming HTTP request for /reject.
+ *
+ * @param h the fakebank handle
+ * @param connection the connection
+ * @param upload_data request data
+ * @param upload_data_size size of @a upload_data in bytes
+ * @param con_cls closure for request (a `struct Buffer *`)
+ * @return MHD result code
+ */
+static int
+handle_reject (struct TALER_FAKEBANK_Handle *h,
+               struct MHD_Connection *connection,
+               const char *upload_data,
+               size_t *upload_data_size,
+               void **con_cls)
+{
+  enum GNUNET_JSON_PostResult pr;
+  json_t *json;
+  struct MHD_Response *resp;
+  int ret;
+  int found;
+
+  pr = GNUNET_JSON_post_parser (REQUEST_BUFFER_MAX,
+                                con_cls,
+                                upload_data,
+                                upload_data_size,
+                                &json);
+  switch (pr)
+  {
+  case GNUNET_JSON_PR_OUT_OF_MEMORY:
+    GNUNET_break (0);
+    return MHD_NO;
+  case GNUNET_JSON_PR_CONTINUE:
+    return MHD_YES;
+  case GNUNET_JSON_PR_REQUEST_TOO_LARGE:
+    GNUNET_break (0);
+    return MHD_NO;
+  case GNUNET_JSON_PR_JSON_INVALID:
+    GNUNET_break (0);
+    return MHD_NO;
+  case GNUNET_JSON_PR_SUCCESS:
+    break;
+  }
+  {
+    uint64_t row_id;
+    uint64_t credit_account;
+    struct GNUNET_JSON_Specification spec[] = {
+      GNUNET_JSON_spec_uint64 ("row_id", &row_id),
+      GNUNET_JSON_spec_uint64 ("credit_account", &credit_account),
+      GNUNET_JSON_spec_end ()
+    };
+    if (GNUNET_OK !=
+        GNUNET_JSON_parse (json,
+                           spec,
+                           NULL, NULL))
+    {
+      GNUNET_break (0);
+      json_decref (json);
+      return MHD_NO;
+    }
+    found = TALER_FAKEBANK_reject_transfer (h,
+                                            row_id,
+                                            credit_account);
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "Rejected wire transfer #%llu (to %llu)\n",
+                (unsigned long long) row_id,
+                (unsigned long long) credit_account);
+  }
+  json_decref (json);
+
+  if (GNUNET_OK != found)
+    return create_bank_error (connection,
+                              MHD_HTTP_NOT_FOUND,
+                              TALER_EC_BANK_REJECT_NOT_FOUND,
+                              "transaction unknown");
+  /* finally build regular response */
+  resp = MHD_create_response_from_buffer (0,
+                                          NULL,
+                                          MHD_RESPMEM_PERSISTENT);
+  ret = MHD_queue_response (connection,
+                            MHD_HTTP_NO_CONTENT,
+                            resp);
+  MHD_destroy_response (resp);
+  return ret;
+}
+
+
+/**
  * Handle incoming HTTP request for /history
  *
  * @param h the fakebank handle
@@ -451,6 +645,7 @@ handle_history (struct TALER_FAKEBANK_Handle *h,
   const char *start;
   const char *dir;
   const char *acc;
+  const char *cancelled;
   unsigned long long account_number;
   unsigned long long start_number;
   long long count;
@@ -469,6 +664,9 @@ handle_history (struct TALER_FAKEBANK_Handle *h,
   dir = MHD_lookup_connection_value (connection,
                                      MHD_GET_ARGUMENT_KIND,
                                      "direction");
+  cancelled = MHD_lookup_connection_value (connection,
+                                           MHD_GET_ARGUMENT_KIND,
+                                           "cancelled");
   start = MHD_lookup_connection_value (connection,
                                        MHD_GET_ARGUMENT_KIND,
                                        "start");
@@ -496,7 +694,14 @@ handle_history (struct TALER_FAKEBANK_Handle *h,
          (1 != sscanf (start,
                        "%llu",
                        &start_number)) ) ||
-       ( (NULL != dir) &&
+       (NULL == dir) ||
+       (NULL == cancelled) ||
+       ( (0 != strcasecmp (cancelled,
+                           "OMIT")) &&
+         (0 != strcasecmp (cancelled,
+                           "SHOW")) ) ||
+       ( (0 != strcasecmp (dir,
+                           "BOTH")) &&
          (0 != strcasecmp (dir,
                            "CREDIT")) &&
          (0 != strcasecmp (dir,
@@ -513,13 +718,40 @@ handle_history (struct TALER_FAKEBANK_Handle *h,
               dir,
               (unsigned long long) account_number,
               start);
-  if (NULL == dir)
-    direction = TALER_BANK_DIRECTION_BOTH;
-  else if (0 == strcasecmp (dir,
-                            "CREDIT"))
+  if (0 == strcasecmp (dir,
+                       "CREDIT"))
+  {
     direction = TALER_BANK_DIRECTION_CREDIT;
-  else
+  }
+  else if (0 == strcasecmp (dir,
+                            "DEBIT"))
+  {
     direction = TALER_BANK_DIRECTION_DEBIT;
+  }
+  else if (0 == strcasecmp (dir,
+                            "BOTH"))
+  {
+    direction = TALER_BANK_DIRECTION_BOTH;
+  }
+  else
+  {
+    GNUNET_assert (0);
+    return MHD_NO;
+  }
+  if (0 == strcasecmp (cancelled,
+                       "OMIT"))
+  {
+    /* nothing */
+  } else if (0 == strcasecmp (cancelled,
+                              "SHOW"))
+  {
+    direction |= TALER_BANK_DIRECTION_CANCEL;
+  }
+  else
+  {
+    GNUNET_assert (0);
+    return MHD_NO;
+  }
   if (NULL == start)
   {
     if (count > 0)
@@ -532,11 +764,13 @@ handle_history (struct TALER_FAKEBANK_Handle *h,
     for (pos = h->transactions_head;
          NULL != pos;
          pos = pos->next)
-      if (pos->serial_id == start_number)
+      if (pos->row_id  == start_number)
         break;
     if (NULL == pos)
     {
-      GNUNET_break (0);
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "Invalid range specified, transaction %llu not known!\n",
+                  (unsigned long long) start_number);
       return MHD_NO;
     }
     /* range is exclusive, skip the matching entry */
@@ -557,6 +791,7 @@ handle_history (struct TALER_FAKEBANK_Handle *h,
   {
     json_t *trans;
     char *subject;
+    const char *sign;
 
     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
                 "Found transaction over %s from %llu to %llu\n",
@@ -564,10 +799,12 @@ handle_history (struct TALER_FAKEBANK_Handle *h,
                 (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) &&
-              (0 != (direction & TALER_BANK_DIRECTION_CREDIT) ) ) ) )
+    if ( (! ( ( (account_number == pos->debit_account) &&
+                (0 != (direction & TALER_BANK_DIRECTION_DEBIT)) ) ||
+              ( (account_number == pos->credit_account) &&
+                (0 != (direction & TALER_BANK_DIRECTION_CREDIT) ) ) ) ) ||
+         ( (0 == (direction & TALER_BANK_DIRECTION_CANCEL)) &&
+           (GNUNET_YES == pos->rejected) ) )
     {
       if (count > 0)
         pos = pos->next;
@@ -580,18 +817,23 @@ handle_history (struct TALER_FAKEBANK_Handle *h,
                      "%s %s",
                      pos->subject,
                      pos->exchange_base_url);
+    sign =
+      (account_number == pos->debit_account)
+      ? (pos->rejected ? "cancel-" : "-")
+      : (pos->rejected ? "cancel+" : "+");
     trans = json_pack ("{s:I, s:o, s:o, s:s, s:I, s:s}",
-                       "row_id", (json_int_t) pos->serial_id,
+                       "row_id", (json_int_t) pos->row_id,
                        "date", GNUNET_JSON_from_time_abs (pos->date),
                        "amount", TALER_JSON_from_amount (&pos->amount),
-                       "sign", (account_number == pos->debit_account) ? "-" : 
"+",
+                       "sign", sign,
                        "counterpart", (json_int_t) ( (account_number == 
pos->debit_account)
                                                      ? pos->credit_account
                                                      : pos->debit_account),
                        "wt_subject", subject);
+    GNUNET_assert (NULL != trans);
     GNUNET_free (subject);
-    json_array_append (history,
-                       trans);
+    GNUNET_assert (0 == json_array_append (history,
+                                           trans));
     if (count > 0)
     {
       pos = pos->next;
@@ -621,7 +863,8 @@ handle_history (struct TALER_FAKEBANK_Handle *h,
   }
 
   jresponse = json_pack ("{s:o}",
-                         "data", history);
+                         "data",
+                         history);
   if (NULL == jresponse)
   {
     GNUNET_break (0);
@@ -699,6 +942,15 @@ handle_mhd_request (void *cls,
                                       upload_data_size,
                                       con_cls);
   if ( (0 == strcasecmp (url,
+                         "/reject")) &&
+       (0 == strcasecmp (method,
+                         MHD_HTTP_METHOD_POST)) )
+    return handle_reject (h,
+                          connection,
+                          upload_data,
+                          upload_data_size,
+                          con_cls);
+  if ( (0 == strcasecmp (url,
                          "/history")) &&
        (0 == strcasecmp (method,
                          MHD_HTTP_METHOD_GET)) )
diff --git a/src/bank-lib/test_bank_api.c b/src/bank-lib/test_bank_api.c
index 19d15ca..c70ae88 100644
--- a/src/bank-lib/test_bank_api.c
+++ b/src/bank-lib/test_bank_api.c
@@ -46,21 +46,23 @@ run (void *cls)
       .details.history.direction = TALER_BANK_DIRECTION_BOTH,
       .details.history.start_row_ref = NULL,
       .details.history.num_results = 5 },
-    /* Move money from Exchange to Bank */
+    /* Move money from Bank to Exchange */
     { .oc = TBI_OC_ADMIN_ADD_INCOMING,
       .label = "deposit-1",
       .details.admin_add_incoming.exchange_base_url = "https://exchange.net/";, 
/* bogus */
+      .details.admin_add_incoming.subject = "subject 1",
       .details.admin_add_incoming.expected_response_code = MHD_HTTP_OK,
-      .details.admin_add_incoming.credit_account_no = 1,
-      .details.admin_add_incoming.debit_account_no = 2, /* Ignored */
+      .details.admin_add_incoming.credit_account_no = 2,
+      .details.admin_add_incoming.debit_account_no = 1,
       .details.admin_add_incoming.amount = "KUDOS:5.01" },
-    /* Move money from Exchange to Bank */
+    /* Move money from Bank to Exchange */
     { .oc = TBI_OC_ADMIN_ADD_INCOMING,
       .label = "deposit-2",
       .details.admin_add_incoming.exchange_base_url = "https://exchange.net/";, 
/* bogus */
+      .details.admin_add_incoming.subject = "subject 2",
       .details.admin_add_incoming.expected_response_code = MHD_HTTP_OK,
-      .details.admin_add_incoming.credit_account_no = 1,
-      .details.admin_add_incoming.debit_account_no = 2, /* Ignored */
+      .details.admin_add_incoming.credit_account_no = 2,
+      .details.admin_add_incoming.debit_account_no = 1,
       .details.admin_add_incoming.amount = "KUDOS:5.01" },
     /* Ask Exchange's incoming history */
     { .oc = TBI_OC_HISTORY,
@@ -69,7 +71,7 @@ run (void *cls)
       .details.history.direction = TALER_BANK_DIRECTION_CREDIT,
       .details.history.start_row_ref = NULL,
       .details.history.num_results = 5 },
-    /* Ask Exchange's outgoing history, 5 records into the future?? */
+    /* Ask Exchange's outgoing history, 5 records into the future */
     { .oc = TBI_OC_HISTORY,
       .label = "history-2d",
       .details.history.account_number = 2,
@@ -89,6 +91,29 @@ run (void *cls)
       .details.history.direction = TALER_BANK_DIRECTION_DEBIT,
       .details.history.start_row_ref = "deposit-1",
       .details.history.num_results = 5 },
+    /* FIXME(bug #5200): re-enable once implemented in bank
+    { .oc = TBI_OC_REJECT,
+      .label = "reject-1",
+      .details.reject.cmd_ref = "deposit-1" },
+    { .oc = TBI_OC_HISTORY,
+      .label = "history-r1",
+      .details.history.account_number = 2,
+      .details.history.direction = TALER_BANK_DIRECTION_CREDIT,
+      .details.history.start_row_ref = NULL,
+      .details.history.num_results = 5 },
+    { .oc = TBI_OC_HISTORY,
+      .label = "history-r2",
+      .details.history.account_number = 2,
+      .details.history.direction = TALER_BANK_DIRECTION_DEBIT,
+      .details.history.start_row_ref = NULL,
+      .details.history.num_results = 5 },
+    { .oc = TBI_OC_HISTORY,
+      .label = "history-r3",
+      .details.history.account_number = 2,
+      .details.history.direction = TALER_BANK_DIRECTION_BOTH | 
TALER_BANK_DIRECTION_CANCEL,
+      .details.history.start_row_ref = NULL,
+      .details.history.num_results = 5 },
+    */
     { .oc = TBI_OC_END }
   };
 
diff --git a/src/bank-lib/test_bank_api_with_fakebank.c 
b/src/bank-lib/test_bank_api_with_fakebank.c
index e16c3a9..b860669 100644
--- a/src/bank-lib/test_bank_api_with_fakebank.c
+++ b/src/bank-lib/test_bank_api_with_fakebank.c
@@ -48,6 +48,7 @@ run (void *cls)
     /* Add EUR:5.01 to account 1 */
     { .oc = TBI_OC_ADMIN_ADD_INCOMING,
       .label = "debit-1",
+      .details.admin_add_incoming.subject = "subject 1",
       .details.admin_add_incoming.expected_response_code = MHD_HTTP_OK,
       .details.admin_add_incoming.credit_account_no = 1,
       .details.admin_add_incoming.debit_account_no = 2,
@@ -68,6 +69,7 @@ run (void *cls)
       .details.history.num_results = 5 },
     { .oc = TBI_OC_ADMIN_ADD_INCOMING,
       .label = "debit-2",
+      .details.admin_add_incoming.subject = "subject 2",
       .details.admin_add_incoming.expected_response_code = MHD_HTTP_OK,
       .details.admin_add_incoming.credit_account_no = 3,
       .details.admin_add_incoming.debit_account_no = 2,
@@ -75,6 +77,7 @@ run (void *cls)
       .details.admin_add_incoming.amount = "KUDOS:3.21" },
     { .oc = TBI_OC_ADMIN_ADD_INCOMING,
       .label = "credit-2",
+      .details.admin_add_incoming.subject = "credit 2",
       .details.admin_add_incoming.expected_response_code = MHD_HTTP_OK,
       .details.admin_add_incoming.credit_account_no = 2,
       .details.admin_add_incoming.debit_account_no = 3,
@@ -105,6 +108,40 @@ run (void *cls)
     /* check transfer list is now empty */
     { .oc = TBI_OC_EXPECT_TRANSFERS_EMPTY,
       .label = "expect-empty" },
+    /* Add EUR:5.01 to account 1 */
+    { .oc = TBI_OC_ADMIN_ADD_INCOMING,
+      .label = "credit-for-reject-1",
+      .details.admin_add_incoming.subject = "subject 3",
+      .details.admin_add_incoming.expected_response_code = MHD_HTTP_OK,
+      .details.admin_add_incoming.credit_account_no = 2,
+      .details.admin_add_incoming.debit_account_no = 1,
+      .details.admin_add_incoming.exchange_base_url = "https://exchange.net/";,
+      .details.admin_add_incoming.amount = "KUDOS:5.01" },
+    { .oc = TBI_OC_REJECT,
+      .label = "reject-1",
+      .details.reject.cmd_ref = "credit-for-reject-1" },
+    { .oc = TBI_OC_HISTORY,
+      .label = "history-r1",
+      .details.history.account_number = 1,
+      .details.history.direction = TALER_BANK_DIRECTION_BOTH,
+      /* range is exclusive, and everything up to and including "credit-2"
+         was already killed via TBI_OC_EXPECT_TRANSFER and thus won't show
+         in the history. So to see the rejected transfer, we need to start
+         looking after "credit-2" */
+      .details.history.start_row_ref = NULL,
+      .details.history.num_results = 5 },
+    { .oc = TBI_OC_HISTORY,
+      .label = "history-r1c",
+      .details.history.account_number = 1,
+      .details.history.direction = TALER_BANK_DIRECTION_BOTH | 
TALER_BANK_DIRECTION_CANCEL,
+      .details.history.start_row_ref = NULL,
+      .details.history.num_results = 5 },
+    { .oc = TBI_OC_EXPECT_TRANSFER,
+      .label = "expect-credit-reject-1",
+      .details.expect_transfer.cmd_ref = "credit-for-reject-1" },
+    /* check transfer list is now empty */
+    { .oc = TBI_OC_EXPECT_TRANSFERS_EMPTY,
+      .label = "expect-empty-2" },
     { .oc = TBI_OC_END }
   };
 
diff --git a/src/bank-lib/test_bank_interpreter.c 
b/src/bank-lib/test_bank_interpreter.c
index f5aee8e..ab41ed6 100644
--- a/src/bank-lib/test_bank_interpreter.c
+++ b/src/bank-lib/test_bank_interpreter.c
@@ -110,7 +110,6 @@ static const struct TBI_Command *
 find_command (const struct InterpreterState *is,
               const char *label)
 {
-  unsigned int i;
   const struct TBI_Command *cmd;
 
   if (NULL == label)
@@ -119,7 +118,7 @@ find_command (const struct InterpreterState *is,
                 "Attempt to lookup command for empty label\n");
     return NULL;
   }
-  for (i=0;TBI_OC_END != (cmd = &is->commands[i])->oc;i++)
+  for (unsigned int i=0;TBI_OC_END != (cmd = &is->commands[i])->oc;i++)
     if ( (NULL != cmd->label) &&
          (0 == strcmp (cmd->label,
                        label)) )
@@ -132,6 +131,34 @@ find_command (const struct InterpreterState *is,
 
 
 /**
+ * Test if the /admin/add/incoming transaction at offset @a off
+ * has been /rejected.
+ *
+ * @param is interpreter state (where we are right now)
+ * @param off offset of the command to test for rejection
+ * @return #GNUNET_YES if the command at @a off was cancelled
+ */
+static int
+test_cancelled (struct InterpreterState *is,
+                unsigned int off)
+{
+  const struct TBI_Command *cmd = &is->commands[off];
+
+  for (unsigned int i=0;i<is->ip;i++)
+  {
+    const struct TBI_Command *c = &is->commands[i];
+
+    if (TBI_OC_REJECT != c->oc)
+      continue;
+    if (0 == strcmp (c->details.reject.cmd_ref,
+                     cmd->label))
+      return GNUNET_YES;
+  }
+  return GNUNET_NO;
+}
+
+
+/**
  * Item in the transaction history, as reconstructed from the
  * command history.
  */
@@ -146,7 +173,7 @@ struct History
   /**
    * Serial ID of the wire transfer.
    */
-  uint64_t serial_id;
+  uint64_t row_id;
 
   /**
    * Direction of the transfer.
@@ -214,12 +241,13 @@ build_history (struct InterpreterState *is,
   for (unsigned int off = start;off != end + inc; off += inc)
   {
     const struct TBI_Command *pos = &is->commands[off];
+    int cancelled;
 
     if (TBI_OC_ADMIN_ADD_INCOMING != pos->oc)
       continue;
     if ( (NULL != ref) &&
-         (ref->details.admin_add_incoming.serial_id ==
-          pos->details.admin_add_incoming.serial_id) )
+         (ref->details.admin_add_incoming.row_id ==
+          pos->details.admin_add_incoming.row_id) )
     {
       total = 0;
       ok = GNUNET_YES;
@@ -229,6 +257,11 @@ build_history (struct InterpreterState *is,
       continue; /* skip until we find the marker */
     if (total >= cmd->details.history.num_results * inc)
       break; /* hit limit specified by command */
+    cancelled = test_cancelled (is,
+                                off);
+    if ( (GNUNET_YES == cancelled) &&
+         (0 == (cmd->details.history.direction & TALER_BANK_DIRECTION_CANCEL)) 
)
+      continue;
     if ( ( (0 != (cmd->details.history.direction & 
TALER_BANK_DIRECTION_CREDIT)) &&
            (cmd->details.history.account_number ==
             pos->details.admin_add_incoming.credit_account_no)) ||
@@ -253,12 +286,13 @@ build_history (struct InterpreterState *is,
   for (unsigned int off = start;off != end + inc; off += inc)
   {
     const struct TBI_Command *pos = &is->commands[off];
+    int cancelled;
 
     if (TBI_OC_ADMIN_ADD_INCOMING != pos->oc)
       continue;
     if ( (NULL != ref) &&
-         (ref->details.admin_add_incoming.serial_id ==
-          pos->details.admin_add_incoming.serial_id) )
+         (ref->details.admin_add_incoming.row_id ==
+          pos->details.admin_add_incoming.row_id) )
     {
       total = 0;
       ok = GNUNET_YES;
@@ -268,7 +302,6 @@ build_history (struct InterpreterState *is,
       continue; /* skip until we find the marker */
     if (total >= cmd->details.history.num_results * inc)
       break; /* hit limit specified by command */
-
     if ( ( (0 != (cmd->details.history.direction & 
TALER_BANK_DIRECTION_CREDIT)) &&
            (cmd->details.history.account_number ==
             pos->details.admin_add_incoming.credit_account_no)) &&
@@ -280,11 +313,19 @@ build_history (struct InterpreterState *is,
       continue;
     }
 
+    cancelled = test_cancelled (is,
+                                off);
+    if ( (GNUNET_YES == cancelled) &&
+         (0 == (cmd->details.history.direction & TALER_BANK_DIRECTION_CANCEL)) 
)
+      continue;
+
     if ( (0 != (cmd->details.history.direction & TALER_BANK_DIRECTION_CREDIT)) 
&&
          (cmd->details.history.account_number ==
           pos->details.admin_add_incoming.credit_account_no))
     {
       h[total].direction = TALER_BANK_DIRECTION_CREDIT;
+      if (GNUNET_YES == cancelled)
+        h[total].direction |= TALER_BANK_DIRECTION_CANCEL;
       h[total].details.account_details
         = json_pack ("{s:s, s:s, s:I}",
                      "type",
@@ -300,6 +341,8 @@ build_history (struct InterpreterState *is,
             pos->details.admin_add_incoming.debit_account_no))
     {
       h[total].direction = TALER_BANK_DIRECTION_DEBIT;
+      if (GNUNET_YES == cancelled)
+        h[total].direction |= TALER_BANK_DIRECTION_CANCEL;
       h[total].details.account_details
         = json_pack ("{s:s, s:s, s:I}",
                      "type",
@@ -321,19 +364,12 @@ build_history (struct InterpreterState *is,
                      TALER_string_to_amount 
(pos->details.admin_add_incoming.amount,
                                              &h[total].details.amount));
       /* h[total].execution_date; // unknown here */
-      h[total].serial_id
-        = pos->details.admin_add_incoming.serial_id;
-      {
-        char *ws;
-
-        ws = GNUNET_STRINGS_data_to_string_alloc 
(&pos->details.admin_add_incoming.wtid,
-                                                  sizeof (struct 
TALER_WireTransferIdentifierRawP));
-        GNUNET_asprintf (&h[total].details.wire_transfer_subject,
-                         "%s %s",
-                         ws,
-                         pos->details.admin_add_incoming.exchange_base_url);
-        GNUNET_free (ws);
-      }
+      h[total].row_id
+        = pos->details.admin_add_incoming.row_id;
+      GNUNET_asprintf (&h[total].details.wire_transfer_subject,
+                       "%s %s",
+                       pos->details.admin_add_incoming.subject,
+                       pos->details.admin_add_incoming.exchange_base_url);
       total++;
     }
   }
@@ -371,7 +407,7 @@ print_expected (struct History *h,
                 (unsigned long long) i,
                 (TALER_BANK_DIRECTION_CREDIT == h[i].direction) ? "+" : "-",
                 TALER_amount2s (&h[i].details.amount),
-                (unsigned long long) h[i].serial_id,
+                (unsigned long long) h[i].row_id,
                 h[i].details.wire_transfer_subject,
                 acc);
     if (NULL != acc)
@@ -489,25 +525,41 @@ interpreter_run (void *cls);
 
 
 /**
+ * Run the next command.
+ *
+ * @param is interpreter to progress
+ */
+static void
+next (struct InterpreterState *is)
+{
+  is->ip++;
+  is->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
+                                       is);
+}
+
+
+/**
  * Function called upon completion of our /admin/add/incoming request.
  *
  * @param cls closure with the interpreter state
  * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful 
status request
  *                    0 if the bank's reply is bogus (fails to follow the 
protocol)
- * @param serial_id unique ID of the wire transfer in the bank's records; 
UINT64_MAX on error
+ * @param ec taler status code
+ * @param row_id unique ID of the wire transfer in the bank's records; 
UINT64_MAX on error
  * @param json detailed response from the HTTPD, or NULL if reply was not in 
JSON
  */
 static void
 add_incoming_cb (void *cls,
                  unsigned int http_status,
-                 uint64_t serial_id,
+                 enum TALER_ErrorCode ec,
+                 uint64_t row_id,
                  const json_t *json)
 {
   struct InterpreterState *is = cls;
   struct TBI_Command *cmd = &is->commands[is->ip];
 
   cmd->details.admin_add_incoming.aih = NULL;
-  cmd->details.admin_add_incoming.serial_id = serial_id;
+  cmd->details.admin_add_incoming.row_id = row_id;
   if (cmd->details.admin_add_incoming.expected_response_code != http_status)
   {
     GNUNET_break (0);
@@ -522,9 +574,7 @@ add_incoming_cb (void *cls,
     fail (is);
     return;
   }
-  is->ip++;
-  is->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
-                                       is);
+  next (is);
 }
 
 
@@ -538,16 +588,18 @@ add_incoming_cb (void *cls,
  *                    #MHD_HTTP_NO_CONTENT if there are no more results; on 
success the
  *                    last callback is always of this status (even if 
`abs(num_results)` were
  *                    already returned).
+ * @param ec taler status code
  * @param dir direction of the transfer
- * @param serial_id monotonically increasing counter corresponding to the 
transaction
+ * @param row_id monotonically increasing counter corresponding to the 
transaction
  * @param details details about the wire transfer
  * @param json detailed response from the HTTPD, or NULL if reply was not in 
JSON
  */
 static void
 history_cb (void *cls,
             unsigned int http_status,
+            enum TALER_ErrorCode ec,
             enum TALER_BANK_Direction dir,
-            uint64_t serial_id,
+            uint64_t row_id,
             const struct TALER_BANK_TransferDetails *details,
             const json_t *json)
 {
@@ -580,9 +632,7 @@ history_cb (void *cls,
       fail (is);
       return;
     }
-    is->ip++;
-    is->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
-                                         is);
+    next (is);
     return;
   }
   if (GNUNET_OK !=
@@ -613,6 +663,38 @@ history_cb (void *cls,
 
 
 /**
+ * Callbacks of this type are used to serve the result of asking
+ * the bank to reject an incoming wire transfer.
+ *
+ * @param cls closure
+ * @param http_status HTTP response code, #MHD_HTTP_NO_CONTENT (204) for 
successful status request;
+ *                    #MHD_HTTP_NOT_FOUND if the rowid is unknown;
+ *                    0 if the bank's reply is bogus (fails to follow the 
protocol),
+ * @param ec detailed error code
+ */
+static void
+reject_cb (void *cls,
+           unsigned int http_status,
+           enum TALER_ErrorCode ec)
+{
+  struct InterpreterState *is = cls;
+  struct TBI_Command *cmd = &is->commands[is->ip];
+
+  cmd->details.reject.rh = NULL;
+  if (MHD_HTTP_NO_CONTENT != http_status)
+  {
+    GNUNET_break (0);
+    fprintf (stderr,
+             "Unexpected response code %u:\n",
+             http_status);
+    fail (is);
+    return;
+  }
+  next (is);
+}
+
+
+/**
  * Run the main interpreter loop that performs bank operations.
  *
  * @param cls contains the `struct InterpreterState`
@@ -658,15 +740,29 @@ interpreter_run (void *cls)
       fail (is);
       return;
     }
-    GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
-                                &cmd->details.admin_add_incoming.wtid,
-                                sizeof (cmd->details.admin_add_incoming.wtid));
+    GNUNET_break (NULL != cmd->details.admin_add_incoming.subject);
+    switch (cmd->details.admin_add_incoming.debit_account_no)
+    {
+    case 1:
+      auth.details.basic.username = "Bank";
+      break;
+    case 2:
+      auth.details.basic.username = "Exchange";
+      break;
+    case 3:
+      auth.details.basic.username = "user3";
+      auth.details.basic.password = "pass3";
+      break;
+    default:
+      GNUNET_break (0);
+      break;
+    }
     cmd->details.admin_add_incoming.aih
       = TALER_BANK_admin_add_incoming (is->ctx,
                                        "http://localhost:8080";,
                                        &auth,
                                        
cmd->details.admin_add_incoming.exchange_base_url,
-                                       &cmd->details.admin_add_incoming.wtid,
+                                       cmd->details.admin_add_incoming.subject,
                                        &amount,
                                        
cmd->details.admin_add_incoming.debit_account_no,
                                        
cmd->details.admin_add_incoming.credit_account_no,
@@ -693,9 +789,21 @@ interpreter_run (void *cls)
       ref = NULL;
     }
     if (NULL != ref)
-      rowid = ref->details.admin_add_incoming.serial_id;
+      rowid = ref->details.admin_add_incoming.row_id;
     else
       rowid = UINT64_MAX;
+    switch (cmd->details.history.account_number)
+    {
+    case 1:
+      auth.details.basic.username = "Bank";
+      break;
+    case 2:
+      auth.details.basic.username = "Exchange";
+      break;
+    default:
+      GNUNET_break (0);
+      break;
+    }
     cmd->details.history.hh
       = TALER_BANK_history (is->ctx,
                             "http://localhost:8080";,
@@ -722,7 +830,6 @@ interpreter_run (void *cls)
                                            &amount));
     {
       char *subject;
-      char *expect;
 
       if (GNUNET_OK !=
           TALER_FAKEBANK_check (is->fakebank,
@@ -736,22 +843,17 @@ interpreter_run (void *cls)
         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))
+      if (0 != strcmp (ref->details.admin_add_incoming.subject,
+                       subject))
       {
-        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,
-                                         is);
+    next (is);
    return;
   case TBI_OC_EXPECT_TRANSFERS_EMPTY:
     if (GNUNET_OK != TALER_FAKEBANK_check_empty (is->fakebank))
@@ -760,9 +862,39 @@ interpreter_run (void *cls)
       fail (is);
       return;
     }
-    is->ip++;
-    is->task = GNUNET_SCHEDULER_add_now (&interpreter_run,
-                                         is);
+    next (is);
+    return;
+  case TBI_OC_REJECT:
+    ref = find_command (is,
+                        cmd->details.reject.cmd_ref);
+    GNUNET_assert (NULL != ref);
+    GNUNET_assert (TBI_OC_ADMIN_ADD_INCOMING == ref->oc);
+    switch (ref->details.admin_add_incoming.debit_account_no)
+    {
+    case 1:
+      auth.details.basic.username = "Bank";
+      break;
+    case 2:
+      auth.details.basic.username = "Exchange";
+      break;
+    default:
+      GNUNET_break (0);
+      break;
+    }
+    cmd->details.reject.rh
+      = TALER_BANK_reject (is->ctx,
+                           "http://localhost:8080";,
+                           &auth,
+                           ref->details.admin_add_incoming.credit_account_no,
+                           ref->details.admin_add_incoming.row_id,
+                           &reject_cb,
+                           is);
+    if (NULL == cmd->details.reject.rh)
+    {
+      GNUNET_break (0);
+      fail (is);
+      return;
+    }
     return;
   default:
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
@@ -802,7 +934,6 @@ do_shutdown (void *cls)
 {
   struct InterpreterState *is = cls;
   struct TBI_Command *cmd;
-  unsigned int i;
 
   if (NULL != is->timeout_task)
   {
@@ -810,7 +941,7 @@ do_shutdown (void *cls)
     is->timeout_task = NULL;
   }
 
-  for (i=0;TBI_OC_END != (cmd = &is->commands[i])->oc;i++)
+  for (unsigned int i=0;TBI_OC_END != (cmd = &is->commands[i])->oc;i++)
   {
     switch (cmd->oc)
     {
@@ -843,6 +974,17 @@ do_shutdown (void *cls)
       break;
     case TBI_OC_EXPECT_TRANSFERS_EMPTY:
       break;
+    case TBI_OC_REJECT:
+      if (NULL != cmd->details.reject.rh)
+      {
+        GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                    "Command %u (%s) did not complete\n",
+                    i,
+                    cmd->label);
+        TALER_BANK_reject_cancel (cmd->details.reject.rh);
+        cmd->details.reject.rh = NULL;
+      }
+      break;
     default:
       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                   "Unknown instruction %d at %u (%s)\n",
diff --git a/src/bank-lib/test_bank_interpreter.h 
b/src/bank-lib/test_bank_interpreter.h
index d4e9c1a..010538a 100644
--- a/src/bank-lib/test_bank_interpreter.h
+++ b/src/bank-lib/test_bank_interpreter.h
@@ -57,7 +57,12 @@ enum TBI_OpCode
   /**
    * Expect that we have exhaustively gone over all transfers at fakebank.
    */
-  TBI_OC_EXPECT_TRANSFERS_EMPTY
+  TBI_OC_EXPECT_TRANSFERS_EMPTY,
+
+  /**
+   * Reject incoming transfer.
+   */
+  TBI_OC_REJECT
 
 };
 
@@ -110,10 +115,9 @@ struct TBI_Command
       const char *exchange_base_url;
 
       /**
-       * Wire transfer identifier to use.  Initialized to
-       * a random value.
+       * Wire transfer subject to use.
        */
-      struct TALER_WireTransferIdentifierRawP wtid;
+      const char *subject;
 
       /**
        * Which response code do we expect for this command?
@@ -128,7 +132,7 @@ struct TBI_Command
       /**
        * The serial ID for this record, as returned by the bank.
        */
-      uint64_t serial_id;
+      uint64_t row_id;
 
     } admin_add_incoming;
 
@@ -186,6 +190,23 @@ struct TBI_Command
 
     } expect_transfer;
 
+    /**
+     * Execute /reject operation.
+     */
+    struct {
+
+      /**
+       * Reference to the matching transfer that is now to be rejected.
+       */
+      const char *cmd_ref;
+
+      /**
+       * Set to the API's handle during the operation.
+       */
+      struct TALER_BANK_RejectHandle *rh;
+
+    } reject;
+
   } details;
 
 };
diff --git a/src/benchmark/Makefile.am b/src/benchmark/Makefile.am
index ad386e9..d8c1f16 100644
--- a/src/benchmark/Makefile.am
+++ b/src/benchmark/Makefile.am
@@ -16,6 +16,8 @@ taler_exchange_benchmark_LDADD = \
   $(top_builddir)/src/json/libtalerjson.la \
   $(top_builddir)/src/util/libtalerutil.la \
   $(top_builddir)/src/exchange-lib/libtalerexchange.la \
+  $(top_builddir)/src/bank-lib/libtalerfakebank.la \
+  $(top_builddir)/src/bank-lib/libtalerbank.la \
   -lgnunetjson \
   -lgnunetcurl \
   -lgnunetutil \
diff --git a/src/benchmark/taler-exchange-benchmark.c 
b/src/benchmark/taler-exchange-benchmark.c
index a90773b..c4f5420 100644
--- a/src/benchmark/taler-exchange-benchmark.c
+++ b/src/benchmark/taler-exchange-benchmark.c
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  Copyright (C) 2014, 2015, 2016 GNUnet e.V. and INRIA
+  Copyright (C) 2014, 2015, 2016, 2017 Taler Systems SA
 
   TALER is free software; you can redistribute it and/or modify it under the
   terms of the GNU Lesser General Public License as published by the Free 
Software
@@ -26,6 +26,8 @@
 #include "taler_json_lib.h"
 #include <gnunet/gnunet_util_lib.h>
 #include <gnunet/gnunet_curl_lib.h>
+#include "taler_bank_service.h"
+#include "taler_fakebank_lib.h"
 #include <microhttpd.h>
 #include <jansson.h>
 
@@ -118,7 +120,7 @@ struct Reserve
   /**
    * Set to the API's handle during the operation.
    */
-  struct TALER_EXCHANGE_AdminAddIncomingHandle *aih;
+  struct TALER_BANK_AdminAddIncomingHandle *aih;
 
   /**
    * How much is left in this reserve.
@@ -233,6 +235,11 @@ struct Coin
 
 
 /**
+ * Handle to our fakebank.
+ */
+static struct TALER_FAKEBANK_Handle *fakebank;
+
+/**
  * DLL of reserves to fill.
  */
 static struct Reserve *empty_reserve_head;
@@ -358,11 +365,6 @@ static struct Reserve *reserves;
 static struct Coin *coins;
 
 /**
- * Transfer UUID counter, used in /admin/add/incoming
- */
-static unsigned int transfer_uuid;
-
-/**
  * This key (usually provided by merchants) is needed when depositing coins,
  * even though there is no merchant acting in the benchmark
  */
@@ -421,6 +423,16 @@ static unsigned long long num_refresh;
  */
 static unsigned long long num_admin;
 
+/**
+ * Process for the wirewatcher.
+ */
+static struct GNUNET_OS_Process *wirewatch_proc;
+
+/**
+ * ID of task called whenever we get a SIGCHILD.
+ */
+static struct GNUNET_SCHEDULER_Task *child_death_task;
+
 
 /**
  * Throw a weighted coin with @a probability.
@@ -623,7 +635,7 @@ static void
 melt_cb (void *cls,
          unsigned int http_status,
         enum TALER_ErrorCode ec,
-         uint16_t noreveal_index,
+         uint32_t noreveal_index,
          const struct TALER_ExchangePublicKeyP *exchange_pub,
          const json_t *full_response)
 {
@@ -1024,6 +1036,52 @@ withdraw_coin (struct Coin *coin)
 
 
 /**
+ * Pipe used to communicate child death via signal.
+ */
+static struct GNUNET_DISK_PipeHandle *sigpipe;
+
+
+/**
+ * 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 */
+}
+
+
+/**
+ * Task triggered whenever we receive a SIGCHLD (child
+ * process died).
+ *
+ * @param cls closure, NULL if we need to self-restart
+ */
+static void
+maint_wirewatch_death (void *cls)
+{
+  const struct GNUNET_DISK_FileHandle *pr;
+  char c[16];
+
+  child_death_task = NULL;
+  pr = GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_READ);
+  GNUNET_break (0 < GNUNET_DISK_file_read (pr, &c, sizeof (c)));
+  GNUNET_OS_process_wait (wirewatch_proc);
+  GNUNET_OS_process_destroy (wirewatch_proc);
+  wirewatch_proc = NULL;
+  continue_master_task ();
+}
+
+
+/**
  * Function called upon completion of our /admin/add/incoming request.
  * Its duty is withdrawing coins on the freshly created reserve.
  *
@@ -1031,12 +1089,14 @@ withdraw_coin (struct Coin *coin)
  * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful 
status request
  *                    0 if the exchange's reply is bogus (fails to follow the 
protocol)
  * @param ec taler-specific error code, #TALER_EC_NONE on success
+ * @param rowid unique wire transfer identifier of the bank
  * @param full_response full response from the exchange (for logging, in case 
of errors)
  */
 static void
 add_incoming_cb (void *cls,
                  unsigned int http_status,
                 enum TALER_ErrorCode ec,
+                 uint64_t rowid,
                  const json_t *full_response)
 {
   struct Reserve *r = cls;
@@ -1054,6 +1114,34 @@ add_incoming_cb (void *cls,
   GNUNET_CONTAINER_DLL_remove (empty_reserve_head,
                               empty_reserve_tail,
                               r);
+  if (NULL == empty_reserve_head)
+  {
+    const struct GNUNET_DISK_FileHandle *pr;
+
+    wirewatch_proc
+      = GNUNET_OS_start_process (GNUNET_NO,
+                                 GNUNET_OS_INHERIT_STD_ALL,
+                                 NULL, NULL, NULL,
+                                 "taler-exchange-wirewatch",
+                                 "taler-exchange-wirewatch",
+                                 "-c", config_file,
+                                 "-t", "test", /* use Taler's bank/fakebank */
+                                 "-T", /* exit when done */
+                                 NULL);
+    if (NULL == wirewatch_proc)
+    {
+      GNUNET_break (0);
+      fail ("could not start wirewatch process");
+      return;
+    }
+    pr = GNUNET_DISK_pipe_handle (sigpipe,
+                                  GNUNET_DISK_PIPE_END_READ);
+    child_death_task
+      = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
+                                        pr,
+                                        &maint_wirewatch_death, NULL);
+    return;
+  }
   continue_master_task ();
 }
 
@@ -1070,7 +1158,8 @@ fill_reserve (struct Reserve *r)
   struct TALER_ReservePublicKeyP reserve_pub;
   struct GNUNET_TIME_Absolute execution_date;
   struct TALER_Amount reserve_amount;
-  json_t *transfer_details;
+  char *subject;
+  struct TALER_BANK_AuthenticationData auth;
 
   GNUNET_assert (GNUNET_OK ==
                  TALER_amount_get_zero (currency,
@@ -1082,25 +1171,28 @@ fill_reserve (struct Reserve *r)
   priv = GNUNET_CRYPTO_eddsa_key_create ();
   r->reserve_priv.eddsa_priv = *priv;
   GNUNET_free (priv);
-  transfer_details = json_pack ("{s:I}",
-                               "uuid", (json_int_t) transfer_uuid++);
-  GNUNET_assert (NULL != transfer_details);
   GNUNET_CRYPTO_eddsa_key_get_public (&r->reserve_priv.eddsa_priv,
                                      &reserve_pub.eddsa_pub);
   r->left = reserve_amount;
   if (warm >= WARM_THRESHOLD)
     num_admin++;
-  r->aih = TALER_EXCHANGE_admin_add_incoming (exchange,
-                                             exchange_admin_uri,
-                                             &reserve_pub,
-                                             &reserve_amount,
-                                             execution_date,
-                                             bank_details,
-                                             transfer_details,
-                                             &add_incoming_cb,
-                                             r);
+  auth.method = TALER_BANK_AUTH_BASIC;
+  auth.details.basic.username = "Admin";
+  auth.details.basic.password = "x";
+  subject = GNUNET_STRINGS_data_to_string_alloc (&reserve_pub,
+                                                 sizeof (reserve_pub));
+  r->aih = TALER_BANK_admin_add_incoming (ctx,
+                                          "http://localhost:8082/";,
+                                          &auth,
+                                          "https://exchange/";,
+                                          subject,
+                                          &reserve_amount,
+                                          1, /* origin */
+                                          2, /* exchange account */
+                                          &add_incoming_cb,
+                                          r);
   GNUNET_assert (NULL != r->aih);
-  json_decref (transfer_details);
+  GNUNET_free (subject);
 }
 
 
@@ -1112,7 +1204,6 @@ fill_reserve (struct Reserve *r)
 static void
 benchmark_run (void *cls)
 {
-  unsigned int i;
   int refresh;
   struct Coin *coin;
 
@@ -1162,7 +1253,7 @@ benchmark_run (void *cls)
   }
 
   /* By default, pick a random valid coin to spend */
-  for (i=0;i<1000;i++)
+  for (unsigned int i=0;i<1000;i++)
   {
     coin = &coins[GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
                                            ncoins)];
@@ -1311,7 +1402,7 @@ do_shutdown (void *cls)
       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
                   "Cancelling %d-th reserve\n",
                   i);
-      TALER_EXCHANGE_admin_add_incoming_cancel(reserves[i].aih);
+      TALER_BANK_admin_add_incoming_cancel(reserves[i].aih);
       reserves[i].aih = NULL;
     }
   }
@@ -1367,6 +1458,11 @@ do_shutdown (void *cls)
       coin->denoms = NULL;
     }
   }
+  if (NULL != fakebank)
+  {
+    TALER_FAKEBANK_stop (fakebank);
+    fakebank = NULL;
+  }
   if (NULL != bank_details)
   {
     json_decref (bank_details);
@@ -1543,6 +1639,7 @@ run (void *cls)
   GNUNET_assert (NULL != ctx);
   rc = GNUNET_CURL_gnunet_rc_create (ctx);
   GNUNET_assert (NULL != rc);
+  fakebank = TALER_FAKEBANK_start (8082);
   exchange = TALER_EXCHANGE_connect (ctx,
                                      exchange_uri,
                                      &cert_cb, NULL,
@@ -1561,10 +1658,11 @@ main (int argc,
 {
   struct GNUNET_OS_Process *proc;
   unsigned int cnt;
+  struct GNUNET_SIGNAL_Context *shc_chld;
   const struct GNUNET_GETOPT_CommandLineOption options[] = {
     GNUNET_GETOPT_option_flag ('a',
                                "automate",
-                               "Initialize and start the bank and exchange",
+                               "Initialize and start the exchange",
                                &run_exchange),
     GNUNET_GETOPT_option_mandatory
     (GNUNET_GETOPT_option_cfgfile (&config_file)),
@@ -1697,8 +1795,15 @@ main (int argc,
     GNUNET_free (wget);
     fprintf (stderr, "\n");
   }
+  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,
                         NULL);
+  GNUNET_SIGNAL_handler_uninstall (shc_chld);
+  shc_chld = NULL;
+  GNUNET_DISK_pipe_close (sigpipe);
   if (run_exchange)
   {
     GNUNET_OS_process_kill (exchanged,
diff --git a/src/benchmark/taler-exchange-benchmark.conf 
b/src/benchmark/taler-exchange-benchmark.conf
index b264aaa..1306e50 100644
--- a/src/benchmark/taler-exchange-benchmark.conf
+++ b/src/benchmark/taler-exchange-benchmark.conf
@@ -36,6 +36,28 @@ BANK_ACCOUNT_NUMBER = 2
 # wallets with the bank information for transfers to the exchange.
 TEST_RESPONSE_FILE = ${TALER_CONFIG_HOME}/test.json
 
+WIRE-FEE-2017 = KUDOS:0.01
+WIRE-FEE-2018 = KUDOS:0.01
+WIRE-FEE-2019 = KUDOS:0.01
+WIRE-FEE-2020 = KUDOS:0.01
+WIRE-FEE-2021 = KUDOS:0.01
+WIRE-FEE-2022 = KUDOS:0.01
+WIRE-FEE-2023 = KUDOS:0.01
+WIRE-FEE-2024 = KUDOS:0.01
+WIRE-FEE-2025 = KUDOS:0.01
+WIRE-FEE-2026 = KUDOS:0.01
+
+CLOSING-FEE-2017 = KUDOS:0.01
+CLOSING-FEE-2018 = KUDOS:0.01
+CLOSING-FEE-2019 = KUDOS:0.01
+CLOSING-FEE-2020 = KUDOS:0.01
+CLOSING-FEE-2021 = KUDOS:0.01
+CLOSING-FEE-2022 = KUDOS:0.01
+CLOSING-FEE-2023 = KUDOS:0.01
+CLOSING-FEE-2024 = KUDOS:0.01
+CLOSING-FEE-2025 = KUDOS:0.01
+CLOSING-FEE-2026 = KUDOS:0.01
+
 
 [coin_kudos_1]
 value = KUDOS:1
diff --git a/src/exchange-lib/Makefile.am b/src/exchange-lib/Makefile.am
index b3a30e1..f92ea63 100644
--- a/src/exchange-lib/Makefile.am
+++ b/src/exchange-lib/Makefile.am
@@ -59,6 +59,7 @@ test_exchange_api_LDADD = \
   libtalerexchange.la \
   $(LIBGCRYPT_LIBS) \
   $(top_builddir)/src/bank-lib/libtalerfakebank.la \
+  $(top_builddir)/src/bank-lib/libtalerbank.la \
   $(top_builddir)/src/json/libtalerjson.la \
   $(top_builddir)/src/util/libtalerutil.la \
   -lgnunetcurl \
diff --git a/src/exchange-lib/exchange_api_common.c 
b/src/exchange-lib/exchange_api_common.c
index 9d2a827..de05348 100644
--- a/src/exchange-lib/exchange_api_common.c
+++ b/src/exchange-lib/exchange_api_common.c
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  Copyright (C) 2015-2017 Inria & GNUnet e.V.
+  Copyright (C) 2015-2017 Taler Systems SA
 
   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
@@ -143,8 +143,8 @@ TALER_EXCHANGE_verify_coin_history (const char *currency,
       struct GNUNET_JSON_Specification spec[] = {
         GNUNET_JSON_spec_fixed_auto ("coin_sig",
                                      &sig),
-        GNUNET_JSON_spec_fixed_auto ("session_hash",
-                                     &rm.session_hash),
+        GNUNET_JSON_spec_fixed_auto ("rc",
+                                     &rm.rc),
         TALER_JSON_spec_amount_nbo ("melt_fee",
                                    &rm.melt_fee),
         GNUNET_JSON_spec_end()
diff --git a/src/exchange-lib/exchange_api_handle.c 
b/src/exchange-lib/exchange_api_handle.c
index e89448d..22e64b9 100644
--- a/src/exchange-lib/exchange_api_handle.c
+++ b/src/exchange-lib/exchange_api_handle.c
@@ -33,12 +33,12 @@
  * Which revision of the Taler protocol is implemented
  * by this library?  Used to determine compatibility.
  */
-#define TALER_PROTOCOL_CURRENT 1
+#define TALER_PROTOCOL_CURRENT 2
 
 /**
  * How many revisions back are we compatible to?
  */
-#define TALER_PROTOCOL_AGE 1
+#define TALER_PROTOCOL_AGE 0
 
 
 /**
diff --git a/src/exchange-lib/exchange_api_refresh.c 
b/src/exchange-lib/exchange_api_refresh.c
index 305747f..1d0f406 100644
--- a/src/exchange-lib/exchange_api_refresh.c
+++ b/src/exchange-lib/exchange_api_refresh.c
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  Copyright (C) 2015, 2016 GNUnet e.V.
+  Copyright (C) 2015, 2016, 2017 Taler Systems SA
 
   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
@@ -101,7 +101,7 @@ struct MeltDataP
   /**
    * Hash over the melting session.
    */
-  struct GNUNET_HashCode melt_session_hash;
+  struct TALER_RefreshCommitmentP rc;
 
   /**
    * Number of coins we are melting, in NBO
@@ -180,9 +180,9 @@ struct MeltData
 {
 
   /**
-   * Hash over the melting session.
+   * Hash over the committed data during refresh operation.
    */
-  struct GNUNET_HashCode melt_session_hash;
+  struct TALER_RefreshCommitmentP rc;
 
   /**
    * Number of coins we are creating
@@ -466,7 +466,6 @@ deserialize_denomination_key (struct 
TALER_DenominationPublicKey *dk,
   dk->rsa_public_key
     = GNUNET_CRYPTO_rsa_public_key_decode (&buf[sizeof (uint32_t)],
                                            pbuf_size);
-
   if (NULL == dk->rsa_public_key)
   {
     GNUNET_break (0);
@@ -542,8 +541,6 @@ serialize_melt_data (const struct MeltData *md,
   size_t size;
   size_t asize;
   char *buf;
-  unsigned int i;
-  unsigned int j;
 
   size = 0;
   asize = (size_t) -1; /* make the compiler happy */
@@ -563,18 +560,18 @@ serialize_melt_data (const struct MeltData *md,
       asize = size; /* just for invariant check later */
       size = sizeof (struct MeltDataP);
       mdp = (struct MeltDataP *) buf;
-      mdp->melt_session_hash = md->melt_session_hash;
+      mdp->rc = md->rc;
       mdp->num_fresh_coins = htons (md->num_fresh_coins);
     }
     size += serialize_melted_coin (&md->melted_coin,
                                    buf,
                                    size);
-    for (i=0;i<md->num_fresh_coins;i++)
+    for (unsigned int i=0;i<md->num_fresh_coins;i++)
       size += serialize_denomination_key (&md->fresh_pks[i],
                                           buf,
                                           size);
-    for (i=0;i<TALER_CNC_KAPPA;i++)
-      for(j=0;j<md->num_fresh_coins;j++)
+    for (unsigned int i=0;i<TALER_CNC_KAPPA;i++)
+      for(unsigned int j=0;j<md->num_fresh_coins;j++)
         size += serialize_fresh_coin (&md->fresh_coins[i][j],
                                       buf,
                                       size);
@@ -607,7 +604,7 @@ deserialize_melt_data (const char *buf,
           buf,
           sizeof (struct MeltDataP));
   md = GNUNET_new (struct MeltData);
-  md->melt_session_hash = mdp.melt_session_hash;
+  md->rc = mdp.rc;
   md->num_fresh_coins = ntohs (mdp.num_fresh_coins);
   md->fresh_pks = GNUNET_new_array (md->num_fresh_coins,
                                     struct TALER_DenominationPublicKey);
@@ -700,34 +697,14 @@ TALER_EXCHANGE_refresh_prepare (const struct 
TALER_CoinSpendPrivateKeyP *melt_pr
 {
   struct MeltData md;
   char *buf;
-  struct GNUNET_HashContext *hash_context;
   struct TALER_Amount total;
   struct TALER_CoinSpendPublicKeyP coin_pub;
   struct TALER_TransferSecretP trans_sec[TALER_CNC_KAPPA];
+  struct TALER_RefreshCommitmentEntry rce[TALER_CNC_KAPPA];
 
   GNUNET_CRYPTO_eddsa_key_get_public (&melt_priv->eddsa_priv,
                                       &coin_pub.eddsa_pub);
-  hash_context = GNUNET_CRYPTO_hash_context_start ();
   /* build up melt data structure */
-  for (unsigned int i=0;i<TALER_CNC_KAPPA;i++)
-  {
-    struct GNUNET_CRYPTO_EcdhePrivateKey *tpk;
-    struct TALER_TransferPublicKeyP tp;
-
-    tpk = GNUNET_CRYPTO_ecdhe_key_create ();
-    md.melted_coin.transfer_priv[i].ecdhe_priv = *tpk;
-    GNUNET_free (tpk);
-
-    GNUNET_CRYPTO_ecdhe_key_get_public 
(&md.melted_coin.transfer_priv[i].ecdhe_priv,
-                                        &tp.ecdhe_pub);
-    GNUNET_CRYPTO_hash_context_read (hash_context,
-                                     &tp,
-                                     sizeof (struct TALER_TransferPublicKeyP));
-    /* DH */
-    TALER_link_derive_transfer_secret  (melt_priv,
-                                        &md.melted_coin.transfer_priv[i],
-                                        &trans_sec[i]);
-  }
   md.num_fresh_coins = fresh_pks_len;
   md.melted_coin.coin_priv = *melt_priv;
   md.melted_coin.melt_amount_with_fee = *melt_amount;
@@ -735,6 +712,9 @@ TALER_EXCHANGE_refresh_prepare (const struct 
TALER_CoinSpendPrivateKeyP *melt_pr
   md.melted_coin.original_value = melt_pk->value;
   md.melted_coin.expire_deposit
     = melt_pk->expire_deposit;
+  GNUNET_assert (GNUNET_OK ==
+                TALER_amount_get_zero (melt_amount->currency,
+                                       &total));
   md.melted_coin.pub_key.rsa_public_key
     = GNUNET_CRYPTO_rsa_public_key_dup (melt_pk->key.rsa_public_key);
   md.melted_coin.sig.rsa_signature
@@ -742,40 +722,24 @@ TALER_EXCHANGE_refresh_prepare (const struct 
TALER_CoinSpendPrivateKeyP *melt_pr
   md.fresh_pks = GNUNET_new_array (fresh_pks_len,
                                    struct TALER_DenominationPublicKey);
   for (unsigned int i=0;i<fresh_pks_len;i++)
+  {
     md.fresh_pks[i].rsa_public_key
       = GNUNET_CRYPTO_rsa_public_key_dup (fresh_pks[i].key.rsa_public_key);
-  for (unsigned int i=0;i<TALER_CNC_KAPPA;i++)
-  {
-    md.fresh_coins[i] = GNUNET_new_array (fresh_pks_len,
-                                          struct TALER_PlanchetSecretsP);
-    for (unsigned int j=0;j<fresh_pks_len;j++)
-    {
-      TALER_planchet_setup_refresh (&trans_sec[i],
-                                    j,
-                                    &md.fresh_coins[i][j]);
-    }
-  }
-
-  /* verify that melt_amount is above total cost */
-  GNUNET_assert (GNUNET_OK ==
-                TALER_amount_get_zero (melt_amount->currency,
-                                       &total));
-  for (unsigned int j=0;j<fresh_pks_len;j++)
-  {
     if ( (GNUNET_OK !=
          TALER_amount_add (&total,
                            &total,
-                           &fresh_pks[j].value)) ||
+                           &fresh_pks[i].value)) ||
         (GNUNET_OK !=
          TALER_amount_add (&total,
                            &total,
-                           &fresh_pks[j].fee_withdraw)) )
+                           &fresh_pks[i].fee_withdraw)) )
     {
       GNUNET_break (0);
       free_melt_data (&md);
       return NULL;
     }
   }
+  /* verify that melt_amount is above total cost */
   if (1 ==
       TALER_amount_cmp (&total,
                        melt_amount) )
@@ -787,63 +751,64 @@ TALER_EXCHANGE_refresh_prepare (const struct 
TALER_CoinSpendPrivateKeyP *melt_pr
     return NULL;
   }
 
-  /* next, add all of the hashes from the denomination keys to the
-     hash_context */
-  for (unsigned int i=0;i<fresh_pks_len;i++)
-  {
-    char *buf;
-    size_t buf_size;
-
-    buf_size = GNUNET_CRYPTO_rsa_public_key_encode 
(fresh_pks[i].key.rsa_public_key,
-                                                    &buf);
-    GNUNET_CRYPTO_hash_context_read (hash_context,
-                                     buf,
-                                     buf_size);
-    GNUNET_free (buf);
-  }
+  /* build up coins */
+  for (unsigned int i=0;i<TALER_CNC_KAPPA;i++)
   {
-    struct TALER_AmountNBO melt_amountn;
+    struct GNUNET_CRYPTO_EcdhePrivateKey *tpk;
 
-    GNUNET_CRYPTO_hash_context_read (hash_context,
-                                     &coin_pub,
-                                     sizeof (struct 
TALER_CoinSpendPublicKeyP));
-    TALER_amount_hton (&melt_amountn,
-                       melt_amount);
-    GNUNET_CRYPTO_hash_context_read (hash_context,
-                                     &melt_amountn,
-                                     sizeof (struct TALER_AmountNBO));
+    tpk = GNUNET_CRYPTO_ecdhe_key_create ();
+    md.melted_coin.transfer_priv[i].ecdhe_priv = *tpk;
+    GNUNET_free (tpk);
 
-  }
-  for (unsigned int i = 0; i < TALER_CNC_KAPPA; i++)
-  {
-    for (unsigned int j = 0; j < fresh_pks_len; j++)
+    GNUNET_CRYPTO_ecdhe_key_get_public 
(&md.melted_coin.transfer_priv[i].ecdhe_priv,
+                                        &rce[i].transfer_pub.ecdhe_pub);
+    TALER_link_derive_transfer_secret  (melt_priv,
+                                        &md.melted_coin.transfer_priv[i],
+                                        &trans_sec[i]);
+    md.fresh_coins[i] = GNUNET_new_array (fresh_pks_len,
+                                          struct TALER_PlanchetSecretsP);
+    rce[i].new_coins = GNUNET_new_array (fresh_pks_len,
+                                         struct TALER_RefreshCoinData);
+    for (unsigned int j=0;j<fresh_pks_len;j++)
     {
-      const struct TALER_PlanchetSecretsP *fc; /* coin this is about */
+      struct TALER_PlanchetSecretsP *fc = &md.fresh_coins[i][j];
+      struct TALER_RefreshCoinData *rcd = &rce[i].new_coins[j];
       struct TALER_PlanchetDetail pd;
 
-      fc = &md.fresh_coins[i][j];
+      TALER_planchet_setup_refresh (&trans_sec[i],
+                                    j,
+                                    fc);
       if (GNUNET_OK !=
           TALER_planchet_prepare (&md.fresh_pks[j],
                                   fc,
                                   &pd))
       {
         GNUNET_break_op (0);
-        GNUNET_CRYPTO_hash_context_abort (hash_context);
         free_melt_data (&md);
         return NULL;
       }
-      GNUNET_CRYPTO_hash_context_read (hash_context,
-                                       pd.coin_ev,
-                                       pd.coin_ev_size);
-      GNUNET_free (pd.coin_ev);
+      rcd->dk = &md.fresh_pks[j];
+      rcd->coin_ev = pd.coin_ev;
+      rcd->coin_ev_size = pd.coin_ev_size;
     }
   }
-  GNUNET_CRYPTO_hash_context_finish (hash_context,
-                                     &md.melt_session_hash);
 
+  /* Compute refresh commitment */
+  TALER_refresh_get_commitment (&md.rc,
+                                TALER_CNC_KAPPA,
+                                fresh_pks_len,
+                                rce,
+                                &coin_pub,
+                                melt_amount);
   /* finally, serialize everything */
   buf = serialize_melt_data (&md,
                              res_size);
+  for (unsigned int i = 0; i < TALER_CNC_KAPPA; i++)
+  {
+    for (unsigned int j = 0; j < fresh_pks_len; j++)
+      GNUNET_free_non_null (rce[i].new_coins[j].coin_ev);
+    GNUNET_free_non_null (rce[i].new_coins);
+  }
   free_melt_data (&md);
   return buf;
 }
@@ -909,14 +874,14 @@ static int
 verify_refresh_melt_signature_ok (struct TALER_EXCHANGE_RefreshMeltHandle *rmh,
                                   const json_t *json,
                                   struct TALER_ExchangePublicKeyP 
*exchange_pub,
-                                  uint16_t *noreveal_index)
+                                  uint32_t *noreveal_index)
 {
   struct TALER_ExchangeSignatureP exchange_sig;
   const struct TALER_EXCHANGE_Keys *key_state;
   struct GNUNET_JSON_Specification spec[] = {
     GNUNET_JSON_spec_fixed_auto ("exchange_sig", &exchange_sig),
     GNUNET_JSON_spec_fixed_auto ("exchange_pub", exchange_pub),
-    GNUNET_JSON_spec_uint16 ("noreveal_index", noreveal_index),
+    GNUNET_JSON_spec_uint32 ("noreveal_index", noreveal_index),
     GNUNET_JSON_spec_end()
   };
   struct TALER_RefreshMeltConfirmationPS confirm;
@@ -950,9 +915,8 @@ verify_refresh_melt_signature_ok (struct 
TALER_EXCHANGE_RefreshMeltHandle *rmh,
   /* verify signature by exchange */
   confirm.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_MELT);
   confirm.purpose.size = htonl (sizeof (struct 
TALER_RefreshMeltConfirmationPS));
-  confirm.session_hash = rmh->md->melt_session_hash;
-  confirm.noreveal_index = htons (*noreveal_index);
-  confirm.reserved = htons (0);
+  confirm.rc = rmh->md->rc;
+  confirm.noreveal_index = htonl (*noreveal_index);
   if (GNUNET_OK !=
       GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_EXCHANGE_CONFIRM_MELT,
                                   &confirm.purpose,
@@ -1076,7 +1040,7 @@ handle_refresh_melt_finished (void *cls,
                               const json_t *json)
 {
   struct TALER_EXCHANGE_RefreshMeltHandle *rmh = cls;
-  uint16_t noreveal_index = TALER_CNC_KAPPA; /* invalid value */
+  uint32_t noreveal_index = TALER_CNC_KAPPA; /* invalid value */
   struct TALER_ExchangePublicKeyP exchange_pub;
 
   rmh->job = NULL;
@@ -1145,7 +1109,7 @@ handle_refresh_melt_finished (void *cls,
     rmh->melt_cb (rmh->melt_cb_cls,
                   response_code,
                  TALER_JSON_get_error_code (json),
-                  UINT16_MAX,
+                  UINT32_MAX,
                   NULL,
                   json);
   TALER_EXCHANGE_refresh_melt_cancel (rmh);
@@ -1153,46 +1117,6 @@ handle_refresh_melt_finished (void *cls,
 
 
 /**
- * Convert a coin to be melted to the respective JSON encoding.
- *
- * @param melt_session_hash session hash to use
- * @param mc coin to be melted
- * @return JSON encoding of the melting request
- */
-static json_t *
-melted_coin_to_json (const struct GNUNET_HashCode *melt_session_hash,
-                     const struct MeltedCoin *mc)
-{
-  struct TALER_CoinSpendSignatureP confirm_sig;
-  struct TALER_RefreshMeltCoinAffirmationPS melt;
-
-  melt.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_MELT);
-  melt.purpose.size = htonl (sizeof (struct 
TALER_RefreshMeltCoinAffirmationPS));
-  melt.session_hash = *melt_session_hash;
-  TALER_amount_hton (&melt.amount_with_fee,
-                     &mc->melt_amount_with_fee);
-  TALER_amount_hton (&melt.melt_fee,
-                     &mc->fee_melt);
-  GNUNET_CRYPTO_eddsa_key_get_public (&mc->coin_priv.eddsa_priv,
-                                      &melt.coin_pub.eddsa_pub);
-  GNUNET_CRYPTO_eddsa_sign (&mc->coin_priv.eddsa_priv,
-                            &melt.purpose,
-                            &confirm_sig.eddsa_signature);
-  return json_pack ("{s:o, s:o, s:o, s:o, s:o}",
-                    "coin_pub",
-                    GNUNET_JSON_from_data_auto (&melt.coin_pub),
-                    "denom_pub",
-                    GNUNET_JSON_from_rsa_public_key 
(mc->pub_key.rsa_public_key),
-                    "denom_sig",
-                    GNUNET_JSON_from_rsa_signature (mc->sig.rsa_signature),
-                    "confirm_sig",
-                    GNUNET_JSON_from_data_auto (&confirm_sig),
-                    "value_with_fee",
-                    TALER_JSON_from_amount (&mc->melt_amount_with_fee));
-}
-
-
-/**
  * Submit a melt request to the exchange and get the exchange's
  * response.
  *
@@ -1220,17 +1144,12 @@ TALER_EXCHANGE_refresh_melt (struct 
TALER_EXCHANGE_Handle *exchange,
                              void *melt_cb_cls)
 {
   json_t *melt_obj;
-  json_t *new_denoms;
-  json_t *melt_coin;
-  json_t *coin_evs;
-  json_t *transfer_pubs;
-  json_t *tmp;
   struct TALER_EXCHANGE_RefreshMeltHandle *rmh;
   CURL *eh;
   struct GNUNET_CURL_Context *ctx;
   struct MeltData *md;
-  unsigned int i;
-  unsigned int j;
+  struct TALER_CoinSpendSignatureP confirm_sig;
+  struct TALER_RefreshMeltCoinAffirmationPS melt;
 
   GNUNET_assert (GNUNET_YES ==
                 MAH_handle_is_ready (exchange));
@@ -1242,78 +1161,35 @@ TALER_EXCHANGE_refresh_melt (struct 
TALER_EXCHANGE_Handle *exchange,
     return NULL;
   }
 
-  /* build JSON request, each of the 4 arrays first */
-  new_denoms = json_array ();
-  melt_coin = melted_coin_to_json (&md->melt_session_hash,
-                                   &md->melted_coin);
-  coin_evs = json_array ();
-  transfer_pubs = json_array ();
-
-  /* now transfer_pubs */
-  for (j=0;j<TALER_CNC_KAPPA;j++)
-  {
-    const struct MeltedCoin *mc = &md->melted_coin;
-    struct TALER_TransferPublicKeyP transfer_pub;
-
-    GNUNET_CRYPTO_ecdhe_key_get_public (&mc->transfer_priv[j].ecdhe_priv,
-                                        &transfer_pub.ecdhe_pub);
-    GNUNET_assert (0 ==
-                   json_array_append_new (transfer_pubs,
-                                          GNUNET_JSON_from_data_auto 
(&transfer_pub)));
-  }
-
-  /* now new_denoms */
-  for (i=0;i<md->num_fresh_coins;i++)
-  {
-    GNUNET_assert (0 ==
-                   json_array_append_new (new_denoms,
-                                          GNUNET_JSON_from_rsa_public_key
-                                          (md->fresh_pks[i].rsa_public_key)));
-  }
-
-  /* now coin_evs */
-  for (j=0;j<TALER_CNC_KAPPA;j++)
-  {
-    tmp = json_array ();
-    for (i=0;i<md->num_fresh_coins;i++)
-    {
-      const struct TALER_PlanchetSecretsP *fc = &md->fresh_coins[j][i];
-      struct TALER_PlanchetDetail pd;
-
-      if (GNUNET_OK !=
-          TALER_planchet_prepare (&md->fresh_pks[i],
-                                  fc,
-                                  &pd))
-      {
-        /* This should have been noticed during the preparation stage. */
-        GNUNET_break (0);
-        json_decref (new_denoms);
-        json_decref (tmp);
-        json_decref (coin_evs);
-        json_decref (melt_coin);
-        json_decref (transfer_pubs);
-        return NULL;
-      }
-      GNUNET_assert (0 ==
-                     json_array_append_new (tmp,
-                                            GNUNET_JSON_from_data (pd.coin_ev,
-                                                                   
pd.coin_ev_size)));
-      GNUNET_free (pd.coin_ev);
-    }
-    GNUNET_assert (0 ==
-                   json_array_append_new (coin_evs,
-                                          tmp));
-  }
-
-  /* finally, assemble main JSON request from constitutent arrays */
-  melt_obj = json_pack ("{s:o, s:o, s:o, s:o}",
-                        "new_denoms", new_denoms,
-                        "melt_coin", melt_coin,
-                        "coin_evs", coin_evs,
-                        "transfer_pubs", transfer_pubs);
+  melt.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_MELT);
+  melt.purpose.size = htonl (sizeof (struct 
TALER_RefreshMeltCoinAffirmationPS));
+  melt.rc = md->rc;
+  TALER_amount_hton (&melt.amount_with_fee,
+                     &md->melted_coin.melt_amount_with_fee);
+  TALER_amount_hton (&melt.melt_fee,
+                     &md->melted_coin.fee_melt);
+  GNUNET_CRYPTO_eddsa_key_get_public (&md->melted_coin.coin_priv.eddsa_priv,
+                                      &melt.coin_pub.eddsa_pub);
+  GNUNET_CRYPTO_eddsa_sign (&md->melted_coin.coin_priv.eddsa_priv,
+                            &melt.purpose,
+                            &confirm_sig.eddsa_signature);
+  melt_obj = json_pack ("{s:o, s:o, s:o, s:o, s:o, s:o}",
+                        "coin_pub",
+                        GNUNET_JSON_from_data_auto (&melt.coin_pub),
+                        "denom_pub",
+                        GNUNET_JSON_from_rsa_public_key 
(md->melted_coin.pub_key.rsa_public_key),
+                        "denom_sig",
+                        GNUNET_JSON_from_rsa_signature 
(md->melted_coin.sig.rsa_signature),
+                        "confirm_sig",
+                        GNUNET_JSON_from_data_auto (&confirm_sig),
+                        "value_with_fee",
+                        TALER_JSON_from_amount 
(&md->melted_coin.melt_amount_with_fee),
+                        "rc",
+                        GNUNET_JSON_from_data_auto (&melt.rc));
   if (NULL == melt_obj)
   {
     GNUNET_break (0);
+    free_melt_data (md);
     return NULL;
   }
 
@@ -1325,7 +1201,6 @@ TALER_EXCHANGE_refresh_melt (struct TALER_EXCHANGE_Handle 
*exchange,
   rmh->md = md;
   rmh->url = MAH_path_to_url (exchange,
                               "/refresh/melt");
-
   eh = curl_easy_init ();
   GNUNET_assert (NULL != (rmh->json_enc =
                           json_dumps (melt_obj,
@@ -1449,7 +1324,6 @@ refresh_reveal_ok (struct 
TALER_EXCHANGE_RefreshRevealHandle *rrh,
                    struct TALER_CoinSpendPrivateKeyP *coin_privs,
                    struct TALER_DenominationSignature *sigs)
 {
-  unsigned int i;
   json_t *jsona;
   struct GNUNET_JSON_Specification outer_spec[] = {
     GNUNET_JSON_spec_json ("ev_sigs", &jsona),
@@ -1478,7 +1352,7 @@ refresh_reveal_ok (struct 
TALER_EXCHANGE_RefreshRevealHandle *rrh,
     GNUNET_JSON_parse_free (outer_spec);
     return GNUNET_SYSERR;
   }
-  for (i=0;i<rrh->md->num_fresh_coins;i++)
+  for (unsigned int i=0;i<rrh->md->num_fresh_coins;i++)
   {
     const struct TALER_PlanchetSecretsP *fc;
     struct TALER_DenominationPublicKey *pk;
@@ -1559,7 +1433,6 @@ handle_refresh_reveal_finished (void *cls,
     {
       struct TALER_CoinSpendPrivateKeyP coin_privs[rrh->md->num_fresh_coins];
       struct TALER_DenominationSignature sigs[rrh->md->num_fresh_coins];
-      unsigned int i;
       int ret;
 
       memset (sigs, 0, sizeof (sigs));
@@ -1582,7 +1455,7 @@ handle_refresh_reveal_finished (void *cls,
                         json);
         rrh->reveal_cb = NULL;
       }
-      for (i=0;i<rrh->md->num_fresh_coins;i++)
+      for (unsigned int i=0;i<rrh->md->num_fresh_coins;i++)
         if (NULL != sigs[i].rsa_signature)
           GNUNET_CRYPTO_rsa_signature_free (sigs[i].rsa_signature);
     }
@@ -1647,17 +1520,19 @@ struct TALER_EXCHANGE_RefreshRevealHandle *
 TALER_EXCHANGE_refresh_reveal (struct TALER_EXCHANGE_Handle *exchange,
                               size_t refresh_data_length,
                               const char *refresh_data,
-                              uint16_t noreveal_index,
+                              uint32_t noreveal_index,
                               TALER_EXCHANGE_RefreshRevealCallback reveal_cb,
                               void *reveal_cb_cls)
 {
   struct TALER_EXCHANGE_RefreshRevealHandle *rrh;
   json_t *transfer_privs;
+  json_t *new_denoms_h;
+  json_t *coin_evs;
   json_t *reveal_obj;
   CURL *eh;
   struct GNUNET_CURL_Context *ctx;
   struct MeltData *md;
-  unsigned int j;
+  struct TALER_TransferPublicKeyP transfer_pub;
 
   GNUNET_assert (GNUNET_YES ==
                 MAH_handle_is_ready (exchange));
@@ -1678,9 +1553,45 @@ TALER_EXCHANGE_refresh_reveal (struct 
TALER_EXCHANGE_Handle *exchange,
     return NULL;
   }
 
+  /* now transfer_pub */
+  GNUNET_CRYPTO_ecdhe_key_get_public 
(&md->melted_coin.transfer_priv[noreveal_index].ecdhe_priv,
+                                      &transfer_pub.ecdhe_pub);
+
+  /* now new_denoms */
+  GNUNET_assert (NULL != (new_denoms_h = json_array ()));
+  GNUNET_assert (NULL != (coin_evs = json_array ()));
+  for (unsigned int i=0;i<md->num_fresh_coins;i++)
+  {
+    struct GNUNET_HashCode denom_hash;
+    struct TALER_PlanchetDetail pd;
+
+    GNUNET_CRYPTO_rsa_public_key_hash (md->fresh_pks[i].rsa_public_key,
+                                       &denom_hash);
+    GNUNET_assert (0 ==
+                   json_array_append_new (new_denoms_h,
+                                          GNUNET_JSON_from_data_auto 
(&denom_hash)));
+
+    if (GNUNET_OK !=
+        TALER_planchet_prepare (&md->fresh_pks[i],
+                                &md->fresh_coins[noreveal_index][i],
+                                &pd))
+    {
+      /* This should have been noticed during the preparation stage. */
+      GNUNET_break (0);
+      json_decref (new_denoms_h);
+      json_decref (coin_evs);
+      return NULL;
+    }
+    GNUNET_assert (0 ==
+                   json_array_append_new (coin_evs,
+                                          GNUNET_JSON_from_data (pd.coin_ev,
+                                                                 
pd.coin_ev_size)));
+    GNUNET_free (pd.coin_ev);
+  }
+
   /* build array of transfer private keys */
-  transfer_privs = json_array ();
-  for (j=0;j<TALER_CNC_KAPPA;j++)
+  GNUNET_assert (NULL != (transfer_privs = json_array ()));
+  for (unsigned int j=0;j<TALER_CNC_KAPPA;j++)
   {
     if (j == noreveal_index)
     {
@@ -1694,11 +1605,17 @@ TALER_EXCHANGE_refresh_reveal (struct 
TALER_EXCHANGE_Handle *exchange,
   }
 
   /* build main JSON request */
-  reveal_obj = json_pack ("{s:o, s:o}",
-                          "session_hash",
-                          GNUNET_JSON_from_data_auto (&md->melt_session_hash),
+  reveal_obj = json_pack ("{s:o, s:o, s:o, s:o, s:o}",
+                          "rc",
+                          GNUNET_JSON_from_data_auto (&md->rc),
+                          "transfer_pub",
+                          GNUNET_JSON_from_data_auto (&transfer_pub),
                           "transfer_privs",
-                          transfer_privs);
+                          transfer_privs,
+                          "new_denoms_h",
+                          new_denoms_h,
+                          "coin_evs",
+                          coin_evs);
   if (NULL == reveal_obj)
   {
     GNUNET_break (0);
diff --git a/src/exchange-lib/test_exchange_api.c 
b/src/exchange-lib/test_exchange_api.c
index 7c0dfa9..475fbde 100644
--- a/src/exchange-lib/test_exchange_api.c
+++ b/src/exchange-lib/test_exchange_api.c
@@ -26,6 +26,7 @@
 #include "taler_json_lib.h"
 #include <gnunet/gnunet_util_lib.h>
 #include <microhttpd.h>
+#include "taler_bank_service.h"
 #include "taler_fakebank_lib.h"
 
 
@@ -40,6 +41,11 @@
 #define WIRE_SEPA 1
 
 /**
+ * Account number of the exchange at the bank.
+ */
+#define EXCHANGE_ACCOUNT_NO 2
+
+/**
  * Main execution context for the main loop.
  */
 static struct GNUNET_CURL_Context *ctx;
@@ -142,6 +148,11 @@ enum OpCode
   OC_RUN_AGGREGATOR,
 
   /**
+   * Run the wirewatcher to check for incoming transactions.
+   */
+  OC_RUN_WIREWATCH,
+
+  /**
    * Check that the fakebank has received a certain transaction.
    */
   OC_CHECK_BANK_TRANSFER,
@@ -263,14 +274,31 @@ struct Command
       const char *amount;
 
       /**
-       * Sender account details (JSON).
+       * Wire transfer subject. NULL to use public key corresponding
+       * to @e reserve_priv or @e reserve_reference.  Should only be
+       * set manually to test invalid wire transfer subjects.
+       */
+      const char *subject;
+
+      /**
+       * Sender (debit) account number.
+       */
+      uint64_t debit_account_no;
+
+      /**
+       * Receiver (credit) account number.
+       */
+      uint64_t credit_account_no;
+
+      /**
+       * Username to use for authentication.
        */
-      const char *sender_details;
+      const char *auth_username;
 
       /**
-       * Transfer information identifier (JSON).
+       * Password to use for authentication.
        */
-      const char *transfer_details;
+      const char *auth_password;
 
       /**
        * Set (by the interpreter) to the reserve's private key
@@ -281,7 +309,12 @@ struct Command
       /**
        * Set to the API's handle during the operation.
        */
-      struct TALER_EXCHANGE_AdminAddIncomingHandle *aih;
+      struct TALER_BANK_AdminAddIncomingHandle *aih;
+
+      /**
+       * Set to the wire transfer's unique ID.
+       */
+      uint64_t serial_id;
 
     } admin_add_incoming;
 
@@ -611,6 +644,20 @@ struct Command
     struct {
 
       /**
+       * Process for the wirewatcher.
+       */
+      struct GNUNET_OS_Process *wirewatch_proc;
+
+      /**
+       * ID of task called whenever we get a SIGCHILD.
+       */
+      struct GNUNET_SCHEDULER_Task *child_death_task;
+
+    } run_wirewatch;
+
+    struct {
+
+      /**
        * Which amount do we expect to see transferred?
        */
       const char *amount;
@@ -677,7 +724,7 @@ struct Command
       const char *ref;
 
       /**
-       * Process for the aggregator.
+       * Process for the revocation process.
        */
       struct GNUNET_OS_Process *revoke_proc;
 
@@ -825,18 +872,21 @@ next_command (struct InterpreterState *is)
  * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful 
status request
  *                    0 if the exchange's reply is bogus (fails to follow the 
protocol)
  * @param ec taler-specific error code, #TALER_EC_NONE on success
+ * @param serial_id unique ID of the wire transfer
  * @param full_response full response from the exchange (for logging, in case 
of errors)
  */
 static void
 add_incoming_cb (void *cls,
                  unsigned int http_status,
                 enum TALER_ErrorCode ec,
+                 uint64_t serial_id,
                  const json_t *full_response)
 {
   struct InterpreterState *is = cls;
   struct Command *cmd = &is->commands[is->ip];
 
   cmd->details.admin_add_incoming.aih = NULL;
+  cmd->details.admin_add_incoming.serial_id = serial_id;
   if (MHD_HTTP_OK != http_status)
   {
     GNUNET_break (0);
@@ -1199,7 +1249,7 @@ static void
 melt_cb (void *cls,
          unsigned int http_status,
         enum TALER_ErrorCode ec,
-         uint16_t noreveal_index,
+         uint32_t noreveal_index,
          const struct TALER_ExchangePublicKeyP *exchange_pub,
          const json_t *full_response)
 {
@@ -1312,8 +1362,6 @@ link_cb (void *cls,
   struct InterpreterState *is = cls;
   struct Command *cmd = &is->commands[is->ip];
   const struct Command *ref;
-  unsigned int i;
-  unsigned int j;
   unsigned int found;
 
   cmd->details.refresh_link.rlh = NULL;
@@ -1341,16 +1389,16 @@ link_cb (void *cls,
       return;
     }
     /* check that the coins match */
-    for (i=0;i<num_coins;i++)
-      for (j=i+1;j<num_coins;j++)
+    for (unsigned int i=0;i<num_coins;i++)
+      for (unsigned int j=i+1;j<num_coins;j++)
        if (0 == memcmp (&coin_privs[i],
                         &coin_privs[j],
                         sizeof (struct TALER_CoinSpendPrivateKeyP)))
          GNUNET_break (0);
     /* Note: coins might be legitimately permutated in here... */
     found = 0;
-    for (i=0;i<num_coins;i++)
-      for (j=0;j<num_coins;j++)
+    for (unsigned int i=0;i<num_coins;i++)
+      for (unsigned int j=0;j<num_coins;j++)
       {
        const struct FreshCoin *fc;
 
@@ -1408,6 +1456,14 @@ maint_child_death (void *cls)
     GNUNET_OS_process_destroy (cmd->details.run_aggregator.aggregator_proc);
     cmd->details.run_aggregator.aggregator_proc = NULL;
     break;
+  case OC_RUN_WIREWATCH:
+    cmd->details.run_wirewatch.child_death_task = NULL;
+    pr = GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_READ);
+    GNUNET_break (0 < GNUNET_DISK_file_read (pr, &c, sizeof (c)));
+    GNUNET_OS_process_wait (cmd->details.run_wirewatch.wirewatch_proc);
+    GNUNET_OS_process_destroy (cmd->details.run_wirewatch.wirewatch_proc);
+    cmd->details.run_wirewatch.wirewatch_proc = NULL;
+    break;
   case OC_REVOKE:
     cmd->details.revoke.child_death_task = NULL;
     pr = GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_READ);
@@ -2012,9 +2068,6 @@ interpreter_run (void *cls)
   const struct Command *ref;
   struct TALER_ReservePublicKeyP reserve_pub;
   struct TALER_Amount amount;
-  struct GNUNET_TIME_Absolute execution_date;
-  json_t *sender_details;
-  json_t *transfer_details;
   const struct GNUNET_SCHEDULER_TaskContext *tc;
 
   is->task = NULL;
@@ -2036,80 +2089,73 @@ interpreter_run (void *cls)
     GNUNET_SCHEDULER_shutdown ();
     return;
   case OC_ADMIN_ADD_INCOMING:
-    if (NULL !=
-        cmd->details.admin_add_incoming.reserve_reference)
-    {
-      ref = find_command (is,
-                          cmd->details.admin_add_incoming.reserve_reference);
-      GNUNET_assert (NULL != ref);
-      GNUNET_assert (OC_ADMIN_ADD_INCOMING == ref->oc);
-      cmd->details.admin_add_incoming.reserve_priv
-        = ref->details.admin_add_incoming.reserve_priv;
-    }
-    else
     {
-      struct GNUNET_CRYPTO_EddsaPrivateKey *priv;
+      char *subject;
+      struct TALER_BANK_AuthenticationData auth;
 
-      priv = GNUNET_CRYPTO_eddsa_key_create ();
-      cmd->details.admin_add_incoming.reserve_priv.eddsa_priv = *priv;
-      GNUNET_free (priv);
-    }
-    GNUNET_CRYPTO_eddsa_key_get_public 
(&cmd->details.admin_add_incoming.reserve_priv.eddsa_priv,
-                                        &reserve_pub.eddsa_pub);
-    if (GNUNET_OK !=
-        TALER_string_to_amount (cmd->details.admin_add_incoming.amount,
-                                &amount))
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                  "Failed to parse amount `%s' at %u\n",
-                  cmd->details.admin_add_incoming.amount,
-                  is->ip);
-      fail (is);
-      return;
-    }
-    sender_details = json_loads 
(cmd->details.admin_add_incoming.sender_details,
-                                 JSON_REJECT_DUPLICATES,
-                                 NULL);
-    if (NULL == sender_details)
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                  "Failed to parse sender details `%s' at %u\n",
-                  cmd->details.admin_add_incoming.sender_details,
-                  is->ip);
-      fail (is);
-      return;
-    }
-    transfer_details = json_loads 
(cmd->details.admin_add_incoming.transfer_details,
-                                   JSON_REJECT_DUPLICATES,
-                                   NULL);
-    if (NULL == transfer_details)
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                  "Failed to parse transfer details `%s' at %u\n",
-                  cmd->details.admin_add_incoming.transfer_details,
-                  is->ip);
-      fail (is);
-      return;
-    }
-    execution_date = GNUNET_TIME_absolute_get ();
-    GNUNET_TIME_round_abs (&execution_date);
-    cmd->details.admin_add_incoming.aih
-      = TALER_EXCHANGE_admin_add_incoming (exchange,
-                                           "http://localhost:18080/";,
-                                           &reserve_pub,
-                                           &amount,
-                                           execution_date,
-                                           sender_details,
-                                           transfer_details,
-                                           &add_incoming_cb,
-                                           is);
-    json_decref (sender_details);
-    json_decref (transfer_details);
-    if (NULL == cmd->details.admin_add_incoming.aih)
-    {
-      GNUNET_break (0);
-      fail (is);
-      return;
+      if (NULL !=
+          cmd->details.admin_add_incoming.subject)
+      {
+        subject = GNUNET_strdup (cmd->details.admin_add_incoming.subject);
+      }
+      else
+      {
+        /* Use reserve public key as subject */
+        if (NULL !=
+            cmd->details.admin_add_incoming.reserve_reference)
+        {
+          ref = find_command (is,
+                              
cmd->details.admin_add_incoming.reserve_reference);
+          GNUNET_assert (NULL != ref);
+          GNUNET_assert (OC_ADMIN_ADD_INCOMING == ref->oc);
+          cmd->details.admin_add_incoming.reserve_priv
+            = ref->details.admin_add_incoming.reserve_priv;
+        }
+        else
+        {
+          struct GNUNET_CRYPTO_EddsaPrivateKey *priv;
+
+          priv = GNUNET_CRYPTO_eddsa_key_create ();
+          cmd->details.admin_add_incoming.reserve_priv.eddsa_priv = *priv;
+          GNUNET_free (priv);
+        }
+        GNUNET_CRYPTO_eddsa_key_get_public 
(&cmd->details.admin_add_incoming.reserve_priv.eddsa_priv,
+                                            &reserve_pub.eddsa_pub);
+        subject = GNUNET_STRINGS_data_to_string_alloc (&reserve_pub,
+                                                       sizeof (reserve_pub));
+      }
+      if (GNUNET_OK !=
+          TALER_string_to_amount (cmd->details.admin_add_incoming.amount,
+                                  &amount))
+      {
+        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                    "Failed to parse amount `%s' at %u\n",
+                    cmd->details.admin_add_incoming.amount,
+                    is->ip);
+        fail (is);
+        return;
+      }
+      auth.method = TALER_BANK_AUTH_BASIC;
+      auth.details.basic.username = (char *) 
cmd->details.admin_add_incoming.auth_username;
+      auth.details.basic.password = (char *) 
cmd->details.admin_add_incoming.auth_password;
+      cmd->details.admin_add_incoming.aih
+        = TALER_BANK_admin_add_incoming (ctx,
+                                         "http://localhost:8082/";, /* bank URL 
*/
+                                         &auth,
+                                         "https://exchange.com/";, /* exchange 
URL */
+                                         subject,
+                                         &amount,
+                                         
cmd->details.admin_add_incoming.debit_account_no,
+                                         
cmd->details.admin_add_incoming.credit_account_no,
+                                         &add_incoming_cb,
+                                         is);
+      GNUNET_free (subject);
+      if (NULL == cmd->details.admin_add_incoming.aih)
+      {
+        GNUNET_break (0);
+        fail (is);
+        return;
+      }
     }
     return;
   case OC_WITHDRAW_STATUS:
@@ -2384,9 +2430,18 @@ interpreter_run (void *cls)
             fail (is);
             return;
           }
-          cmd->details.refresh_melt.fresh_pks[i]
-            = find_pk (is->keys,
-                       &amount);
+          if (NULL ==
+              (cmd->details.refresh_melt.fresh_pks[i]
+               = find_pk (is->keys,
+                          &amount)))
+          {
+            GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                        "Failed to find denomination key for amount `%s' at 
%u\n",
+                        cmd->details.reserve_withdraw.amount,
+                        is->ip);
+            fail (is);
+            return;
+          }
           fresh_pks[i] = *cmd->details.refresh_melt.fresh_pks[i];
         }
         cmd->details.refresh_melt.refresh_data
@@ -2567,13 +2622,42 @@ interpreter_run (void *cls)
         fail (is);
         return;
       }
-      pr = GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_READ);
+      pr = GNUNET_DISK_pipe_handle (sigpipe,
+                                    GNUNET_DISK_PIPE_END_READ);
       cmd->details.run_aggregator.child_death_task
         = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
                                           pr,
                                           &maint_child_death, is);
       return;
     }
+  case OC_RUN_WIREWATCH:
+    {
+      const struct GNUNET_DISK_FileHandle *pr;
+
+      cmd->details.run_wirewatch.wirewatch_proc
+        = GNUNET_OS_start_process (GNUNET_NO,
+                                   GNUNET_OS_INHERIT_STD_ALL,
+                                   NULL, NULL, NULL,
+                                   "taler-exchange-wirewatch",
+                                   "taler-exchange-wirewatch",
+                                   "-c", "test_exchange_api.conf",
+                                   "-t", "test", /* use Taler's bank/fakebank 
*/
+                                   "-T", /* exit when done */
+                                   NULL);
+      if (NULL == cmd->details.run_wirewatch.wirewatch_proc)
+      {
+        GNUNET_break (0);
+        fail (is);
+        return;
+      }
+      pr = GNUNET_DISK_pipe_handle (sigpipe,
+                                    GNUNET_DISK_PIPE_END_READ);
+      cmd->details.run_wirewatch.child_death_task
+        = GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL,
+                                          pr,
+                                          &maint_child_death, is);
+      return;
+    }
   case OC_CHECK_BANK_TRANSFER:
     {
       if (GNUNET_OK !=
@@ -2806,7 +2890,7 @@ do_shutdown (void *cls)
                     "Command %u (%s) did not complete\n",
                     i,
                     cmd->label);
-        TALER_EXCHANGE_admin_add_incoming_cancel 
(cmd->details.admin_add_incoming.aih);
+        TALER_BANK_admin_add_incoming_cancel 
(cmd->details.admin_add_incoming.aih);
         cmd->details.admin_add_incoming.aih = NULL;
       }
       break;
@@ -2946,6 +3030,22 @@ do_shutdown (void *cls)
         cmd->details.run_aggregator.child_death_task = NULL;
       }
       break;
+    case OC_RUN_WIREWATCH:
+      if (NULL != cmd->details.run_wirewatch.wirewatch_proc)
+      {
+        GNUNET_break (0 ==
+                      GNUNET_OS_process_kill 
(cmd->details.run_wirewatch.wirewatch_proc,
+                                              SIGKILL));
+        GNUNET_OS_process_wait (cmd->details.run_wirewatch.wirewatch_proc);
+        GNUNET_OS_process_destroy (cmd->details.run_wirewatch.wirewatch_proc);
+        cmd->details.run_wirewatch.wirewatch_proc = NULL;
+      }
+      if (NULL != cmd->details.run_wirewatch.child_death_task)
+      {
+        GNUNET_SCHEDULER_cancel (cmd->details.run_wirewatch.child_death_task);
+        cmd->details.run_wirewatch.child_death_task = NULL;
+      }
+      break;
     case OC_CHECK_BANK_TRANSFER:
       GNUNET_free_non_null (cmd->details.check_bank_transfer.subject);
       cmd->details.check_bank_transfer.subject = NULL;
@@ -3127,9 +3227,14 @@ run (void *cls)
     { .oc = OC_ADMIN_ADD_INCOMING,
       .label = "create-reserve-1",
       .expected_response_code = MHD_HTTP_OK,
-      .details.admin_add_incoming.sender_details = "{ \"type\":\"test\", 
\"bank_uri\":\"http://localhost:8082/\";, \"account_number\":42}",
-      .details.admin_add_incoming.transfer_details = "{ \"uuid\":1  }",
+      .details.admin_add_incoming.debit_account_no = 42,
+      .details.admin_add_incoming.credit_account_no = EXCHANGE_ACCOUNT_NO,
+      .details.admin_add_incoming.auth_username = "user42",
+      .details.admin_add_incoming.auth_password = "pass42",
       .details.admin_add_incoming.amount = "EUR:5.01" },
+    /* Run wirewatch to observe /admin/add/incoming */
+    { .oc = OC_RUN_WIREWATCH,
+      .label = "wirewatch-1" },
     /* Withdraw a 5 EUR coin, at fee of 1 ct */
     { .oc = OC_WITHDRAW_SIGN,
       .label = "withdraw-coin-1",
@@ -3192,9 +3297,14 @@ run (void *cls)
     { .oc = OC_ADMIN_ADD_INCOMING,
       .label = "refresh-create-reserve-1",
       .expected_response_code = MHD_HTTP_OK,
-      .details.admin_add_incoming.sender_details = "{ \"type\":\"test\", 
\"bank_uri\":\"http://localhost:8082/\";, \"account_number\":424  }",
-      .details.admin_add_incoming.transfer_details = "{ \"uuid\":2  }",
+      .details.admin_add_incoming.debit_account_no = 424,
+      .details.admin_add_incoming.credit_account_no = EXCHANGE_ACCOUNT_NO,
+      .details.admin_add_incoming.auth_username = "user424",
+      .details.admin_add_incoming.auth_password = "pass424",
       .details.admin_add_incoming.amount = "EUR:5.01" },
+    /* Run wirewatch to observe /admin/add/incoming */
+    { .oc = OC_RUN_WIREWATCH,
+      .label = "wirewatch-2" },
     /* Withdraw a 5 EUR coin, at fee of 1 ct */
     { .oc = OC_WITHDRAW_SIGN,
       .label = "refresh-withdraw-coin-1",
@@ -3332,10 +3442,23 @@ run (void *cls)
       .details.check_bank_transfer.account_debit = 2,
       .details.check_bank_transfer.account_credit = 43
     },
+    { .oc = OC_CHECK_BANK_TRANSFER,
+      .label = "check_bank_transfer-aai-1",
+      .details.check_bank_transfer.exchange_base_url = "https://exchange.com/";,
+      .details.check_bank_transfer.amount = "EUR:5.01",
+      .details.check_bank_transfer.account_debit = 42,
+      .details.check_bank_transfer.account_credit = 2
+    },
+    { .oc = OC_CHECK_BANK_TRANSFER,
+      .label = "check_bank_transfer-aai-2",
+      .details.check_bank_transfer.exchange_base_url = "https://exchange.com/";,
+      .details.check_bank_transfer.amount = "EUR:5.01",
+      .details.check_bank_transfer.account_debit = 424,
+      .details.check_bank_transfer.account_credit = 2
+    },
 
     { .oc = OC_CHECK_BANK_TRANSFERS_EMPTY,
       .label = "check_bank_empty" },
-
     { .oc = OC_DEPOSIT_WTID,
       .label = "deposit-wtid-ok",
       .expected_response_code = MHD_HTTP_OK,
@@ -3365,9 +3488,14 @@ run (void *cls)
     { .oc = OC_ADMIN_ADD_INCOMING,
       .label = "create-reserve-r1",
       .expected_response_code = MHD_HTTP_OK,
-      .details.admin_add_incoming.sender_details = "{ \"type\":\"test\", 
\"bank_uri\":\"http://localhost:8082/\";, \"account_number\":42 }",
-      .details.admin_add_incoming.transfer_details = "{ \"uuid\":3  }",
+      .details.admin_add_incoming.debit_account_no = 42,
+      .details.admin_add_incoming.credit_account_no = EXCHANGE_ACCOUNT_NO,
+      .details.admin_add_incoming.auth_username = "user42",
+      .details.admin_add_incoming.auth_password = "pass42",
       .details.admin_add_incoming.amount = "EUR:5.01" },
+    /* Run wirewatch to observe /admin/add/incoming */
+    { .oc = OC_RUN_WIREWATCH,
+      .label = "wirewatch-3" },
     /* Withdraw a 5 EUR coin, at fee of 1 ct */
     { .oc = OC_WITHDRAW_SIGN,
       .label = "withdraw-coin-r1",
@@ -3388,6 +3516,13 @@ run (void *cls)
     /* Run transfers. Should do nothing as refund deadline blocks it */
     { .oc = OC_RUN_AGGREGATOR,
       .label = "run-aggregator-refund" },
+    { .oc = OC_CHECK_BANK_TRANSFER,
+      .label = "check_bank_transfer-aai-3",
+      .details.check_bank_transfer.exchange_base_url = "https://exchange.com/";,
+      .details.check_bank_transfer.amount = "EUR:5.01",
+      .details.check_bank_transfer.account_debit = 42,
+      .details.check_bank_transfer.account_credit = 2
+    },
     /* check that aggregator didn't do anything, as expected */
     { .oc = OC_CHECK_BANK_TRANSFERS_EMPTY,
       .label = "check-refund-not-run" },
@@ -3438,9 +3573,14 @@ run (void *cls)
     { .oc = OC_ADMIN_ADD_INCOMING,
       .label = "payback-create-reserve-1",
       .expected_response_code = MHD_HTTP_OK,
-      .details.admin_add_incoming.sender_details = "{ \"type\":\"test\", 
\"bank_uri\":\"http://localhost:8082/\";, \"account_number\":42}",
-      .details.admin_add_incoming.transfer_details = "{ \"uuid\":4  }",
+      .details.admin_add_incoming.debit_account_no = 42,
+      .details.admin_add_incoming.credit_account_no = EXCHANGE_ACCOUNT_NO,
+      .details.admin_add_incoming.auth_username = "user42",
+      .details.admin_add_incoming.auth_password = "pass42",
       .details.admin_add_incoming.amount = "EUR:5.01" },
+    /* Run wirewatch to observe /admin/add/incoming */
+    { .oc = OC_RUN_WIREWATCH,
+      .label = "wirewatch-4" },
     /* Withdraw a 5 EUR coin, at fee of 1 ct */
     { .oc = OC_WITHDRAW_SIGN,
       .label = "payback-withdraw-coin-1",
@@ -3472,9 +3612,14 @@ run (void *cls)
     { .oc = OC_ADMIN_ADD_INCOMING,
       .label = "payback-create-reserve-2",
       .expected_response_code = MHD_HTTP_OK,
-      .details.admin_add_incoming.sender_details = "{ \"type\":\"test\", 
\"bank_uri\":\"http://localhost:8082/\";, \"account_number\":42}",
-      .details.admin_add_incoming.transfer_details = "{ \"uuid\":5  }",
+      .details.admin_add_incoming.debit_account_no = 42,
+      .details.admin_add_incoming.credit_account_no = EXCHANGE_ACCOUNT_NO,
+      .details.admin_add_incoming.auth_username = "user42",
+      .details.admin_add_incoming.auth_password = "pass42",
       .details.admin_add_incoming.amount = "EUR:2.02" },
+    /* Run wirewatch to observe /admin/add/incoming */
+    { .oc = OC_RUN_WIREWATCH,
+      .label = "wirewatch-5" },
     /* Withdraw a 1 EUR coin, at fee of 1 ct */
     { .oc = OC_WITHDRAW_SIGN,
       .label = "payback-withdraw-coin-2a",
@@ -3533,15 +3678,62 @@ run (void *cls)
     { .oc = OC_ADMIN_ADD_INCOMING,
       .label = "payback-create-reserve-3",
       .expected_response_code = MHD_HTTP_OK,
-      .details.admin_add_incoming.sender_details = "{ \"type\":\"test\", 
\"bank_uri\":\"http://localhost:8082/\";, \"account_number\":42}",
-      .details.admin_add_incoming.transfer_details = "{ \"uuid\":6  }",
+      .details.admin_add_incoming.debit_account_no = 42,
+      .details.admin_add_incoming.credit_account_no = EXCHANGE_ACCOUNT_NO,
+      .details.admin_add_incoming.auth_username = "user42",
+      .details.admin_add_incoming.auth_password = "pass42",
       .details.admin_add_incoming.amount = "EUR:1.01" },
+    /* Run wirewatch to observe /admin/add/incoming */
+    { .oc = OC_RUN_WIREWATCH,
+      .label = "wirewatch-6" },
     { .oc = OC_WITHDRAW_SIGN,
       .label = "payback-withdraw-coin-3-revoked",
       .expected_response_code = MHD_HTTP_NOT_FOUND,
       .details.reserve_withdraw.reserve_reference = "payback-create-reserve-3",
       .details.reserve_withdraw.amount = "EUR:1" },
 
+    /* check that we are empty before the rejection test */
+    { .oc = OC_CHECK_BANK_TRANSFER,
+      .label = "check_bank_transfer-pr1",
+      .details.check_bank_transfer.exchange_base_url = "https://exchange.com/";,
+      .details.check_bank_transfer.amount = "EUR:5.01",
+      .details.check_bank_transfer.account_debit = 42,
+      .details.check_bank_transfer.account_credit = 2
+    },
+    { .oc = OC_CHECK_BANK_TRANSFER,
+      .label = "check_bank_transfer-pr2",
+      .details.check_bank_transfer.exchange_base_url = "https://exchange.com/";,
+      .details.check_bank_transfer.amount = "EUR:2.02",
+      .details.check_bank_transfer.account_debit = 42,
+      .details.check_bank_transfer.account_credit = 2
+    },
+    { .oc = OC_CHECK_BANK_TRANSFER,
+      .label = "check_bank_transfer-pr3",
+      .details.check_bank_transfer.exchange_base_url = "https://exchange.com/";,
+      .details.check_bank_transfer.amount = "EUR:1.01",
+      .details.check_bank_transfer.account_debit = 42,
+      .details.check_bank_transfer.account_credit = 2
+    },
+
+    { .oc = OC_CHECK_BANK_TRANSFERS_EMPTY,
+      .label = "check-empty-again" },
+
+    /* Test rejection of bogus wire transfers */
+    { .oc = OC_ADMIN_ADD_INCOMING,
+      .label = "bogus-subject",
+      .expected_response_code = MHD_HTTP_OK,
+      .details.admin_add_incoming.subject = "not a reserve public key",
+      .details.admin_add_incoming.debit_account_no = 42,
+      .details.admin_add_incoming.credit_account_no = EXCHANGE_ACCOUNT_NO,
+      .details.admin_add_incoming.auth_username = "user42",
+      .details.admin_add_incoming.auth_password = "pass42",
+      .details.admin_add_incoming.amount = "EUR:1.01" },
+    /* Run wirewatch to observe rejection */
+    { .oc = OC_RUN_WIREWATCH,
+      .label = "wirewatch-7" },
+    { .oc = OC_CHECK_BANK_TRANSFERS_EMPTY,
+      .label = "check-empty-from-reject" },
+
 
     /* ************** End of payback API testing************* */
 #endif
diff --git a/src/exchange-tools/taler-exchange-reservemod.c 
b/src/exchange-tools/taler-exchange-reservemod.c
index 84595d1..e901b69 100644
--- a/src/exchange-tools/taler-exchange-reservemod.c
+++ b/src/exchange-tools/taler-exchange-reservemod.c
@@ -81,6 +81,7 @@ run_transaction (const struct TALER_ReservePublicKeyP 
*reserve_pub,
   int ret;
   struct TALER_EXCHANGEDB_Session *session;
   void *json_str;
+  struct GNUNET_TIME_Absolute now;
 
   session = plugin->get_session (plugin->cls);
   if (NULL == session)
@@ -97,11 +98,13 @@ run_transaction (const struct TALER_ReservePublicKeyP 
*reserve_pub,
     GNUNET_break (0); /* out of memory? */
     return GNUNET_SYSERR;
   }
+  now = GNUNET_TIME_absolute_get ();
+  (void) GNUNET_TIME_round_abs (&now);
   ret = plugin->reserves_in_insert (plugin->cls,
                                     session,
                                     reserve_pub,
                                     add_value,
-                                    GNUNET_TIME_absolute_get (),
+                                    now,
                                     jdetails,
                                     json_str,
                                     strlen (json_str));
diff --git a/src/exchange/taler-exchange-aggregator.c 
b/src/exchange/taler-exchange-aggregator.c
index 84aef1a..3de5630 100644
--- a/src/exchange/taler-exchange-aggregator.c
+++ b/src/exchange/taler-exchange-aggregator.c
@@ -624,8 +624,10 @@ deposit_cb (void *cls,
                               &au->wtid,
                               sizeof (au->wtid));
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              "Starting aggregation under H(WTID)=%s\n",
-              TALER_B2S (&au->wtid));
+              "Starting aggregation under H(WTID)=%s, starting amount %s at 
%llu\n",
+              TALER_B2S (&au->wtid),
+              TALER_amount2s (amount_with_fee),
+              (unsigned long long) row_id);
 
   au->wp = find_plugin (extract_type (au->wire));
   if (NULL == au->wp)
@@ -759,8 +761,9 @@ aggregate_cb (void *cls,
     return qs;
   }
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-             "Added row %llu to aggregation\n",
-             (unsigned long long) row_id);
+             "Added row %llu with %s to aggregation\n",
+             (unsigned long long) row_id,
+              TALER_amount2s (&delta));
   return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
 }
 
@@ -1102,6 +1105,7 @@ run_reserve_closures (void *cls)
   enum GNUNET_DB_QueryStatus qs;
   const struct GNUNET_SCHEDULER_TaskContext *tc;
   struct ExpiredReserveContext erc;
+  struct GNUNET_TIME_Absolute now;
 
   task = NULL;
   reserves_idle = GNUNET_NO;
@@ -1130,9 +1134,11 @@ run_reserve_closures (void *cls)
   }
   erc.session = session;
   erc.async_cont = GNUNET_NO;
+  now = GNUNET_TIME_absolute_get ();
+  (void) GNUNET_TIME_round_abs (&now);
   qs = db_plugin->get_expired_reserves (db_plugin->cls,
                                        session,
-                                       GNUNET_TIME_absolute_get (),
+                                       now,
                                        &expired_reserve_cb,
                                        &erc);
   switch (qs)
diff --git a/src/exchange/taler-exchange-httpd_db.c 
b/src/exchange/taler-exchange-httpd_db.c
index 30bc33e..bd7777c 100644
--- a/src/exchange/taler-exchange-httpd_db.c
+++ b/src/exchange/taler-exchange-httpd_db.c
@@ -155,7 +155,7 @@ TEH_DB_calculate_transaction_list_totals (struct 
TALER_EXCHANGEDB_TransactionLis
       if (GNUNET_OK !=
           TALER_amount_add (&spent,
                             &spent,
-                            &pos->details.melt->amount_with_fee))
+                            &pos->details.melt->session.amount_with_fee))
       {
         GNUNET_break (0);
         return GNUNET_SYSERR;
diff --git a/src/exchange/taler-exchange-httpd_keystate.c 
b/src/exchange/taler-exchange-httpd_keystate.c
index be87f6d..801d6fe 100644
--- a/src/exchange/taler-exchange-httpd_keystate.c
+++ b/src/exchange/taler-exchange-httpd_keystate.c
@@ -39,7 +39,7 @@
  * release version, and the format is NOT the same that semantic
  * versioning uses either.
  */
-#define TALER_PROTOCOL_VERSION "1:0:1"
+#define TALER_PROTOCOL_VERSION "2:0:0"
 
 
 /**
@@ -1499,7 +1499,7 @@ make_fresh_key_state ()
                  GNUNET_h2s (&dke->denom_key_hash),
                  dke);
   }
-  
+
   /* Determine size of `krd_array` by counting number of discrete
      denomination key starting times. */
   last = GNUNET_TIME_UNIT_ZERO_ABS;
@@ -1662,15 +1662,37 @@ TEH_KS_denomination_key_lookup (const struct 
TEH_KS_StateHandle *key_state,
                                enum TEH_KS_DenominationKeyUse use)
 {
   struct GNUNET_HashCode hc;
+
+  GNUNET_CRYPTO_rsa_public_key_hash (denom_pub->rsa_public_key,
+                                     &hc);
+  return TEH_KS_denomination_key_lookup_by_hash (key_state,
+                                                 &hc,
+                                                 use);
+}
+
+
+/**
+ * Look up the issue for a denom public key.  Note that the result
+ * is only valid while the @a key_state is not released!
+ *
+ * @param key_state state to look in
+ * @param denom_pub_hash hash of denomination public key
+ * @param use purpose for which the key is being located
+ * @return the denomination key issue,
+ *         or NULL if denom_pub could not be found (or is not valid at this 
time for the given @a use)
+ */
+struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *
+TEH_KS_denomination_key_lookup_by_hash (const struct TEH_KS_StateHandle 
*key_state,
+                                        const struct GNUNET_HashCode 
*denom_pub_hash,
+                                        enum TEH_KS_DenominationKeyUse use)
+{
   struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki;
   struct GNUNET_TIME_Absolute now;
   const struct GNUNET_CONTAINER_MultiHashMap *map;
 
-  GNUNET_CRYPTO_rsa_public_key_hash (denom_pub->rsa_public_key,
-                                     &hc);
   map = (TEH_KS_DKU_PAYBACK == use) ? key_state->revoked_map : 
key_state->denomkey_map;
   dki = GNUNET_CONTAINER_multihashmap_get (map,
-                                          &hc);
+                                          denom_pub_hash);
   if (NULL == dki)
     return NULL;
   now = GNUNET_TIME_absolute_get ();
@@ -1679,7 +1701,7 @@ TEH_KS_denomination_key_lookup (const struct 
TEH_KS_StateHandle *key_state,
   {
     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
                "Not returning DKI for %s, as start time is in the future\n",
-               GNUNET_h2s (&hc));
+               GNUNET_h2s (denom_pub_hash));
     return NULL;
   }
   now = GNUNET_TIME_absolute_get ();
@@ -1691,7 +1713,7 @@ TEH_KS_denomination_key_lookup (const struct 
TEH_KS_StateHandle *key_state,
     {
       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
                  "Not returning DKI for %s, as time to create coins has 
passed\n",
-                 GNUNET_h2s (&hc));
+                 GNUNET_h2s (denom_pub_hash));
       return NULL;
     }
     break;
@@ -1701,7 +1723,7 @@ TEH_KS_denomination_key_lookup (const struct 
TEH_KS_StateHandle *key_state,
     {
       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
                  "Not returning DKI for %s, as time to spend coin has 
passed\n",
-                 GNUNET_h2s (&hc));
+                 GNUNET_h2s (denom_pub_hash));
       return NULL;
     }
     break;
@@ -1711,7 +1733,7 @@ TEH_KS_denomination_key_lookup (const struct 
TEH_KS_StateHandle *key_state,
     {
       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
                  "Not returning DKI for %s, as time to payback coin has 
passed\n",
-                 GNUNET_h2s (&hc));
+                 GNUNET_h2s (denom_pub_hash));
       return NULL;
     }
     break;
diff --git a/src/exchange/taler-exchange-httpd_keystate.h 
b/src/exchange/taler-exchange-httpd_keystate.h
index b956c63..c332182 100644
--- a/src/exchange/taler-exchange-httpd_keystate.h
+++ b/src/exchange/taler-exchange-httpd_keystate.h
@@ -121,6 +121,22 @@ TEH_KS_denomination_key_lookup (const struct 
TEH_KS_StateHandle *key_state,
 
 
 /**
+ * Look up the issue for a denom public key.  Note that the result
+ * is only valid while the @a key_state is not released!
+ *
+ * @param key_state state to look in
+ * @param denom_pub_hash hash of denomination public key
+ * @param use purpose for which the key is being located
+ * @return the denomination key issue,
+ *         or NULL if denom_pub could not be found (or is not valid at this 
time for the given @a use)
+ */
+struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *
+TEH_KS_denomination_key_lookup_by_hash (const struct TEH_KS_StateHandle 
*key_state,
+                                        const struct GNUNET_HashCode 
*denom_pub_hash,
+                                        enum TEH_KS_DenominationKeyUse use);
+
+
+/**
  * Read signals from a pipe in a loop, and reload keys from disk if
  * SIGUSR1 is received, terminate if SIGTERM/SIGINT is received, and
  * restart if SIGHUP is received.
diff --git a/src/exchange/taler-exchange-httpd_refresh_link.c 
b/src/exchange/taler-exchange-httpd_refresh_link.c
index 0be6998..aee2336 100644
--- a/src/exchange/taler-exchange-httpd_refresh_link.c
+++ b/src/exchange/taler-exchange-httpd_refresh_link.c
@@ -32,183 +32,88 @@
 
 
 /**
- * @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.
+   * Public key of the coin for which we are running /refresh/link.
    */
   struct TALER_CoinSpendPublicKeyP coin_pub;
 
   /**
-   * Session link data we collect.
-   */
-  struct TEH_RESPONSE_LinkSessionInfo *sessions;
-
-  /**
-   * Database session. Nothing to do with @a sessions.
+   * Json array with transfer data we collect.
    */
-  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;
+  json_t *mlist;
 
   /**
-   * 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().
+   * Taler error code.
    */
-  int status;
+  enum TALER_ErrorCode ec;
 };
 
 
 /**
- * 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
+ * @param ldl link data related to @a transfer_pub
  */
 static void
-handle_transfer_data (void *cls,
-                      const struct GNUNET_HashCode *session_hash,
-                      const struct TALER_TransferPublicKeyP *transfer_pub)
+handle_link_data (void *cls,
+                  const struct TALER_TransferPublicKeyP *transfer_pub,
+                  const struct TALER_EXCHANGEDB_LinkDataList *ldl)
 {
   struct HTD_Context *ctx = cls;
-  struct TALER_EXCHANGEDB_LinkDataList *ldl;
-  struct TEH_RESPONSE_LinkSessionInfo *lsi;
-  enum GNUNET_DB_QueryStatus qs;
+  json_t *list;
+  json_t *root;
 
-  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != ctx->status)
+  if (NULL == ctx->mlist)
     return;
-  ldl = NULL;
-  qs = TEH_plugin->get_link_data_list (TEH_plugin->cls,
-                                      ctx->session,
-                                      session_hash,
-                                      &ldl);
-  if (qs <= 0) 
+  if (NULL == (list = json_array ()))
+    goto fail;
+
+  for (const struct TALER_EXCHANGEDB_LinkDataList *pos = ldl;
+       NULL != pos;
+       pos = pos->next)
   {
-    if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
-      ctx->status = GNUNET_DB_STATUS_HARD_ERROR;
-    else
-      ctx->status = qs;
-    return;
+    json_t *obj;
+
+    if (NULL == (obj = json_object ()))
+      goto fail;
+    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));
+    if (0 !=
+        json_array_append_new (list,
+                               obj))
+      goto fail;
   }
-  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;
+  if (NULL == (root = json_object ()))
+    goto fail;
+  json_object_set_new (root,
+                       "new_coins",
+                       list);
+  json_object_set_new (root,
+                       "transfer_pub",
+                       GNUNET_JSON_from_data_auto (transfer_pub));
+  if (0 !=
+      json_array_append_new (ctx->mlist,
+                             root))
+    goto fail;
+  return;
+ fail:
+  ctx->ec = TALER_EC_JSON_ALLOCATION_FAILURE;
+  json_decref (ctx->mlist);
+  ctx->mlist = NULL;
 }
 
 
@@ -239,14 +144,18 @@ refresh_link_transaction (void *cls,
   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;
+  qs = TEH_plugin->get_link_data (TEH_plugin->cls,
+                                  session,
+                                  &ctx->coin_pub,
+                                  &handle_link_data,
+                                  ctx);
+  if (NULL == ctx->mlist)
+  {
+    *mhd_ret = TEH_RESPONSE_reply_internal_error (connection,
+                                                  ctx->ec,
+                                                  "coin_pub");
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  }
   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
   {
     *mhd_ret = TEH_RESPONSE_reply_arg_unknown (connection,
@@ -254,21 +163,6 @@ refresh_link_transaction (void *cls,
                                               "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;
 }
 
@@ -306,19 +200,21 @@ TEH_REFRESH_handler_refresh_link (struct 
TEH_RequestHandler *rh,
     return MHD_NO;
   if (GNUNET_OK != res)
     return MHD_YES;
+  ctx.mlist = json_array ();
   if (GNUNET_OK !=
       TEH_DB_run_transaction (connection,
                              &mhd_ret,
                              &refresh_link_transaction,
                              &ctx))
   {
-    purge_context (&ctx);
+    if (NULL != ctx.mlist)
+      json_decref (ctx.mlist);
     return mhd_ret;
   }
-  mhd_ret = reply_refresh_link_success (connection,
-                                       ctx.num_sessions,
-                                       ctx.sessions);
-  purge_context (&ctx);
+  mhd_ret = TEH_RESPONSE_reply_json (connection,
+                                     ctx.mlist,
+                                     MHD_HTTP_OK);
+  json_decref (ctx.mlist);
   return mhd_ret;
 }
 
diff --git a/src/exchange/taler-exchange-httpd_refresh_melt.c 
b/src/exchange/taler-exchange-httpd_refresh_melt.c
index 320ec9d..400d2bb 100644
--- a/src/exchange/taler-exchange-httpd_refresh_melt.c
+++ b/src/exchange/taler-exchange-httpd_refresh_melt.c
@@ -32,38 +32,6 @@
 
 
 /**
- * @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
@@ -116,14 +84,14 @@ reply_refresh_melt_insufficient_funds (struct 
MHD_Connection *connection,
  * 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 rc value the client commited to
  * @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)
+                           const struct TALER_RefreshCommitmentP *rc,
+                           uint32_t noreveal_index)
 {
   struct TALER_RefreshMeltConfirmationPS body;
   struct TALER_ExchangePublicKeyP pub;
@@ -132,9 +100,8 @@ reply_refresh_melt_success (struct MHD_Connection 
*connection,
 
   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);
+  body.rc = *rc;
+  body.noreveal_index = htonl (noreveal_index);
   if (GNUNET_OK !=
       TEH_KS_sign (&body.purpose,
                   &pub,
@@ -162,63 +129,22 @@ struct RefreshMeltContext
 {
 
   /**
-   * Key state that can be used to lookup keys.
+   * noreveal_index is only initialized during
+   * #refresh_melt_transaction().
    */
-  struct TEH_KS_StateHandle *key_state;
+  struct TALER_EXCHANGEDB_RefreshSession refresh_session;
 
   /**
-   * Information about the denomination key of the coin being
-   * melted.
+   * Information about the @e coin's denomination.
    */
   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.
+ * Check that the coin has sufficient funds left for the selected
+ * melt operation.
  *
  * @param connection the connection to send errors to
  * @param session the database connection
@@ -233,20 +159,19 @@ refresh_check_melt (struct MHD_Connection *connection,
                    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;
+  /* Start with cost of this melt transaction */
+  spent = rmc->refresh_session.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,
+                                          &rmc->refresh_session.coin.coin_pub,
                                          &tl);
   if (0 > qs)
   {
@@ -267,33 +192,32 @@ refresh_check_melt (struct MHD_Connection *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)
   {
+    struct TALER_Amount coin_residual;
+
     GNUNET_assert (GNUNET_SYSERR !=
                    TALER_amount_subtract (&coin_residual,
                                           &spent,
-                                          
&rmc->coin_melt_details.melt_amount_with_fee));
+                                          
&rmc->refresh_session.amount_with_fee));
     *mhd_ret = reply_refresh_melt_insufficient_funds (connection,
-                                                     
&rmc->coin_melt_details.coin_info.coin_pub,
+                                                     
&rmc->refresh_session.coin.coin_pub,
                                                      coin_value,
                                                      tl,
-                                                     
&rmc->coin_melt_details.melt_amount_with_fee,
+                                                     
&rmc->refresh_session.amount_with_fee,
                                                      &coin_residual);
     TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
                                             tl);
     return GNUNET_DB_STATUS_HARD_ERROR;
   }
+
+  /* we're good, coin has sufficient funds to be melted */
   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;
 }
 
@@ -325,17 +249,19 @@ refresh_melt_transaction (void *cls,
                          int *mhd_ret)
 {
   struct RefreshMeltContext *rmc = cls;
+  struct TALER_EXCHANGEDB_RefreshMelt rm;
   enum GNUNET_DB_QueryStatus qs;
 
-  qs = TEH_plugin->get_refresh_session (TEH_plugin->cls,
-                                       session,
-                                       &rmc->session_hash,
-                                       &rmc->refresh_session);
+  /* Check if we already created such a session */
+  qs = TEH_plugin->get_melt (TEH_plugin->cls,
+                             session,
+                             &rmc->refresh_session.rc,
+                             &rm);
   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
   {
     *mhd_ret = reply_refresh_melt_success (connection,
-                                          &rmc->session_hash,
-                                          rmc->refresh_session.noreveal_index);
+                                          &rmc->refresh_session.rc,
+                                          rm.session.noreveal_index);
     return GNUNET_DB_STATUS_HARD_ERROR;
   }
   if (0 > qs)
@@ -346,12 +272,7 @@ refresh_melt_transaction (void *cls,
     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);
-
+  /* check coin has enough funds remaining on it to cover melt cost */
   qs = refresh_check_melt (connection,
                           session,
                           rmc,
@@ -359,28 +280,15 @@ refresh_melt_transaction (void *cls,
   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]))) )
+  /* pick challenge and persist it */
+  rmc->refresh_session.noreveal_index
+    = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_STRONG,
+                               TALER_CNC_KAPPA);
+
+  if (0 >=
+      (qs = TEH_plugin->insert_melt (TEH_plugin->cls,
+                                     session,
+                                     &rmc->refresh_session)))
   {
     if (GNUNET_DB_STATUS_SOFT_ERROR != qs)
     {
@@ -395,233 +303,6 @@ refresh_melt_transaction (void *cls,
 
 
 /**
- * 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
@@ -629,223 +310,70 @@ cleanup_rmc (struct RefreshMeltContext *rmc)
  * 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
+ * @param[in,out] rmc details about the melt request
  * @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)
+handle_refresh_melt (struct MHD_Connection *connection,
+                     struct RefreshMeltContext *rmc)
 {
-  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 */
+  /* sanity-check that "total melt amount > melt fee" */
   {
-    struct TALER_AmountNBO melt_amount;
+    struct TALER_Amount fee_refresh;
 
-    res = get_coin_public_info (connection,
-                                melt_coin,
-                                &rmc.coin_melt_details);
-    if (GNUNET_OK != res)
+    TALER_amount_ntoh (&fee_refresh,
+                      &rmc->dki->issue.properties.fee_refresh);
+    if (TALER_amount_cmp (&fee_refresh,
+                         &rmc->refresh_session.amount_with_fee) > 0)
     {
       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);
+      return TEH_RESPONSE_reply_external_error (connection,
+                                               
TALER_EC_REFRESH_MELT_FEES_EXCEED_CONTRIBUTION,
+                                               "melt amount smaller than 
melting fee");
     }
   }
 
-  GNUNET_CRYPTO_hash_context_finish (rmc.hash_context,
-                                     &rmc.session_hash);
-  rmc.hash_context = NULL;
-
-  rmc.key_state = TEH_KS_acquire ();
-  if (NULL == rmc.key_state)
-  {
-    TALER_LOG_ERROR ("Lacking keys to operate\n");
-    return TEH_RESPONSE_reply_internal_error (connection,
-                                              
TALER_EC_EXCHANGE_BAD_CONFIGURATION,
-                                              "no keys");
-  }
-  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;
+    body.rc = rmc->refresh_session.rc;
     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");
-    }
+                      &rmc->refresh_session.amount_with_fee);
+    body.melt_fee = rmc->dki->issue.properties.fee_refresh;
+    body.coin_pub = rmc->refresh_session.coin.coin_pub;
 
     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))
+                                   
&rmc->refresh_session.coin_sig.eddsa_signature,
+                                   
&rmc->refresh_session.coin.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))
+  /* run transaction */
   {
-    cleanup_rmc (&rmc);
-    return mhd_ret;
+    int mhd_ret;
+
+    if (GNUNET_OK !=
+        TEH_DB_run_transaction (connection,
+                                &mhd_ret,
+                                &refresh_melt_transaction,
+                                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;
+
+  /* generate ordinary response */
+  return reply_refresh_melt_success (connection,
+                                     &rmc->refresh_session.rc,
+                                     rmc->refresh_session.noreveal_index);
 }
 
 
@@ -870,16 +398,22 @@ TEH_REFRESH_handler_refresh_melt (struct 
TEH_RequestHandler *rh,
                                   size_t *upload_data_size)
 {
   json_t *root;
-  json_t *new_denoms;
-  json_t *melt_coin;
-  json_t *coin_evs;
-  json_t *transfer_pubs;
+  struct RefreshMeltContext rmc;
   int res;
+  struct TEH_KS_StateHandle *key_state;
   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_fixed_auto ("coin_pub",
+                                 &rmc.refresh_session.coin.coin_pub),
+    TALER_JSON_spec_denomination_signature ("denom_sig",
+                                            
&rmc.refresh_session.coin.denom_sig),
+    TALER_JSON_spec_denomination_public_key ("denom_pub",
+                                             
&rmc.refresh_session.coin.denom_pub),
+    GNUNET_JSON_spec_fixed_auto ("confirm_sig",
+                                 &rmc.refresh_session.coin_sig),
+    TALER_JSON_spec_amount ("value_with_fee",
+                            &rmc.refresh_session.amount_with_fee),
+    GNUNET_JSON_spec_fixed_auto ("rc",
+                                 &rmc.refresh_session.rc),
     GNUNET_JSON_spec_end ()
   };
 
@@ -894,6 +428,9 @@ TEH_REFRESH_handler_refresh_melt (struct TEH_RequestHandler 
*rh,
        (NULL == root) )
     return MHD_YES;
 
+  memset (&rmc,
+          0,
+          sizeof (rmc));
   res = TEH_PARSE_json_data (connection,
                              root,
                              spec);
@@ -901,29 +438,60 @@ TEH_REFRESH_handler_refresh_melt (struct 
TEH_RequestHandler *rh,
   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))
+  if (GNUNET_OK !=
+      TALER_test_coin_valid (&rmc.refresh_session.coin))
   {
     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");
+    return TEH_RESPONSE_reply_signature_invalid (connection,
+                                                 
TALER_EC_REFRESH_MELT_DENOMINATION_SIGNATURE_INVALID,
+                                                 "denom_sig");
   }
-  if (TALER_CNC_KAPPA != json_array_size (transfer_pubs))
+
+  /* run actual logic, now that the request was parsed */
+  key_state = TEH_KS_acquire ();
+  if (NULL == key_state)
   {
-    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");
+    TALER_LOG_ERROR ("Lacking keys to operate\n");
+    res = TEH_RESPONSE_reply_internal_error (connection,
+                                             
TALER_EC_EXCHANGE_BAD_CONFIGURATION,
+                                             "no keys");
+    goto cleanup;
+  }
+  rmc.dki = TEH_KS_denomination_key_lookup (key_state,
+                                            
&rmc.refresh_session.coin.denom_pub,
+                                            TEH_KS_DKU_DEPOSIT);
+  if (NULL == rmc.dki)
+  {
+    TALER_LOG_WARNING ("Unknown denomination key in /refresh/melt request\n");
+    res = TEH_RESPONSE_reply_arg_unknown (connection,
+                                          
TALER_EC_REFRESH_MELT_DENOMINATION_KEY_NOT_FOUND,
+                                          "denom_pub");
+    goto cleanup;
+  }
+
+  res = handle_refresh_melt (connection,
+                             &rmc);
+
+
+ cleanup:
+  if (NULL != key_state)
+  {
+    TEH_KS_release (key_state);
+    key_state = NULL;
+  }
+  if (NULL != rmc.refresh_session.coin.denom_pub.rsa_public_key)
+  {
+    GNUNET_CRYPTO_rsa_public_key_free 
(rmc.refresh_session.coin.denom_pub.rsa_public_key);
+    rmc.refresh_session.coin.denom_pub.rsa_public_key = NULL;
+  }
+  if (NULL != rmc.refresh_session.coin.denom_sig.rsa_signature)
+  {
+    GNUNET_CRYPTO_rsa_signature_free 
(rmc.refresh_session.coin.denom_sig.rsa_signature);
+    rmc.refresh_session.coin.denom_sig.rsa_signature = NULL;
   }
-  res = handle_refresh_melt_json (connection,
-                                  new_denoms,
-                                  melt_coin,
-                                  transfer_pubs,
-                                  coin_evs);
   GNUNET_JSON_parse_free (spec);
+
   return res;
 }
 
diff --git a/src/exchange/taler-exchange-httpd_refresh_reveal.c 
b/src/exchange/taler-exchange-httpd_refresh_reveal.c
index a64ec1c..4a7cd33 100644
--- a/src/exchange/taler-exchange-httpd_refresh_reveal.c
+++ b/src/exchange/taler-exchange-httpd_refresh_reveal.c
@@ -32,6 +32,12 @@
 
 
 /**
+ * Maximum number of fresh coins we allow per refresh operation.
+ */
+#define MAX_FRESH_COINS 256
+
+
+/**
  * Send a response for "/refresh/reveal".
  *
  * @param connection the connection to send the response to
@@ -79,133 +85,19 @@ reply_refresh_reveal_success (struct MHD_Connection 
*connection,
  * 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
+ * @param rc commitment computed by the exchange
  * @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)
+                               const struct TALER_RefreshCommitmentP *rc)
 {
-  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}",
+                                       "{s:s, s:I, s:o}",
                                        "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_PlanchetSecretsP fc;
-    struct TALER_CoinSpendPublicKeyP coin_pub;
-    struct GNUNET_HashCode h_msg;
-    char *buf;
-    size_t buf_len;
-
-    TALER_planchet_setup_refresh (&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;
+                                       "rc_expected", 
GNUNET_JSON_from_data_auto (rc));
 }
 
 
@@ -216,169 +108,83 @@ struct RevealContext
 {
 
   /**
-   * Hash of the refresh session.
+   * Commitment of the refresh operaton.
    */
-  const struct GNUNET_HashCode *session_hash;
+  struct TALER_RefreshCommitmentP rc;
 
   /**
-   * Database session used to execute the transaction.
+   * Transfer public key at gamma.
    */
-  struct TALER_EXCHANGEDB_Session *session;
-
-  /**
-   * Session state from the database.
-   */
-  struct TALER_EXCHANGEDB_RefreshSession refresh_session;
+  struct TALER_TransferPublicKeyP gamma_tp;
 
   /**
-   * Array of denomination public keys used for the refresh.
+   * Transfer private keys revealed to us.
    */
-  struct TALER_DenominationPublicKey *denom_pubs;
+  struct TALER_TransferPrivateKeyP transfer_privs[TALER_CNC_KAPPA - 1];
 
   /**
-   * Envelopes with the signatures to be returned.
+   * Denominations being requested.
    */
-  struct TALER_DenominationSignature *ev_sigs;
+  const struct TALER_EXCHANGEDB_DenominationKeyIssueInformation **dkis;
 
   /**
-   * Commitment data from the DB giving data about original
-   * commitments, in particular the blinded envelopes (for
-   * index gamma).
+   * Envelopes to be signed.
    */
-  struct TALER_EXCHANGEDB_RefreshCommitCoin *commit_coins;
+  const struct TALER_RefreshCoinData *rcds;
 
   /**
-   * Transfer public key associated with the gamma value
-   * selected by the exchange.
+   * Envelopes with the signatures to be returned.  Initially NULL.
    */
-  struct TALER_TransferPublicKeyP gamma_tp;
+  struct TALER_DenominationSignature *ev_sigs;
 
   /**
-   * Transfer private keys revealed to us.
+   * Size of the @e dkis, @e rcds and @e ev_sigs arrays (if non-NULL).
    */
-  struct TALER_TransferPrivateKeyP transfer_privs[TALER_CNC_KAPPA - 1];
+  unsigned int num_fresh_coins;
 
 };
 
 
 /**
- * Exchange a coin as part of a refresh operation.  Obtains the
- * envelope from the database and performs the signing operation.
+ * Function called with information about a refresh order we already
+ * persisted.  Stores the result in @a cls so we don't do the calculation
+ * again.
  *
- * @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
+ * @param cls closure with a `struct RevealContext`
+ * @param rowid unique serial ID for the row in our database
+ * @param num_newcoins size of the @a rrcs array
+ * @param rrcs array of @a num_newcoins information about coins to be created
+ * @param num_tprivs number of entries in @a tprivs, should be 
#TALER_CNC_KAPPA - 1
+ * @param tprivs array of @e num_tprivs transfer private keys
+ * @param tp transfer public key information
  */
-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)
+static void
+check_exists_cb (void *cls,
+                 uint32_t num_newcoins,
+                 const struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrcs,
+                 unsigned int num_tprivs,
+                 const struct TALER_TransferPrivateKeyP *tprivs,
+                 const struct TALER_TransferPublicKeyP *tp)
 {
-  struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki;
-  enum GNUNET_DB_QueryStatus qs;
+  struct RevealContext *rctx = cls;
 
-  dki = TEH_KS_denomination_key_lookup (key_state,
-                                        denom_pub,
-                                       TEH_KS_DKU_WITHDRAW);
-  if (NULL == dki)
+  if (0 == num_newcoins)
   {
     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;
+    return;
   }
+  GNUNET_break (TALER_CNC_KAPPA - 1 == num_tprivs);
+  GNUNET_break_op (0 == memcmp (tp,
+                                &rctx->gamma_tp,
+                                sizeof (struct TALER_TransferPublicKeyP)));
+  GNUNET_break_op (0 == memcmp (tprivs,
+                                &rctx->transfer_privs,
+                                sizeof (struct TALER_TransferPrivateKeyP) * 
num_tprivs));
+  rctx->ev_sigs = GNUNET_new_array (num_newcoins,
+                                    struct TALER_DenominationSignature);
+  for (unsigned int i=0;i<num_newcoins;i++)
+    rctx->ev_sigs[i].rsa_signature
+      = GNUNET_CRYPTO_rsa_signature_dup (rrcs[i].coin_sig.rsa_signature);
 }
 
 
@@ -408,233 +214,230 @@ refresh_reveal_transaction (void *cls,
                            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;
+  struct RevealContext *rctx = cls;
+  struct TALER_EXCHANGEDB_RefreshMelt refresh_melt;
   enum GNUNET_DB_QueryStatus qs;
 
-  rc->session = session;
-  qs = TEH_plugin->get_refresh_session (TEH_plugin->cls,
-                                       session,
-                                       rc->session_hash,
-                                       &rc->refresh_session);
+  /* Try to see if we already have given an answer before. */
+  qs = TEH_plugin->get_refresh_reveal (TEH_plugin->cls,
+                                       session,
+                                       &rctx->rc,
+                                       &check_exists_cb,
+                                       rctx);
+  switch (qs) {
+  case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+    /* continue normal execution */
+    break;
+  case GNUNET_DB_STATUS_SOFT_ERROR:
+    return qs;
+  case GNUNET_DB_STATUS_HARD_ERROR:
+    GNUNET_break (qs);
+    *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+                                                    
TALER_EC_REFRESH_REVEAL_DB_FETCH_REVEAL_ERROR);
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+  default:
+    /* Hossa, already found our reply! */
+    GNUNET_assert (NULL != rctx->ev_sigs);
+    return qs;
+  }
+
+  /* Obtain basic information about the refresh operation and what
+     gamma we committed to. */
+  qs = TEH_plugin->get_melt (TEH_plugin->cls,
+                             session,
+                             &rctx->rc,
+                             &refresh_melt);
   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
   {
     *mhd_ret = TEH_RESPONSE_reply_arg_invalid (connection,
                                               
TALER_EC_REFRESH_REVEAL_SESSION_UNKNOWN,
-                                              "session_hash");
+                                              "rc");
     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) )
+       (refresh_melt.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++)
+  /* Verify commitment */
   {
-    if (i == rc->refresh_session.noreveal_index)
+    /* Note that the contents of rcs[refresh_melt.session.noreveal_index]
+       will be aliased and are *not* allocated (or deallocated) in
+       this function -- in contrast to the other offsets! */
+    struct TALER_RefreshCommitmentEntry rcs[TALER_CNC_KAPPA];
+    struct TALER_RefreshCommitmentP rc_expected;
+    unsigned int off;
+
+    off = 0; /* did we pass session.noreveal_index yet? */
+    for (unsigned int i=0;i<TALER_CNC_KAPPA;i++)
     {
-      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)
+      struct TALER_RefreshCommitmentEntry *rce = &rcs[i];
+
+      if (i == refresh_melt.session.noreveal_index)
+      {
+        /* Take these coin envelopes from the client */
+        rce->transfer_pub = rctx->gamma_tp;
+        rce->new_coins = (struct TALER_RefreshCoinData *) rctx->rcds;
+        off = 1;
+      }
+      else
       {
-        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;
+        /* Reconstruct coin envelopes from transfer private key */
+        struct TALER_TransferPrivateKeyP *tpriv = &rctx->transfer_privs[i - 
off];
+        struct TALER_TransferSecretP ts;
+
+        GNUNET_CRYPTO_ecdhe_key_get_public (&tpriv->ecdhe_priv,
+                                            &rce->transfer_pub.ecdhe_pub);
+        TALER_link_reveal_transfer_secret (tpriv,
+                                           &refresh_melt.session.coin.coin_pub,
+                                           &ts);
+        rce->new_coins = GNUNET_new_array (rctx->num_fresh_coins,
+                                           struct TALER_RefreshCoinData);
+        for (unsigned int j=0;j<rctx->num_fresh_coins;j++)
+        {
+          struct TALER_RefreshCoinData *rcd = &rce->new_coins[j];
+          struct TALER_PlanchetSecretsP ps;
+          struct TALER_PlanchetDetail pd;
+
+          rcd->dk = &rctx->dkis[j]->denom_pub;
+          TALER_planchet_setup_refresh (&ts,
+                                        j,
+                                        &ps);
+          TALER_planchet_prepare (rcd->dk,
+                                  &ps,
+                                  &pd);
+          rcd->coin_ev = pd.coin_ev;
+          rcd->coin_ev_size = pd.coin_ev_size;
+        }
       }
-      GNUNET_CRYPTO_hash_context_read (hash_context,
-                                       &rc->gamma_tp,
-                                       sizeof (struct 
TALER_TransferPublicKeyP));
     }
-    else
+    TALER_refresh_get_commitment (&rc_expected,
+                                  TALER_CNC_KAPPA,
+                                  rctx->num_fresh_coins,
+                                  rcs,
+                                  &refresh_melt.session.coin.coin_pub,
+                                  &refresh_melt.session.amount_with_fee);
+
+    /* Free resources allocated above */
+    for (unsigned int i=0;i<TALER_CNC_KAPPA;i++)
     {
-      /* 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));
-    }
-  }
+      struct TALER_RefreshCommitmentEntry *rce = &rcs[i];
 
-  /* 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);
-  }
+      if (i == refresh_melt.session.noreveal_index)
+        continue; /* This offset is special... */
+      for (unsigned int j=0;j<rctx->num_fresh_coins;j++)
+      {
+        struct TALER_RefreshCoinData *rcd = &rce->new_coins[j];
 
-  /* 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));
-  }
+        GNUNET_free (rcd->coin_ev);
+      }
+      GNUNET_free (rce->new_coins);
+    }
+
+    /* Verify rc_expected matches rc */
+    if (0 != memcmp (&rctx->rc,
+                     &rc_expected,
+                     sizeof (struct TALER_RefreshCommitmentP)))
+    {
+      GNUNET_break_op (0);
+      *mhd_ret = reply_refresh_reveal_missmatch (connection,
+                                                 &rc_expected);
+      return GNUNET_DB_STATUS_HARD_ERROR;
+    }
+  } /* end of checking "rc_expected" */
 
-  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++)
+  /* check amounts add up! */
   {
-    int res;
+    struct TALER_Amount refresh_cost;
 
-    if (i == rc->refresh_session.noreveal_index)
+    refresh_cost = refresh_melt.melt_fee;
+    for (unsigned int i=0;i<rctx->num_fresh_coins;i++)
     {
-      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++)
+      struct TALER_Amount fee_withdraw;
+      struct TALER_Amount value;
+      struct TALER_Amount total;
+
+      TALER_amount_ntoh (&fee_withdraw,
+                         &rctx->dkis[i]->issue.properties.fee_withdraw);
+      TALER_amount_ntoh (&value,
+                         &rctx->dkis[i]->issue.properties.value);
+      if ( (GNUNET_OK !=
+            TALER_amount_add (&total,
+                              &fee_withdraw,
+                              &value)) ||
+           (GNUNET_OK !=
+            TALER_amount_add (&refresh_cost,
+                              &refresh_cost,
+                              &total)) )
       {
-        GNUNET_CRYPTO_hash_context_read (hash_context,
-                                         rc->commit_coins[j].coin_ev,
-                                         rc->commit_coins[j].coin_ev_size);
+        GNUNET_break_op (0);
+        *mhd_ret = TEH_RESPONSE_reply_internal_error (connection,
+                                                      
TALER_EC_REFRESH_REVEAL_COST_CALCULATION_OVERFLOW,
+                                                      "failed to add up 
refresh costs");
+        return GNUNET_DB_STATUS_HARD_ERROR;
       }
-      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)))
+    if (0 < TALER_amount_cmp (&refresh_cost,
+                              &refresh_melt.session.amount_with_fee))
     {
       GNUNET_break_op (0);
-      cleanup_rc (rc);
-      GNUNET_CRYPTO_hash_context_abort (hash_context);
-      *mhd_ret = (GNUNET_NO == res) ? MHD_YES : MHD_NO;
+      *mhd_ret = TEH_RESPONSE_reply_external_error (connection,
+                                                    
TALER_EC_REFRESH_REVEAL_AMOUNT_INSUFFICIENT,
+                                                    "melted coin value is 
insufficient to cover cost of operation");
       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);
+  rctx->ev_sigs = GNUNET_new_array (rctx->num_fresh_coins,
+                                    struct TALER_DenominationSignature);
+  for (unsigned int i=0;i<rctx->num_fresh_coins;i++)
   {
-    struct TEH_KS_StateHandle *key_state;
-
-    key_state = TEH_KS_acquire ();
-    if (NULL == key_state)
+    rctx->ev_sigs[i].rsa_signature
+      = GNUNET_CRYPTO_rsa_sign_blinded 
(rctx->dkis[i]->denom_priv.rsa_private_key,
+                                        rctx->rcds[i].coin_ev,
+                                        rctx->rcds[i].coin_ev_size);
+    if (NULL == rctx->ev_sigs[i].rsa_signature)
     {
-      TALER_LOG_ERROR ("Lacking keys to operate\n");
-      cleanup_rc (rc);
+      *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+                                                       
TALER_EC_REFRESH_REVEAL_SIGNING_ERROR);
       return GNUNET_DB_STATUS_HARD_ERROR;
     }
-    for (unsigned int j=0;j<rc->refresh_session.num_newcoins;j++)
+  }
+
+  /* Persist operation result in DB */
+  {
+    struct TALER_EXCHANGEDB_RefreshRevealedCoin rrcs[rctx->num_fresh_coins];
+
+    for (unsigned int i=0;i<rctx->num_fresh_coins;i++)
     {
-      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;
-      }
+      struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrc = &rrcs[i];
+
+      rrc->denom_pub = rctx->dkis[i]->denom_pub;
+      rrc->coin_ev = rctx->rcds[i].coin_ev;
+      rrc->coin_ev_size = rctx->rcds[i].coin_ev_size;
+      rrc->coin_sig = rctx->ev_sigs[i];
     }
-    TEH_KS_release (key_state);
+    qs = TEH_plugin->insert_refresh_reveal (TEH_plugin->cls,
+                                            session,
+                                            &rctx->rc,
+                                            rctx->num_fresh_coins,
+                                            rrcs,
+                                            TALER_CNC_KAPPA - 1,
+                                            rctx->transfer_privs,
+                                            &rctx->gamma_tp);
   }
-  if (0 >= qs)
+  if (GNUNET_DB_STATUS_HARD_ERROR == qs)
   {
-    cleanup_rc (rc);
-    return qs;
+    *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+                                                     
TALER_EC_REFRESH_REVEAL_DB_COMMIT_ERROR);
   }
   return qs;
 }
@@ -648,59 +451,171 @@ refresh_reveal_transaction (void *cls,
  * coins.
  *
  * @param connection the MHD connection to handle
- * @param session_hash hash identifying the melting session
+ * @param rctx context for the operation, partially built at this time
+ * @param transfer_pub transfer public key
  * @param tp_json private transfer keys in JSON format
+ * @param new_denoms_h_json requests for fresh coins to be created
+ * @param coin_evs envelopes of gamma-selected coins to be signed
  * @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 *rctx,
+                            const json_t *tp_json,
+                            const json_t *new_denoms_h_json,
+                            const json_t *coin_evs)
 {
-  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++)
+  unsigned int num_fresh_coins = json_array_size (new_denoms_h_json);
+  unsigned int num_tprivs = json_array_size (tp_json);
+  struct TEH_KS_StateHandle *key_state;
+
+  GNUNET_assert (num_tprivs == TALER_CNC_KAPPA - 1);
+  if ( (num_fresh_coins >= MAX_FRESH_COINS) ||
+       (0 == num_fresh_coins) )
+  {
+    GNUNET_break_op (0);
+    return TEH_RESPONSE_reply_arg_invalid (connection,
+                                          
TALER_EC_REFRESH_REVEAL_NEW_DENOMS_ARRAY_SIZE_EXCESSIVE,
+                                           "new_denoms");
+
+  }
+  if (json_array_size (new_denoms_h_json) !=
+      json_array_size (coin_evs))
+  {
+    GNUNET_break_op (0);
+    return TEH_RESPONSE_reply_arg_invalid (connection,
+                                          
TALER_EC_REFRESH_REVEAL_NEW_DENOMS_ARRAY_SIZE_MISSMATCH,
+                                           "new_denoms/coin_evs");
+  }
+
+  /* Parse transfer private keys array */
+  for (unsigned int i=0;i<num_tprivs;i++)
   {
-    struct GNUNET_JSON_Specification tp_spec[] = {
-      GNUNET_JSON_spec_fixed_auto (NULL, &rc.transfer_privs[i]),
+    struct GNUNET_JSON_Specification trans_spec[] = {
+      GNUNET_JSON_spec_fixed_auto (NULL, &rctx->transfer_privs[i]),
       GNUNET_JSON_spec_end ()
     };
     int res;
 
     res = TEH_PARSE_json_array (connection,
                                 tp_json,
-                                tp_spec,
+                                trans_spec,
                                 i,
-                               -1);
-    GNUNET_break_op (GNUNET_OK == res);
+                                -1);
     if (GNUNET_OK != res)
-      return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
+      return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
   }
-  if (GNUNET_OK !=
-      TEH_DB_run_transaction (connection,
-                             &mhd_ret,
-                             &refresh_reveal_transaction,
-                             &rc))
+
+  /* Resolve denomination hashes */
   {
-    cleanup_rc (&rc);
-    return mhd_ret;
+    const struct TALER_EXCHANGEDB_DenominationKeyIssueInformation 
*dkis[num_fresh_coins];
+    struct TALER_RefreshCoinData rcds[num_fresh_coins];
+    int res;
+
+    /* Resolve denomination hashes */
+    key_state = TEH_KS_acquire ();
+    if (NULL == key_state)
+    {
+      TALER_LOG_ERROR ("Lacking keys to operate\n");
+      /* FIXME: use correct EC code! */
+      return TEH_RESPONSE_reply_internal_error (connection,
+                                                
TALER_EC_REFRESH_REVEAL_SIGNING_ERROR,
+                                                "exchange lacks keys");
+    }
+
+    /* Parse denomination key hashes */
+    for (unsigned int i=0;i<num_fresh_coins;i++)
+    {
+      struct GNUNET_HashCode dpk_h;
+      struct GNUNET_JSON_Specification spec[] = {
+        GNUNET_JSON_spec_fixed_auto (NULL,
+                                     &dpk_h),
+        GNUNET_JSON_spec_end ()
+      };
+
+      res = TEH_PARSE_json_array (connection,
+                                  new_denoms_h_json,
+                                  spec,
+                                  i,
+                                  -1);
+      if (GNUNET_OK != res)
+      {
+        TEH_KS_release (key_state);
+        return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
+      }
+      dkis[i] = TEH_KS_denomination_key_lookup_by_hash (key_state,
+                                                        &dpk_h,
+                                                        TEH_KS_DKU_WITHDRAW);
+      if (NULL == dkis[i])
+      {
+        TEH_KS_release (key_state);
+        return TEH_RESPONSE_reply_arg_invalid (connection,
+                                               
TALER_EC_REFRESH_REVEAL_FRESH_DENOMINATION_KEY_NOT_FOUND,
+                                               "new_denoms");
+      }
+    }
+
+    /* Parse coin envelopes */
+    for (unsigned int i=0;i<num_fresh_coins;i++)
+    {
+      struct TALER_RefreshCoinData *rcd = &rcds[i];
+      struct GNUNET_JSON_Specification spec[] = {
+        GNUNET_JSON_spec_varsize (NULL,
+                                  (void **) &rcd->coin_ev,
+                                  &rcd->coin_ev_size),
+        GNUNET_JSON_spec_end ()
+      };
+
+      res = TEH_PARSE_json_array (connection,
+                                  coin_evs,
+                                  spec,
+                                  i,
+                                  -1);
+      if (GNUNET_OK != res)
+      {
+        for (unsigned int j=0;j<i;j++)
+          GNUNET_free_non_null (rcds[j].coin_ev);
+        TEH_KS_release (key_state);
+        return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
+      }
+      rcd->dk = &dkis[i]->denom_pub;
+    }
+
+    rctx->num_fresh_coins = num_fresh_coins;
+    rctx->rcds = rcds;
+    rctx->dkis = dkis;
+    /* do transactional work */
+    if (GNUNET_OK ==
+        TEH_DB_run_transaction (connection,
+                                &res,
+                                &refresh_reveal_transaction,
+                                rctx))
+    {
+      /* Generate final (positive) response */
+      GNUNET_assert (NULL != rctx->ev_sigs);
+      res = reply_refresh_reveal_success (connection,
+                                         num_fresh_coins,
+                                         rctx->ev_sigs);
+
+    }
+
+    /* free resources */
+    if (NULL != rctx->ev_sigs)
+    {
+      for (unsigned int i=0;i<num_fresh_coins;i++)
+        if (NULL != rctx->ev_sigs[i].rsa_signature)
+          GNUNET_CRYPTO_rsa_signature_free (rctx->ev_sigs[i].rsa_signature);
+      GNUNET_free (rctx->ev_sigs);
+    }
+    for (unsigned int i=0;i<num_fresh_coins;i++)
+      GNUNET_free_non_null (rcds[i].coin_ev);
+    TEH_KS_release (key_state);
+    return res;
   }
-  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
@@ -724,13 +639,18 @@ TEH_REFRESH_handler_refresh_reveal (struct 
TEH_RequestHandler *rh,
                                     const char *upload_data,
                                     size_t *upload_data_size)
 {
-  struct GNUNET_HashCode session_hash;
   int res;
   json_t *root;
+  json_t *coin_evs;
   json_t *transfer_privs;
+  json_t *new_denoms_h;
+  struct RevealContext rctx;
   struct GNUNET_JSON_Specification spec[] = {
-    GNUNET_JSON_spec_fixed_auto ("session_hash", &session_hash),
+    GNUNET_JSON_spec_fixed_auto ("rc", &rctx.rc),
+    GNUNET_JSON_spec_fixed_auto ("transfer_pub", &rctx.gamma_tp),
     GNUNET_JSON_spec_json ("transfer_privs", &transfer_privs),
+    GNUNET_JSON_spec_json ("coin_evs", &coin_evs),
+    GNUNET_JSON_spec_json ("new_denoms_h", &new_denoms_h),
     GNUNET_JSON_spec_end ()
   };
 
@@ -745,6 +665,9 @@ TEH_REFRESH_handler_refresh_reveal (struct 
TEH_RequestHandler *rh,
        (NULL == root) )
     return MHD_YES;
 
+  memset (&rctx,
+          0,
+          sizeof (rctx));
   res = TEH_PARSE_json_data (connection,
                              root,
                              spec);
@@ -754,7 +677,8 @@ TEH_REFRESH_handler_refresh_reveal (struct 
TEH_RequestHandler *rh,
     GNUNET_break_op (0);
     return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
   }
-  /* Determine dimensionality of the request (kappa and #old coins) */
+
+  /* Check we got enough transfer private keys */
   /* Note we do +1 as 1 row (cut-and-choose!) is missing! */
   if (TALER_CNC_KAPPA != json_array_size (transfer_privs) + 1)
   {
@@ -765,8 +689,10 @@ TEH_REFRESH_handler_refresh_reveal (struct 
TEH_RequestHandler *rh,
                                            "transfer_privs");
   }
   res = handle_refresh_reveal_json (connection,
-                                    &session_hash,
-                                    transfer_privs);
+                                    &rctx,
+                                    transfer_privs,
+                                    new_denoms_h,
+                                    coin_evs);
   GNUNET_JSON_parse_free (spec);
   return res;
 }
diff --git a/src/exchange/taler-exchange-httpd_responses.c 
b/src/exchange/taler-exchange-httpd_responses.c
index ac86416..8965e2a 100644
--- a/src/exchange/taler-exchange-httpd_responses.c
+++ b/src/exchange/taler-exchange-httpd_responses.c
@@ -70,15 +70,15 @@ TEH_RESPONSE_can_compress (struct MHD_Connection 
*connection)
                                    MHD_HTTP_HEADER_ACCEPT_ENCODING);
   if (NULL == ae)
     return MHD_NO;
+  if (0 == strcmp (ae,
+                   "*"))
+    return MHD_YES;
   de = strstr (ae,
               "deflate");
   if (NULL == de)
     return MHD_NO;
-  if (0 == strcmp (de,
-                   "*"))
-    return MHD_YES;
   if ( ( (de == ae) ||
-        ( de[-1] == ',') ||
+        (de[-1] == ',') ||
         (de[-1] == ' ') ) &&
        ( (de[strlen ("deflate")] == '\0') ||
         (de[strlen ("deflate")] == ',') ||
@@ -541,18 +541,18 @@ TEH_RESPONSE_compile_transaction_history (const struct 
TALER_EXCHANGEDB_Transact
 
         ms.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_MELT);
         ms.purpose.size = htonl (sizeof (struct 
TALER_RefreshMeltCoinAffirmationPS));
-        ms.session_hash = melt->session_hash;
+        ms.rc = melt->session.rc;
         TALER_amount_hton (&ms.amount_with_fee,
-                           &melt->amount_with_fee);
+                           &melt->session.amount_with_fee);
         TALER_amount_hton (&ms.melt_fee,
                            &melt->melt_fee);
-        ms.coin_pub = melt->coin.coin_pub;
+        ms.coin_pub = melt->session.coin.coin_pub;
        /* internal sanity check before we hand out a bogus sig... */
         if (GNUNET_OK !=
             GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_MELT,
                                         &ms.purpose,
-                                        &melt->coin_sig.eddsa_signature,
-                                        &melt->coin.coin_pub.eddsa_pub))
+                                        
&melt->session.coin_sig.eddsa_signature,
+                                        
&melt->session.coin.coin_pub.eddsa_pub))
        {
          GNUNET_break (0);
          json_decref (history);
@@ -563,10 +563,10 @@ TEH_RESPONSE_compile_transaction_history (const struct 
TALER_EXCHANGEDB_Transact
                       json_array_append_new (history,
                                              json_pack ("{s:s, s:o, s:o, s:o, 
s:o}",
                                                         "type", "MELT",
-                                                        "amount", 
TALER_JSON_from_amount (&melt->amount_with_fee),
+                                                        "amount", 
TALER_JSON_from_amount (&melt->session.amount_with_fee),
                                                         "melt_fee", 
TALER_JSON_from_amount (&melt->melt_fee),
-                                                        "session_hash", 
GNUNET_JSON_from_data_auto (&melt->session_hash),
-                                                        "coin_sig", 
GNUNET_JSON_from_data_auto (&melt->coin_sig))));
+                                                        "rc", 
GNUNET_JSON_from_data_auto (&melt->session.rc),
+                                                        "coin_sig", 
GNUNET_JSON_from_data_auto (&melt->session.coin_sig))));
       }
       break;
     case TALER_EXCHANGEDB_TT_REFUND:
@@ -812,7 +812,7 @@ TEH_RESPONSE_compile_reserve_history (const struct 
TALER_EXCHANGEDB_ReserveHisto
          return NULL;
        }
 
-       GNUNET_assert (0 ==
+        GNUNET_assert (0 ==
                       json_array_append_new (json_history,
                                              json_pack ("{s:s, s:o, s:o, s:o, 
s:o, s:o}",
                                                         "type", "PAYBACK",
@@ -847,7 +847,7 @@ TEH_RESPONSE_compile_reserve_history (const struct 
TALER_EXCHANGEDB_ReserveHisto
          }
        }
        ret |= 2;
-       rcc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_RESERVE_CLOSED);
+        rcc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_RESERVE_CLOSED);
        rcc.purpose.size = htonl (sizeof (struct 
TALER_ReserveCloseConfirmationPS));
        rcc.timestamp = GNUNET_TIME_absolute_hton 
(pos->details.closing->execution_date);
        TALER_amount_hton (&rcc.closing_amount,
diff --git a/src/exchange/taler-exchange-wirewatch.c 
b/src/exchange/taler-exchange-wirewatch.c
index ca7f3ba..e878594 100644
--- a/src/exchange/taler-exchange-wirewatch.c
+++ b/src/exchange/taler-exchange-wirewatch.c
@@ -117,6 +117,11 @@ static int delay;
 static int test_mode;
 
 /**
+ * Are we running from scratch and should re-process all transactions?
+ */
+static int reset_mode;
+
+/**
  * Next task to run, if any.
  */
 static struct GNUNET_SCHEDULER_Task *task;
@@ -282,6 +287,7 @@ reject_cb (void *cls,
  * the bank for the transaction history.
  *
  * @param cls closure with the `struct TALER_EXCHANGEDB_Session *`
+ * @param ec taler error code
  * @param dir direction of the transfer
  * @param row_off identification of the position at which we are querying
  * @param row_off_size number of bytes in @a row_off
@@ -290,6 +296,7 @@ reject_cb (void *cls,
  */
 static int
 history_cb (void *cls,
+            enum TALER_ErrorCode ec,
            enum TALER_BANK_Direction dir,
            const void *row_off,
            size_t row_off_size,
@@ -303,6 +310,12 @@ history_cb (void *cls,
   {
     hh = NULL;
 
+    if (TALER_EC_NONE != ec)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                  "Error fetching history: %u!\n",
+                  (unsigned int) ec);
+    }
     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
                 "End of list. Committing progress!\n");
     qs = db_plugin->commit (db_plugin->cls,
@@ -435,26 +448,29 @@ find_transfers (void *cls)
     GNUNET_SCHEDULER_shutdown ();
     return;
   }
-  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");
-    global_ret = GNUNET_SYSERR;
-    GNUNET_SCHEDULER_shutdown ();
-    return;
-  }
-  if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+  if (! reset_mode)
   {
-    /* try again */
-    db_plugin->rollback (db_plugin->cls,
-                         session);
-    task = GNUNET_SCHEDULER_add_now (&find_transfers,
-                                    NULL);
-    return;
+    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");
+      global_ret = GNUNET_SYSERR;
+      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,
@@ -527,6 +543,10 @@ main (int argc,
                               "test",
                               "run in test mode and exit when idle",
                               &test_mode),
+    GNUNET_GETOPT_option_flag ('r',
+                              "reset",
+                              "start fresh with all transactions in the 
history",
+                              &reset_mode),
     GNUNET_GETOPT_OPTION_END
   };
 
diff --git a/src/exchange/test_taler_exchange_aggregator.c 
b/src/exchange/test_taler_exchange_aggregator.c
index 0b56b48..178b5aa 100644
--- a/src/exchange/test_taler_exchange_aggregator.c
+++ b/src/exchange/test_taler_exchange_aggregator.c
@@ -440,7 +440,9 @@ do_deposit (struct Command *cmd)
                  TALER_JSON_hash (deposit.receiver_wire_account,
                                   &deposit.h_wire));
   deposit.timestamp = GNUNET_TIME_absolute_get ();
+  GNUNET_TIME_round_abs (&deposit.timestamp);
   deposit.wire_deadline = GNUNET_TIME_relative_to_absolute 
(cmd->details.deposit.wire_deadline);
+  GNUNET_TIME_round_abs (&deposit.wire_deadline);
 
   /* finally, actually perform the DB operation */
   if ( (GNUNET_OK !=
diff --git a/src/exchangedb/perf_taler_exchangedb.c 
b/src/exchangedb/perf_taler_exchangedb.c
index dcd8a4e..e2591c8 100644
--- a/src/exchangedb/perf_taler_exchangedb.c
+++ b/src/exchangedb/perf_taler_exchangedb.c
@@ -74,7 +74,7 @@ main (int argc, char ** argv)
     /* End of initialization */
     /* Reserve initialization */
     PERF_TALER_EXCHANGEDB_INIT_CMD_LOOP ("02 - init reserve loop",
-                                     NB_RESERVE_INIT),
+                                         NB_RESERVE_INIT),
     PERF_TALER_EXCHANGEDB_INIT_CMD_CREATE_RESERVE ("02 - reserve"),
     PERF_TALER_EXCHANGEDB_INIT_CMD_INSERT_RESERVE ("02 - insert",
                                                "02 - reserve"),
@@ -131,14 +131,24 @@ main (int argc, char ** argv)
     PERF_TALER_EXCHANGEDB_INIT_CMD_LOOP ("05 - refresh session init loop",
                                      NB_REFRESH_INIT),
     PERF_TALER_EXCHANGEDB_INIT_CMD_START_TRANSACTION (""),
-    PERF_TALER_EXCHANGEDB_INIT_CMD_CREATE_REFRESH_SESSION ("05 - refresh 
session"),
+    PERF_TALER_EXCHANGEDB_INIT_CMD_LOAD_ARRAY ("05 - denomination load",
+                                               "05 - refresh session init 
loop",
+                                               "01 - save denomination"),
+    PERF_TALER_EXCHANGEDB_INIT_CMD_LOAD_ARRAY ("05 - reserve load",
+                                               "05 - refresh session init 
loop",
+                                               "02 - save reserve"),
+    PERF_TALER_EXCHANGEDB_INIT_CMD_CREATE_WITHDRAW ("05 - withdraw",
+                                                    "05 - denomination load",
+                                                    "05 - reserve load"),
+    PERF_TALER_EXCHANGEDB_INIT_CMD_CREATE_REFRESH_SESSION ("05 - refresh 
session",
+                                                           "05 - withdraw"),
     PERF_TALER_EXCHANGEDB_INIT_CMD_SAVE_ARRAY ("05 - session array",
-                                           "05 - refresh session init loop",
-                                           "05 - refresh session",
-                                           NB_RESERVE_SAVE),
+                                               "05 - refresh session init 
loop",
+                                               "05 - refresh session",
+                                               NB_RESERVE_SAVE),
     PERF_TALER_EXCHANGEDB_INIT_CMD_COMMIT_TRANSACTION (""),
     PERF_TALER_EXCHANGEDB_INIT_CMD_END_LOOP ("05 - end",
-                                         "05 - refresh session init loop"),
+                                             "05 - refresh session init loop"),
     /* End of refresh session initialization */
     /* Refresh melt initialization */
     PERF_TALER_EXCHANGEDB_INIT_CMD_LOOP ("06 - refresh melt init loop",
@@ -328,9 +338,23 @@ main (int argc, char ** argv)
                                        NB_DEPOSIT_SAVE),
     PERF_TALER_EXCHANGEDB_INIT_CMD_GET_TIME ("29 - start"),
     PERF_TALER_EXCHANGEDB_INIT_CMD_LOOP ("29 - insert refresh session",
-                                     NB_REFRESH_SAVE),
+                                         NB_REFRESH_SAVE),
     PERF_TALER_EXCHANGEDB_INIT_CMD_START_TRANSACTION (""),
-    PERF_TALER_EXCHANGEDB_INIT_CMD_CREATE_REFRESH_SESSION (""),
+    PERF_TALER_EXCHANGEDB_INIT_CMD_LOAD_ARRAY ("29 - denomination load",
+                                               "29 - insert refresh session",
+                                               "01 - save denomination"),
+    PERF_TALER_EXCHANGEDB_INIT_CMD_LOAD_ARRAY ("29 - reserve load",
+                                               "29 - insert refresh session",
+                                               "02 - save reserve"),
+    PERF_TALER_EXCHANGEDB_INIT_CMD_CREATE_WITHDRAW ("29 - withdraw",
+                                                    "29 - denomination load",
+                                                    "29 - reserve load"),
+    PERF_TALER_EXCHANGEDB_INIT_CMD_CREATE_REFRESH_SESSION ("29 - refresh 
session",
+                                                           "29 - withdraw"),
+    PERF_TALER_EXCHANGEDB_INIT_CMD_SAVE_ARRAY ("29 - session array",
+                                               "29 - insert refresh session",
+                                               "29 - refresh session",
+                                               NB_RESERVE_SAVE),
     PERF_TALER_EXCHANGEDB_INIT_CMD_COMMIT_TRANSACTION (""),
     PERF_TALER_EXCHANGEDB_INIT_CMD_END_LOOP ("",
                                          "29 - insert refresh session"),
diff --git a/src/exchangedb/perf_taler_exchangedb_init.c 
b/src/exchangedb/perf_taler_exchangedb_init.c
index c46bea2..4efec39 100644
--- a/src/exchangedb/perf_taler_exchangedb_init.c
+++ b/src/exchangedb/perf_taler_exchangedb_init.c
@@ -59,15 +59,18 @@ PERF_TALER_EXCHANGEDB_denomination_init ()
 
     {/* properties */
       struct TALER_Amount amount;
+      struct GNUNET_TIME_Absolute now;
 
       properties.purpose.purpose = htonl 
(TALER_SIGNATURE_MASTER_SIGNING_KEY_VALIDITY);
       properties.purpose.size = htonl (sizeof (struct 
TALER_DenominationKeyValidityPS));
       GNUNET_CRYPTO_eddsa_key_get_public (master_prvt,
                                           &properties.master.eddsa_pub);
-      properties.start = GNUNET_TIME_absolute_hton 
(GNUNET_TIME_absolute_get());
-      properties.expire_withdraw = GNUNET_TIME_absolute_hton 
(GNUNET_TIME_absolute_get_forever_());
-      properties.expire_deposit = GNUNET_TIME_absolute_hton 
(GNUNET_TIME_absolute_get_forever_());
-      properties.expire_legal = GNUNET_TIME_absolute_hton 
(GNUNET_TIME_absolute_get_forever_());
+      now = GNUNET_TIME_absolute_get();
+      (void) GNUNET_TIME_round_abs (&now);
+      properties.start = GNUNET_TIME_absolute_hton (now);
+      properties.expire_withdraw = GNUNET_TIME_absolute_hton 
(GNUNET_TIME_UNIT_FOREVER_ABS);
+      properties.expire_deposit = GNUNET_TIME_absolute_hton 
(GNUNET_TIME_UNIT_FOREVER_ABS);
+      properties.expire_legal = GNUNET_TIME_absolute_hton 
(GNUNET_TIME_UNIT_FOREVER_ABS);
       GNUNET_assert (GNUNET_OK ==
                      TALER_string_to_amount (CURRENCY ":1.1", &amount));
       TALER_amount_hton (&properties.value, &amount);
@@ -164,7 +167,7 @@ PERF_TALER_EXCHANGEDB_reserve_init ()
                                       &reserve->reserve.pub.eddsa_pub);
   GNUNET_assert (GNUNET_OK ==
                  TALER_string_to_amount (CURRENCY ":1000", 
&reserve->reserve.balance));
-  reserve->reserve.expiry = GNUNET_TIME_absolute_get_forever_ ();
+  reserve->reserve.expiry = GNUNET_TIME_UNIT_FOREVER_ABS;
   return reserve;
 }
 
@@ -256,7 +259,9 @@ PERF_TALER_EXCHANGEDB_deposit_init (const struct 
PERF_TALER_EXCHANGEDB_Coin *coi
     GNUNET_free (eddsa_prv);
   }
   timestamp = GNUNET_TIME_absolute_get ();
+  (void) GNUNET_TIME_round_abs (&timestamp);
   refund_deadline = GNUNET_TIME_absolute_get ();
+  (void) GNUNET_TIME_round_abs (&refund_deadline);
   GNUNET_assert (GNUNET_OK ==
                  TALER_string_to_amount (CURRENCY ":1.1",
                                          &amount_with_fee));
@@ -435,56 +440,14 @@ PERF_TALER_EXCHANGEDB_coin_free (struct 
PERF_TALER_EXCHANGEDB_Coin *coin)
 
 
 /**
- * @return a randomly generated refresh session
- */
-struct TALER_EXCHANGEDB_RefreshSession *
-PERF_TALER_EXCHANGEDB_refresh_session_init ()
-{
-  struct TALER_EXCHANGEDB_RefreshSession *refresh_session;
-
-  GNUNET_assert (NULL !=
-                 (refresh_session = GNUNET_new (struct 
TALER_EXCHANGEDB_RefreshSession)));
-  refresh_session->noreveal_index = 1;
-  refresh_session->num_newcoins = 1;
-
-  return refresh_session;
-}
-
-
-/**
- * @return #GNUNET_OK if the copy was successful, #GNUNET_SYSERR if it wasn't
- */
-int
-PERF_TALER_EXCHANGEDB_refresh_session_copy (struct 
TALER_EXCHANGEDB_RefreshSession *session,
-                                            struct 
TALER_EXCHANGEDB_RefreshSession *copy)
-{
-  *copy = *session;
-  return GNUNET_OK;
-}
-
-
-/**
- * Free a refresh session
- */
-int
-PERF_TALER_EXCHANGEDB_refresh_session_free (struct 
TALER_EXCHANGEDB_RefreshSession *refresh_session)
-{
-  if (NULL == refresh_session)
-    return GNUNET_OK;
-  GNUNET_free (refresh_session);
-  return GNUNET_OK;
-}
-
-
-/**
  * Create a melt operation
  *
- * @param session the refresh session
+ * @param rc the commitment of the refresh session
  * @param dki the denomination the melted coin uses
  * @return a pointer to a #TALER_EXCHANGEDB_RefreshMelt
  */
 struct TALER_EXCHANGEDB_RefreshMelt *
-PERF_TALER_EXCHANGEDB_refresh_melt_init (struct GNUNET_HashCode *session,
+PERF_TALER_EXCHANGEDB_refresh_melt_init (struct TALER_RefreshCommitmentP *rc,
                                          struct PERF_TALER_EXCHANGEDB_Coin 
*coin)
 {
   struct TALER_EXCHANGEDB_RefreshMelt *melt;
@@ -496,12 +459,12 @@ PERF_TALER_EXCHANGEDB_refresh_melt_init (struct 
GNUNET_HashCode *session,
     struct
     {
       struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
-      struct GNUNET_HashCode session;
+      struct TALER_RefreshCommitmentP rc;
     } to_sign;
 
     to_sign.purpose.purpose = GNUNET_SIGNATURE_PURPOSE_TEST;
     to_sign.purpose.size = htonl (sizeof (to_sign));
-    to_sign.session = *session;
+    to_sign.rc = *rc;
     GNUNET_CRYPTO_eddsa_sign (&coin->priv,
                               &to_sign.purpose,
                               &coin_sig.eddsa_signature);
@@ -513,16 +476,16 @@ PERF_TALER_EXCHANGEDB_refresh_melt_init (struct 
GNUNET_HashCode *session,
                  TALER_string_to_amount (CURRENCY ":0.1",
                                          &amount_with_fee));
   melt = GNUNET_new (struct TALER_EXCHANGEDB_RefreshMelt);
-  melt->coin.coin_pub = coin->public_info.coin_pub;
-  melt->coin.denom_sig.rsa_signature =
+  melt->session.coin.coin_pub = coin->public_info.coin_pub;
+  melt->session.coin.denom_sig.rsa_signature =
     GNUNET_CRYPTO_rsa_signature_dup 
(coin->public_info.denom_sig.rsa_signature);
-  melt->coin.denom_pub.rsa_public_key =
+  melt->session.coin.denom_pub.rsa_public_key =
     GNUNET_CRYPTO_rsa_public_key_dup 
(coin->public_info.denom_pub.rsa_public_key);
-  GNUNET_assert (NULL != melt->coin.denom_pub.rsa_public_key);
-  GNUNET_assert (NULL != melt->coin.denom_sig.rsa_signature);
-  melt->coin_sig = coin_sig;
-  melt->session_hash = *session;
-  melt->amount_with_fee = amount;
+  GNUNET_assert (NULL != melt->session.coin.denom_pub.rsa_public_key);
+  GNUNET_assert (NULL != melt->session.coin.denom_sig.rsa_signature);
+  melt->session.coin_sig = coin_sig;
+  melt->session.rc = *rc;
+  melt->session.amount_with_fee = amount;
   melt->melt_fee = amount_with_fee;
   return melt;
 }
@@ -541,9 +504,9 @@ PERF_TALER_EXCHANGEDB_refresh_melt_copy (const struct 
TALER_EXCHANGEDB_RefreshMe
 
   copy = GNUNET_new (struct TALER_EXCHANGEDB_RefreshMelt);
   *copy = *melt;
-  copy->coin.denom_sig.rsa_signature =
-    GNUNET_CRYPTO_rsa_signature_dup (melt->coin.denom_sig.rsa_signature);
-  GNUNET_assert (NULL != copy->coin.denom_sig.rsa_signature);
+  copy->session.coin.denom_sig.rsa_signature =
+    GNUNET_CRYPTO_rsa_signature_dup 
(melt->session.coin.denom_sig.rsa_signature);
+  GNUNET_assert (NULL != copy->session.coin.denom_sig.rsa_signature);
 
   return copy;
 }
@@ -558,51 +521,7 @@ PERF_TALER_EXCHANGEDB_refresh_melt_copy (const struct 
TALER_EXCHANGEDB_RefreshMe
 int
 PERF_TALER_EXCHANGEDB_refresh_melt_free (struct TALER_EXCHANGEDB_RefreshMelt 
*melt)
 {
-  GNUNET_CRYPTO_rsa_signature_free (melt->coin.denom_sig.rsa_signature);
+  GNUNET_CRYPTO_rsa_signature_free 
(melt->session.coin.denom_sig.rsa_signature);
   GNUNET_free (melt);
   return GNUNET_OK;
 }
-
-
-/**
- * Create a #TALER_EXCHANGEDB_RefreshCommitCoin
- */
-struct TALER_EXCHANGEDB_RefreshCommitCoin *
-PERF_TALER_EXCHANGEDB_refresh_commit_coin_init ()
-{
-  struct TALER_EXCHANGEDB_RefreshCommitCoin *commit_coin;
-
-  commit_coin = GNUNET_new (struct TALER_EXCHANGEDB_RefreshCommitCoin);
-  commit_coin->coin_ev = "coin_ev";
-  commit_coin->coin_ev_size = 8;
-  return commit_coin;
-}
-
-
-/**
- * Copies a #TALER_EXCHANGEDB_RefreshCommitCoin
- *
- * @param commit_coin the commit to copy
- * @return a copy of @a commit_coin
- */
-struct TALER_EXCHANGEDB_RefreshCommitCoin *
-PERF_TALER_EXCHANGEDB_refresh_commit_coin_copy (struct 
TALER_EXCHANGEDB_RefreshCommitCoin *commit_coin)
-{
-  struct TALER_EXCHANGEDB_RefreshCommitCoin *copy;
-
-  copy = GNUNET_new (struct TALER_EXCHANGEDB_RefreshCommitCoin);
-  *copy = *commit_coin;
-  return copy;
-}
-
-
-/**
- * Free a #TALER_EXCHANGEDB_RefreshCommitCoin
- *
- * @param commit_coin the coin to free
- */
-void
-PERF_TALER_EXCHANGEDB_refresh_commit_coin_free (struct 
TALER_EXCHANGEDB_RefreshCommitCoin *commit_coin)
-{
-  GNUNET_free (commit_coin);
-}
diff --git a/src/exchangedb/perf_taler_exchangedb_init.h 
b/src/exchangedb/perf_taler_exchangedb_init.h
index 11b2f16..a1f2559 100644
--- a/src/exchangedb/perf_taler_exchangedb_init.h
+++ b/src/exchangedb/perf_taler_exchangedb_init.h
@@ -44,7 +44,7 @@ struct PERF_TALER_EXCHANGEDB_Reserve
 
 
 /**
- * All informations about a coin 
+ * All informations about a coin
  */
 struct PERF_TALER_EXCHANGEDB_Coin
 {
@@ -169,42 +169,20 @@ PERF_TALER_EXCHANGEDB_coin_free (struct 
PERF_TALER_EXCHANGEDB_Coin *coin);
 
 
 /**
- * @return a randomly generated refresh session
- */
-struct TALER_EXCHANGEDB_RefreshSession *
-PERF_TALER_EXCHANGEDB_refresh_session_init (void);
-
-
-/**
- * @return #GNUNET_OK if the copy was successful, #GNUNET_SYSERR if it wasn't
- */
-int
-PERF_TALER_EXCHANGEDB_refresh_session_copy (struct 
TALER_EXCHANGEDB_RefreshSession *session, 
-                                           struct 
TALER_EXCHANGEDB_RefreshSession *copy);
-
-
-/**
- * Frees memory of a refresh_session
- */
-int
-PERF_TALER_EXCHANGEDB_refresh_session_free (struct 
TALER_EXCHANGEDB_RefreshSession *refresh_session);
-
-
-/**
  * Create a melt operation
  *
- * @param session the refresh session 
+ * @param rc the commitment of the refresh session
  * @param dki the denomination the melted coin uses
- * @return a pointer to a #TALER_EXCHANGEDB_RefreshMelt 
+ * @return a pointer to a #TALER_EXCHANGEDB_RefreshMelt
  */
 struct TALER_EXCHANGEDB_RefreshMelt *
-PERF_TALER_EXCHANGEDB_refresh_melt_init (struct GNUNET_HashCode *session,
-                                     struct PERF_TALER_EXCHANGEDB_Coin *coin);
+PERF_TALER_EXCHANGEDB_refresh_melt_init (struct TALER_RefreshCommitmentP *rc,
+                                         struct PERF_TALER_EXCHANGEDB_Coin 
*coin);
 
 
 /**
  * Copies the internals of a #TALER_EXCHANGEDB_RefreshMelt
- * 
+ *
  * @param melt the refresh melt to copy
  * @return an copy of @ melt
  */
@@ -221,30 +199,4 @@ PERF_TALER_EXCHANGEDB_refresh_melt_copy (const struct 
TALER_EXCHANGEDB_RefreshMe
 int
 PERF_TALER_EXCHANGEDB_refresh_melt_free (struct TALER_EXCHANGEDB_RefreshMelt 
*melt);
 
-
-/**
- * Create a #TALER_EXCHANGEDB_RefreshCommitCoin
- */
-struct TALER_EXCHANGEDB_RefreshCommitCoin *
-PERF_TALER_EXCHANGEDB_refresh_commit_coin_init (void);
-
-
-/**
- * Copies a #TALER_EXCHANGEDB_RefreshCommitCoin
- *
- * @param commit_coin the commit to copy
- * @return a copy of @a commit_coin
- */
-struct TALER_EXCHANGEDB_RefreshCommitCoin *
-PERF_TALER_EXCHANGEDB_refresh_commit_coin_copy (struct 
TALER_EXCHANGEDB_RefreshCommitCoin *commit_coin);
-
-
-/**
- * Free a #TALER_EXCHANGEDB_RefreshCommitCoin
- *
- * @param commit_coin the coin to free
- */
-void
-PERF_TALER_EXCHANGEDB_refresh_commit_coin_free (struct 
TALER_EXCHANGEDB_RefreshCommitCoin *commit_coin);
-
 #endif
diff --git a/src/exchangedb/perf_taler_exchangedb_interpreter.c 
b/src/exchangedb/perf_taler_exchangedb_interpreter.c
index 7ec958c..b9bf9c3 100644
--- a/src/exchangedb/perf_taler_exchangedb_interpreter.c
+++ b/src/exchangedb/perf_taler_exchangedb_interpreter.c
@@ -59,50 +59,40 @@ data_free (struct PERF_TALER_EXCHANGEDB_Data *data)
 {
   switch (data->type)
   {
-    case PERF_TALER_EXCHANGEDB_TIME:
-      if (NULL == data->data.time)
-        break;
-      GNUNET_free (data->data.time);
-      data->data.time = NULL;
-      break;
-
-    case PERF_TALER_EXCHANGEDB_DEPOSIT:
-      if (NULL == data->data.deposit)
-        break;
-      PERF_TALER_EXCHANGEDB_deposit_free (data->data.deposit);
-      data->data.deposit = NULL;
-      break;
-
-    case PERF_TALER_EXCHANGEDB_COIN:
-      if (NULL == data->data.coin)
-        break;
-      PERF_TALER_EXCHANGEDB_coin_free (data->data.coin);
-      data->data.coin = NULL;
+  case PERF_TALER_EXCHANGEDB_TIME:
+    if (NULL == data->data.time)
       break;
-
-    case PERF_TALER_EXCHANGEDB_RESERVE:
-      if (NULL == data->data.reserve)
-        break;
-      PERF_TALER_EXCHANGEDB_reserve_free (data->data.reserve);
-      data->data.reserve = NULL;
+    GNUNET_free (data->data.time);
+    data->data.time = NULL;
+    break;
+  case PERF_TALER_EXCHANGEDB_DEPOSIT:
+    if (NULL == data->data.deposit)
       break;
-
-    case PERF_TALER_EXCHANGEDB_DENOMINATION_INFO:
-      if (NULL == data->data.dki)
-        break;
-      PERF_TALER_EXCHANGEDB_denomination_free (data->data.dki);
-      data->data.dki = NULL;
+    PERF_TALER_EXCHANGEDB_deposit_free (data->data.deposit);
+    data->data.deposit = NULL;
+    break;
+  case PERF_TALER_EXCHANGEDB_COIN:
+    if (NULL == data->data.coin)
       break;
-
-    case PERF_TALER_EXCHANGEDB_REFRESH_HASH:
-      if (NULL == data->data.session_hash)
-        break;
-      GNUNET_free (data->data.session_hash);
-      data->data.session_hash = NULL;
+    GNUNET_free (data->data.coin);
+    data->data.coin = NULL;
+    break;
+  case PERF_TALER_EXCHANGEDB_RESERVE:
+    if (NULL == data->data.reserve)
       break;
-
-    case PERF_TALER_EXCHANGEDB_NONE:
+    PERF_TALER_EXCHANGEDB_reserve_free (data->data.reserve);
+    data->data.reserve = NULL;
+    break;
+  case PERF_TALER_EXCHANGEDB_DENOMINATION_INFO:
+    if (NULL == data->data.dki)
       break;
+    PERF_TALER_EXCHANGEDB_denomination_free (data->data.dki);
+    data->data.dki = NULL;
+    break;
+  case PERF_TALER_EXCHANGEDB_REFRESH_HASH:
+    break;
+  case PERF_TALER_EXCHANGEDB_NONE:
+    break;
   }
 }
 
@@ -124,33 +114,25 @@ data_copy (const struct PERF_TALER_EXCHANGEDB_Data *data,
       copy->data.time = GNUNET_new (struct GNUNET_TIME_Absolute);
       *copy->data.time = *data->data.time;
       return;
-
     case PERF_TALER_EXCHANGEDB_DEPOSIT:
       copy->data.deposit
         = PERF_TALER_EXCHANGEDB_deposit_copy (data->data.deposit);
       return;
-
     case PERF_TALER_EXCHANGEDB_COIN:
       copy->data.coin
         = PERF_TALER_EXCHANGEDB_coin_copy (data->data.coin);
       return;
-
     case PERF_TALER_EXCHANGEDB_RESERVE:
       copy->data.reserve
         = PERF_TALER_EXCHANGEDB_reserve_copy (data->data.reserve);
       return;
-
     case PERF_TALER_EXCHANGEDB_DENOMINATION_INFO:
       copy->data.dki
         = PERF_TALER_EXCHANGEDB_denomination_copy (data->data.dki);
       return;
-
     case PERF_TALER_EXCHANGEDB_REFRESH_HASH:
-      copy-> data.session_hash = GNUNET_new (struct GNUNET_HashCode);
-      *copy->data.session_hash
-        = *data->data.session_hash;
+      copy->data.rc = data->data.rc;
       break;
-
     case PERF_TALER_EXCHANGEDB_NONE:
       break;
   }
@@ -210,9 +192,10 @@ cmd_init (struct PERF_TALER_EXCHANGEDB_Cmd cmd[])
           if (PERF_TALER_EXCHANGEDB_CMD_LOOP != cmd[ret].command)
           {
             GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                        "%d:Wrong type reference to %s\n",
+                        "%d:Wrong type reference to %s at %s\n",
                         i,
-                        cmd[i].details.end_loop.label_loop);
+                        cmd[i].details.end_loop.label_loop,
+                        cmd[i].label);
             return GNUNET_SYSERR;
           }
           cmd[i].details.end_loop.index_loop = ret;
@@ -228,17 +211,19 @@ cmd_init (struct PERF_TALER_EXCHANGEDB_Cmd cmd[])
           if (GNUNET_SYSERR == ret)
           {
             GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                        "%d:Undefined reference to %s\n",
+                        "%d:Undefined reference to %s at %s\n",
                         i,
-                        cmd[i].details.save_array.label_save);
+                        cmd[i].details.save_array.label_save,
+                        cmd[i].label);
             return GNUNET_SYSERR;
           }
           if (PERF_TALER_EXCHANGEDB_NONE == cmd[ret].exposed.type)
           {
             GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                        "%d:Wrong type reference to %s\n",
+                        "%d:Wrong type reference to %s at %s\n",
                         i,
-                        cmd[i].details.save_array.label_save);
+                        cmd[i].details.save_array.label_save,
+                        cmd[i].label);
             return GNUNET_SYSERR;
           }
           cmd[i].details.save_array.index_save = ret;
@@ -248,17 +233,19 @@ cmd_init (struct PERF_TALER_EXCHANGEDB_Cmd cmd[])
           if (GNUNET_SYSERR == ret)
           {
             GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                        "%d:Undefined reference to %s\n",
+                        "%d:Undefined reference to %s at %s\n",
                         i,
-                        cmd[i].details.save_array.label_loop);
+                        cmd[i].details.save_array.label_loop,
+                        cmd[i].label);
             return GNUNET_SYSERR;
           }
           if (PERF_TALER_EXCHANGEDB_CMD_LOOP != cmd[ret].command)
           {
             GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                        "%d:Wrong type reference to %s\n",
+                        "%d:Wrong type reference to %s at %s\n",
                         i,
-                        cmd[i].details.save_array.label_loop);
+                        cmd[i].details.save_array.label_loop,
+                        cmd[i].label);
             return GNUNET_SYSERR;
           }
           cmd[i].details.save_array.index_loop = ret;
@@ -281,17 +268,19 @@ cmd_init (struct PERF_TALER_EXCHANGEDB_Cmd cmd[])
           if (GNUNET_SYSERR == ret)
           {
             GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                        "%d:Undefined reference to %s\n",
+                        "%d:Undefined reference to %s at %s\n",
                         i,
-                        cmd[i].details.load_array.label_save);
+                        cmd[i].details.load_array.label_save,
+                        cmd[i].label);
             return GNUNET_SYSERR;
           }
           if (PERF_TALER_EXCHANGEDB_CMD_SAVE_ARRAY != cmd[ret].command)
           {
             GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                        "%d:Wrong type reference to %s\n",
+                        "%d:Wrong type reference to %s at %s\n",
                         i,
-                        cmd[i].details.load_array.label_save);
+                        cmd[i].details.load_array.label_save,
+                        cmd[i].label);
             return GNUNET_SYSERR;
           }
           cmd[i].details.load_array.index_save = ret;
@@ -732,188 +721,73 @@ cmd_init (struct PERF_TALER_EXCHANGEDB_Cmd cmd[])
         }
         break;
 
-      case PERF_TALER_EXCHANGEDB_CMD_GET_REFRESH_SESSION:
-        {
-          int ret;
-
-          ret = cmd_find (cmd,
-                          cmd[i].details.get_refresh_session.label_hash);
-          if (GNUNET_SYSERR == ret)
-          {
-            GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                        "%d:Undefined reference to %s\n",
-                        i,
-                        cmd[i].details.get_refresh_session.label_hash);
-            return GNUNET_SYSERR;
-          }
-          if (PERF_TALER_EXCHANGEDB_REFRESH_HASH != cmd[ret].exposed.type)
-          {
-            GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                        "%d:Wrong type reference to %s\n",
-                        i,
-                        cmd[i].details.get_refresh_session.label_hash);
-            return GNUNET_SYSERR;
-          }
-          cmd[i].details.get_refresh_session.index_hash = ret;
-        }
-        break;
-
-      case PERF_TALER_EXCHANGEDB_CMD_INSERT_REFRESH_ORDER:
+       case PERF_TALER_EXCHANGEDB_CMD_CREATE_REFRESH_SESSION:
         {
           int ret;
-          ret = cmd_find (cmd,
-                          cmd[i].details.insert_refresh_order.label_hash);
-          if (GNUNET_SYSERR == ret)
-          {
-            GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                        "%d:Undefined reference to %s\n",
-                        i,
-                        cmd[i].details.insert_refresh_order.label_hash);
-            return GNUNET_SYSERR;
-          }
-          if (PERF_TALER_EXCHANGEDB_REFRESH_HASH != cmd[ret].exposed.type)
-          {
-            GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                        "%d:Wrong type reference to %s\n",
-                        i,
-                        cmd[i].details.insert_refresh_order.label_hash);
-            return GNUNET_SYSERR;
-          }
-          cmd[i].details.insert_refresh_order.index_hash = ret;
 
           ret = cmd_find (cmd,
-                          cmd[i].details.insert_refresh_order.label_denom);
+                          cmd[i].details.create_refresh_session.label_coin);
           if (GNUNET_SYSERR == ret)
           {
             GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                        "%d:Undefined reference to %s\n",
+                        "%d:Undefined reference to %s at %s\n",
                         i,
-                        cmd[i].details.insert_refresh_order.label_denom);
+                        cmd[i].details.create_refresh_session.label_coin,
+                        cmd[i].label);
             return GNUNET_SYSERR;
           }
-          if (PERF_TALER_EXCHANGEDB_DENOMINATION_INFO != cmd[ret].exposed.type)
+          if (PERF_TALER_EXCHANGEDB_COIN != cmd[ret].exposed.type)
           {
             GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                        "%d:Wrong type reference to %s\n",
+                        "%d:Wrong type reference to %s at %s\n",
                         i,
-                        cmd[i].details.insert_refresh_order.label_denom);
+                        cmd[i].details.create_refresh_session.label_coin,
+                        cmd[i].label);
             return GNUNET_SYSERR;
           }
-          cmd[i].details.insert_refresh_order.index_denom = ret;
+          cmd[i].details.create_refresh_session.index_coin = ret;
         }
         break;
 
-      case PERF_TALER_EXCHANGEDB_CMD_GET_REFRESH_ORDER:
+       case PERF_TALER_EXCHANGEDB_CMD_GET_REFRESH_SESSION:
         {
           int ret;
-          ret = cmd_find (cmd,
-                          cmd[i].details.get_refresh_order.label_hash);
-          if (GNUNET_SYSERR == ret)
-          {
-            GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                        "%d:Undefined reference to %s\n",
-                        i,
-                        cmd[i].details.get_refresh_order.label_hash);
-            return GNUNET_SYSERR;
-          }
-          if (PERF_TALER_EXCHANGEDB_REFRESH_HASH != cmd[ret].exposed.type)
-          {
-            GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                        "%d:Wrong type reference to %s\n",
-                        i,
-                        cmd[i].details.get_refresh_order.label_hash);
-            return GNUNET_SYSERR;
-          }
-          cmd[i].details.get_refresh_order.index_hash = ret;
-        }
-        break;
-
-      case PERF_TALER_EXCHANGEDB_CMD_INSERT_REFRESH_COMMIT_COIN:
-       {
-          int ret;
-          ret = cmd_find (cmd,
-                          
cmd[i].details.insert_refresh_commit_coin.label_hash);
-          if (GNUNET_SYSERR == ret)
-          {
-            GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                        "%d:Undefined reference to %s\n",
-                        i,
-                        cmd[i].details.insert_refresh_commit_coin.label_hash);
-            return GNUNET_SYSERR;
-          }
-          if (PERF_TALER_EXCHANGEDB_REFRESH_HASH != cmd[ret].exposed.type)
-          {
-            GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                        "%d:Wrong type reference to %s\n",
-                        i,
-                        cmd[i].details.insert_refresh_commit_coin.label_hash);
-            return GNUNET_SYSERR;
-          }
-          cmd[i].details.insert_refresh_commit_coin.index_hash = ret;
-        }
-       break;
-
-      case PERF_TALER_EXCHANGEDB_CMD_GET_REFRESH_COMMIT_COIN:
-       {
-          int ret;
-          ret = cmd_find (cmd,
-                          cmd[i].details.get_refresh_commit_coin.label_hash);
-          if (GNUNET_SYSERR == ret)
-          {
-            GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                        "%d:Undefined reference to %s\n",
-                        i,
-                        cmd[i].details.get_refresh_commit_coin.label_hash);
-            return GNUNET_SYSERR;
-          }
-          if (PERF_TALER_EXCHANGEDB_REFRESH_HASH != cmd[ret].exposed.type)
-          {
-            GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                        "%d:Wrong type reference to %s\n",
-                        i,
-                        cmd[i].details.get_refresh_commit_coin.label_hash);
-            return GNUNET_SYSERR;
-          }
-          cmd[i].details.get_refresh_commit_coin.index_hash = ret;
-        }
-        break;
 
-      case PERF_TALER_EXCHANGEDB_CMD_INSERT_REFRESH_COMMIT_LINK:
-       {
-          int ret;
           ret = cmd_find (cmd,
-                          
cmd[i].details.insert_refresh_commit_link.label_hash);
+                          cmd[i].details.get_refresh_session.label_hash);
           if (GNUNET_SYSERR == ret)
           {
             GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                        "%d:Undefined reference to %s\n",
+                        "%d:Undefined reference to %s at %s\n",
                         i,
-                        cmd[i].details.insert_refresh_commit_link.label_hash);
+                        cmd[i].details.get_refresh_session.label_hash,
+                        cmd[i].label);
             return GNUNET_SYSERR;
           }
           if (PERF_TALER_EXCHANGEDB_REFRESH_HASH != cmd[ret].exposed.type)
           {
             GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                        "%d:Wrong type reference to %s\n",
+                        "%d:Wrong type reference to %s at %s\n",
                         i,
-                        cmd[i].details.insert_refresh_commit_link.label_hash);
+                        cmd[i].details.get_refresh_session.label_hash,
+                        cmd[i].label);
             return GNUNET_SYSERR;
           }
-          cmd[i].details.insert_refresh_commit_link.index_hash = ret;
+          cmd[i].details.get_refresh_session.index_hash = ret;
         }
         break;
 
-      case PERF_TALER_EXCHANGEDB_CMD_GET_REFRESH_COMMIT_LINK:
-       {
+      case PERF_TALER_EXCHANGEDB_CMD_INSERT_REFRESH_REVEAL:
+        {
           int ret;
           ret = cmd_find (cmd,
-                          cmd[i].details.get_refresh_commit_link.label_hash);
+                          cmd[i].details.insert_refresh_reveal.label_hash);
           if (GNUNET_SYSERR == ret)
           {
             GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                         "%d:Undefined reference to %s\n",
                         i,
-                        cmd[i].details.get_refresh_commit_link.label_hash);
+                        cmd[i].details.insert_refresh_reveal.label_hash);
             return GNUNET_SYSERR;
           }
           if (PERF_TALER_EXCHANGEDB_REFRESH_HASH != cmd[ret].exposed.type)
@@ -921,49 +795,44 @@ cmd_init (struct PERF_TALER_EXCHANGEDB_Cmd cmd[])
             GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                         "%d:Wrong type reference to %s\n",
                         i,
-                        cmd[i].details.get_refresh_commit_link.label_hash);
+                        cmd[i].details.insert_refresh_reveal.label_hash);
             return GNUNET_SYSERR;
           }
-          cmd[i].details.get_refresh_commit_link.index_hash = ret;
-        }
-        break;
+          cmd[i].details.insert_refresh_reveal.index_hash = ret;
 
-      case PERF_TALER_EXCHANGEDB_CMD_GET_MELT_COMMITMENT:
-       {
-          int ret;
           ret = cmd_find (cmd,
-                          cmd[i].details.get_melt_commitment.label_hash);
+                          cmd[i].details.insert_refresh_reveal.label_denom);
           if (GNUNET_SYSERR == ret)
           {
             GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                         "%d:Undefined reference to %s\n",
                         i,
-                        cmd[i].details.get_melt_commitment.label_hash);
+                        cmd[i].details.insert_refresh_reveal.label_denom);
             return GNUNET_SYSERR;
           }
-          if (PERF_TALER_EXCHANGEDB_REFRESH_HASH != cmd[ret].exposed.type)
+          if (PERF_TALER_EXCHANGEDB_DENOMINATION_INFO != cmd[ret].exposed.type)
           {
             GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                         "%d:Wrong type reference to %s\n",
                         i,
-                        cmd[i].details.get_melt_commitment.label_hash);
+                        cmd[i].details.insert_refresh_reveal.label_denom);
             return GNUNET_SYSERR;
           }
-          cmd[i].details.get_melt_commitment.index_hash = ret;
+          cmd[i].details.insert_refresh_reveal.index_denom = ret;
         }
         break;
 
-      case PERF_TALER_EXCHANGEDB_CMD_INSERT_REFRESH_OUT:
-       {
+      case PERF_TALER_EXCHANGEDB_CMD_GET_REFRESH_REVEAL:
+        {
           int ret;
           ret = cmd_find (cmd,
-                          cmd[i].details.insert_refresh_out.label_hash);
+                          cmd[i].details.get_refresh_reveal.label_hash);
           if (GNUNET_SYSERR == ret)
           {
             GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                         "%d:Undefined reference to %s\n",
                         i,
-                        cmd[i].details.insert_refresh_out.label_hash);
+                        cmd[i].details.get_refresh_reveal.label_hash);
             return GNUNET_SYSERR;
           }
           if (PERF_TALER_EXCHANGEDB_REFRESH_HASH != cmd[ret].exposed.type)
@@ -971,14 +840,14 @@ cmd_init (struct PERF_TALER_EXCHANGEDB_Cmd cmd[])
             GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                         "%d:Wrong type reference to %s\n",
                         i,
-                        cmd[i].details.insert_refresh_out.label_hash);
+                        cmd[i].details.get_refresh_reveal.label_hash);
             return GNUNET_SYSERR;
           }
-          cmd[i].details.insert_refresh_out.index_hash = ret;
+          cmd[i].details.get_refresh_reveal.index_hash = ret;
         }
         break;
 
-      case PERF_TALER_EXCHANGEDB_CMD_GET_LINK_DATA_LIST:
+      case PERF_TALER_EXCHANGEDB_CMD_GET_LINK_DATA:
        {
           int ret;
           ret = cmd_find (cmd,
@@ -1003,31 +872,6 @@ cmd_init (struct PERF_TALER_EXCHANGEDB_Cmd cmd[])
         }
         break;
 
-      case PERF_TALER_EXCHANGEDB_CMD_GET_TRANSFER:
-       {
-          int ret;
-          ret = cmd_find (cmd,
-                          cmd[i].details.get_transfer.label_hash);
-          if (GNUNET_SYSERR == ret)
-          {
-            GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                        "%d:Undefined reference to %s\n",
-                        i,
-                        cmd[i].details.get_transfer.label_hash);
-            return GNUNET_SYSERR;
-          }
-          if (PERF_TALER_EXCHANGEDB_REFRESH_HASH != cmd[ret].exposed.type)
-          {
-            GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                        "%d:Wrong type reference to %s\n",
-                        i,
-                        cmd[i].details.get_transfer.label_hash);
-            return GNUNET_SYSERR;
-          }
-          cmd[i].details.get_transfer.index_hash = ret;
-        }
-        break;
-
       case PERF_TALER_EXCHANGEDB_CMD_END:
       case PERF_TALER_EXCHANGEDB_CMD_DEBUG:
       case PERF_TALER_EXCHANGEDB_CMD_LOOP:
@@ -1038,7 +882,6 @@ cmd_init (struct PERF_TALER_EXCHANGEDB_Cmd cmd[])
       case PERF_TALER_EXCHANGEDB_CMD_GET_TIME:
       case PERF_TALER_EXCHANGEDB_CMD_CREATE_DENOMINATION:
       case PERF_TALER_EXCHANGEDB_CMD_CREATE_RESERVE:
-      case PERF_TALER_EXCHANGEDB_CMD_CREATE_REFRESH_SESSION:
         break;
     }
   }
@@ -1092,12 +935,11 @@ cmd_clean (struct PERF_TALER_EXCHANGEDB_Cmd cmd[])
 static void
 interpret_end_loop (struct PERF_TALER_EXCHANGEDB_interpreter_state *state)
 {
-  unsigned int i;
   int jump;
 
   jump = state->cmd[state->i].details.end_loop.index_loop;
   // Cleaning up the memory in the loop
-  for (i = jump; i < state->i; i++)
+  for (unsigned int i = jump; i < state->i; i++)
     data_free (&state->cmd[i].exposed);
 
   state->cmd[jump].details.loop.curr_iteration++;
@@ -1230,6 +1072,29 @@ interprete_load_random (struct 
PERF_TALER_EXCHANGEDB_interpreter_state *state)
 
 
 /**
+ * Function called with information about a refresh order.
+ *
+ * @param cls closure
+ * @param rowid unique serial ID for the row in our database
+ * @param num_newcoins size of the @a rrcs array
+ * @param rrcs array of @a num_newcoins information about coins to be created
+ * @param num_tprivs number of entries in @a tprivs, should be 
#TALER_CNC_KAPPA - 1
+ * @param tprivs array of @e num_tprivs transfer private keys
+ * @param tp transfer public key information
+ */
+static void
+refresh_reveal_cb (void *cls,
+                   uint32_t num_newcoins,
+                   const struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrcs,
+                   unsigned int num_tprivs,
+                   const struct TALER_TransferPrivateKeyP *tprivs,
+                   const struct TALER_TransferPublicKeyP *tp)
+{
+  /* intentionally empty */
+}
+
+
+/**
  * Iterate over the commands, acting accordingly at each step
  *
  * @param state the current state of the interpreter
@@ -1340,10 +1205,11 @@ interpret (struct 
PERF_TALER_EXCHANGEDB_interpreter_state *state)
           deposit_index = 
state->cmd[state->i].details.insert_deposit.index_deposit;
           deposit = state->cmd[deposit_index].exposed.data.deposit;
           qs = state->plugin->insert_deposit (state->plugin->cls,
-                                                        state->session,
-                                                        deposit);
+                                              state->session,
+                                              deposit);
           GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs);
-          state->cmd[state->i].exposed.data.deposit = deposit;
+          state->cmd[state->i].exposed.data.deposit
+            = PERF_TALER_EXCHANGEDB_deposit_copy (deposit);
         }
         break;
 
@@ -1378,6 +1244,7 @@ interpret (struct PERF_TALER_EXCHANGEDB_interpreter_state 
*state)
           struct PERF_TALER_EXCHANGEDB_Reserve *reserve;
           json_t *sndr;
           uint32_t uid;
+          struct GNUNET_TIME_Absolute now;
 
           reserve_index = 
state->cmd[state->i].details.insert_reserve.index_reserve;
           reserve = state->cmd[reserve_index].exposed.data.reserve;
@@ -1388,11 +1255,13 @@ interpret (struct 
PERF_TALER_EXCHANGEDB_interpreter_state *state)
           uid = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_WEAK,
                                           UINT32_MAX);
           GNUNET_assert (NULL != sndr);
+          now = GNUNET_TIME_absolute_get ();
+          (void) GNUNET_TIME_round_abs (&now);
           ret = state->plugin->reserves_in_insert (state->plugin->cls,
                                                    state->session,
                                                    &reserve->reserve.pub,
                                                    &reserve->reserve.balance,
-                                                   GNUNET_TIME_absolute_get (),
+                                                   now,
                                                    sndr,
                                                    &uid,
                                                    sizeof (uid));
@@ -1502,8 +1371,8 @@ interpret (struct PERF_TALER_EXCHANGEDB_interpreter_state 
*state)
           coin_index = state->cmd[state->i].details.insert_withdraw.index_coin;
           coin = state->cmd[coin_index].exposed.data.coin;
           qs = state->plugin->insert_withdraw_info (state->plugin->cls,
-                                                     state->session,
-                                                     &coin->blind);
+                                                    state->session,
+                                                    &coin->blind);
           GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs);
         }
         break;
@@ -1546,143 +1415,100 @@ interpret (struct 
PERF_TALER_EXCHANGEDB_interpreter_state *state)
 
       case PERF_TALER_EXCHANGEDB_CMD_CREATE_REFRESH_SESSION:
         {
-          struct GNUNET_HashCode *hash;
-          struct TALER_EXCHANGEDB_RefreshSession *refresh_session;
+          struct TALER_EXCHANGEDB_RefreshSession refresh_session;
+          unsigned int coin_index;
+          struct PERF_TALER_EXCHANGEDB_Coin *coin;
 
-          hash = GNUNET_new (struct GNUNET_HashCode);
-          refresh_session = PERF_TALER_EXCHANGEDB_refresh_session_init ();
+          coin_index = 
state->cmd[state->i].details.create_refresh_session.index_coin;
+          coin = state->cmd[coin_index].exposed.data.coin;
+
+          refresh_session.coin = coin->public_info;
+          GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
+                                      &refresh_session.coin_sig,
+                                      sizeof (refresh_session.coin_sig));
           GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_WEAK,
-                                            hash);
+                                            &refresh_session.rc.session_hash);
+          GNUNET_assert (GNUNET_OK ==
+                         TALER_string_to_amount (CURRENCY ":1.1",
+                                                 
&refresh_session.amount_with_fee));
+          refresh_session.noreveal_index = 1;
           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);
+                        state->plugin->insert_melt (state->session,
+                                                     state->session,
+                                                     &refresh_session));
+          state->cmd[state->i].exposed.data.rc = refresh_session.rc;
         }
         break;
 
       case PERF_TALER_EXCHANGEDB_CMD_GET_REFRESH_SESSION:
         {
           unsigned int hash_index;
-          struct GNUNET_HashCode *hash;
-          struct TALER_EXCHANGEDB_RefreshSession refresh;
+          const struct TALER_RefreshCommitmentP *rc;
+          struct TALER_EXCHANGEDB_RefreshMelt refresh;
 
           hash_index = 
state->cmd[state->i].details.get_refresh_session.index_hash;
-          hash = state->cmd[hash_index].exposed.data.session_hash;
-          state->plugin->get_refresh_session (state->session,
-                                              state->session,
-                                              hash,
-                                              &refresh);
+          rc = &state->cmd[hash_index].exposed.data.rc;
+          state->plugin->get_melt (state->session,
+                                   state->session,
+                                   rc,
+                                   &refresh);
         }
         break;
 
-      case PERF_TALER_EXCHANGEDB_CMD_INSERT_REFRESH_ORDER:
+      case PERF_TALER_EXCHANGEDB_CMD_INSERT_REFRESH_REVEAL:
         {
           unsigned int hash_index;
           unsigned int denom_index;
-          struct GNUNET_HashCode *session_hash;
+          const struct TALER_RefreshCommitmentP *rc;
           struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *denom;
+          struct TALER_TransferPublicKeyP tpub;
+          struct TALER_TransferPrivateKeyP tprivs[2];
+          struct TALER_EXCHANGEDB_RefreshRevealedCoin rrc;
 
-          hash_index = 
state->cmd[state->i].details.insert_refresh_order.index_hash;
-          denom_index = 
state->cmd[state->i].details.insert_refresh_order.index_denom;
-          session_hash = state->cmd[hash_index].exposed.data.session_hash;
+          hash_index = 
state->cmd[state->i].details.insert_refresh_reveal.index_hash;
+          denom_index = 
state->cmd[state->i].details.insert_refresh_reveal.index_denom;
+          rc = &state->cmd[hash_index].exposed.data.rc;
           denom = state->cmd[denom_index].exposed.data.dki;
+          rrc.denom_pub = denom->denom_pub;
+          rrc.coin_ev = "coin_ev";
+          rrc.coin_ev_size = strlen (rrc.coin_ev) + 1;
+          GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
+                                      &rrc.coin_sig,
+                                      sizeof (struct 
TALER_CoinSpendSignatureP));
+          GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
+                                      tprivs,
+                                      sizeof (struct 
TALER_TransferPrivateKeyP) * 2);
+          GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
+                                      &tpub,
+                                      sizeof (struct 
TALER_TransferPublicKeyP));
           GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
-                        state->plugin->insert_refresh_order 
(state->plugin->cls,
-                                                             state->session,
-                                                             session_hash,
-                                                             1,
-                                                             
&denom->denom_pub));
-
+                        state->plugin->insert_refresh_reveal 
(state->plugin->cls,
+                                                               state->session,
+                                                               rc,
+                                                               1,
+                                                               &rrc,
+                                                               2,
+                                                               tprivs,
+                                                               &tpub));
         }
         break;
 
-      case PERF_TALER_EXCHANGEDB_CMD_GET_REFRESH_ORDER:
+      case PERF_TALER_EXCHANGEDB_CMD_GET_REFRESH_REVEAL:
         {
           int hash_index;
-          struct GNUNET_HashCode *hash;
-          struct TALER_DenominationPublicKey denom_pub;
-
-          hash_index = 
state->cmd[state->i].details.get_refresh_order.index_hash;
-          hash = state->cmd[hash_index].exposed.data.session_hash;
-          state->plugin->get_refresh_order (state->plugin->cls,
-                                            state->session,
-                                            hash,
-                                            1,
-                                            &denom_pub);
-        }
-        break;
-
-      case PERF_TALER_EXCHANGEDB_CMD_INSERT_REFRESH_COMMIT_COIN:
-        {
-          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 ();
-          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;
-
-      case PERF_TALER_EXCHANGEDB_CMD_GET_REFRESH_COMMIT_COIN:
-        {
-          unsigned int hash_index;
-          struct TALER_EXCHANGEDB_RefreshCommitCoin refresh_commit;
-
-          hash_index = 
state->cmd[state->i].details.insert_refresh_commit_coin.index_hash;
-          state->plugin->get_refresh_commit_coins (state->plugin->cls,
-                                                   state->session,
-                                                   
state->cmd[hash_index].exposed.data.session_hash,
-                                                   1,
-                                                   &refresh_commit);
-
-        }
-        break;
-
-      case PERF_TALER_EXCHANGEDB_CMD_INSERT_REFRESH_COMMIT_LINK:
-        {
-//          unsigned int hash_index;
-//
-//          hash_index = 
state->cmd[state->i].details.insert_refresh_commit_link.index_hash;
-        }
-        break;
+          const struct TALER_RefreshCommitmentP *rc;
 
-      case PERF_TALER_EXCHANGEDB_CMD_GET_REFRESH_COMMIT_LINK:
-        {
-          int ret;
-          unsigned int hash_index;
-          struct TALER_EXCHANGEDB_RefreshCommitCoin commit_coin;
-
-          // FIXME: this should go after the public key!
-          hash_index = 
state->cmd[state->i].details.get_refresh_commit_link.index_hash;
-          ret = state->plugin->get_refresh_commit_coins(state->plugin->cls,
-                                                        state->session,
-                                                        
state->cmd[hash_index].exposed.data.session_hash,
-                                                        1,
-                                                        &commit_coin);
-          GNUNET_assert (GNUNET_SYSERR != ret);
+          hash_index = 
state->cmd[state->i].details.get_refresh_reveal.index_hash;
+          rc = &state->cmd[hash_index].exposed.data.rc;
+          state->plugin->get_refresh_reveal (state->plugin->cls,
+                                             state->session,
+                                             rc,
+                                             &refresh_reveal_cb,
+                                             state);
         }
         break;
 
-      case PERF_TALER_EXCHANGEDB_CMD_GET_MELT_COMMITMENT:
-        break;
-
-      case PERF_TALER_EXCHANGEDB_CMD_INSERT_REFRESH_OUT:
-        break;
-
-      case PERF_TALER_EXCHANGEDB_CMD_GET_LINK_DATA_LIST:
-        break;
-
-      case PERF_TALER_EXCHANGEDB_CMD_GET_TRANSFER:
+      case PERF_TALER_EXCHANGEDB_CMD_GET_LINK_DATA:
         break;
 
     }
diff --git a/src/exchangedb/perf_taler_exchangedb_interpreter.h 
b/src/exchangedb/perf_taler_exchangedb_interpreter.h
index 169811b..60e6760 100644
--- a/src/exchangedb/perf_taler_exchangedb_interpreter.h
+++ b/src/exchangedb/perf_taler_exchangedb_interpreter.h
@@ -331,7 +331,7 @@
 }
 
 /**
- * Inserts informations about a withdrawal in the database
+ * Inserts information about a withdrawal into the database
  *
  * @exposes #PERF_TALER_EXCHANGEDB_COIN
  *
@@ -452,16 +452,18 @@
                                              _label_coin), \
   PERF_TALER_EXCHANGEDB_INIT_CMD_INSERT_DEPOSIT (_label "insert", \
                                              _label "deposit")
+
 /**
  * Insert informations about a refresh session
  * melts one coin into another
  *
  * @param _label the label of the command
  */
-#define PERF_TALER_EXCHANGEDB_INIT_CMD_CREATE_REFRESH_SESSION(_label) \
+#define PERF_TALER_EXCHANGEDB_INIT_CMD_CREATE_REFRESH_SESSION(_label, 
_label_coin)  \
 { \
   .command = PERF_TALER_EXCHANGEDB_CMD_CREATE_REFRESH_SESSION, \
   .label = _label, \
+  .details.create_refresh_session.label_coin = _label_coin, \
   .exposed.type = PERF_TALER_EXCHANGEDB_REFRESH_HASH \
 }
 
@@ -519,7 +521,7 @@ struct PERF_TALER_EXCHANGEDB_Data
     /** #PERF_TALER_EXCHANGEDB_DENOMINATION_INFO */
     struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki;
     /** #PERF_TALER_EXCHANGEDB_REFRESH_HASH */
-    struct GNUNET_HashCode *session_hash;
+    struct TALER_RefreshCommitmentP rc;
   } data;
 };
 
@@ -679,54 +681,19 @@ enum PERF_TALER_EXCHANGEDB_CMD_Name
   PERF_TALER_EXCHANGEDB_CMD_GET_REFRESH_SESSION,
 
   /**
-   * Insert a melt refresh order
-   */
-  PERF_TALER_EXCHANGEDB_CMD_INSERT_REFRESH_ORDER,
-
-  /**
-   * Get informations about a refresh order
-   */
-  PERF_TALER_EXCHANGEDB_CMD_GET_REFRESH_ORDER,
-
-  /**
-   * Insert refresh commit coin
-   */
-  PERF_TALER_EXCHANGEDB_CMD_INSERT_REFRESH_COMMIT_COIN,
-
-  /**
-   * Get refresh commit coin
-   */
-  PERF_TALER_EXCHANGEDB_CMD_GET_REFRESH_COMMIT_COIN,
-
-  /**
-   * Insert refresh commit link
-   */
-  PERF_TALER_EXCHANGEDB_CMD_INSERT_REFRESH_COMMIT_LINK,
-
-  /**
-   * Get refresh commit link
-   */
-  PERF_TALER_EXCHANGEDB_CMD_GET_REFRESH_COMMIT_LINK,
-
-  /**
-   * Get information avout the melt commit
+   * Insert a melt refresh reveal data
    */
-  PERF_TALER_EXCHANGEDB_CMD_GET_MELT_COMMITMENT,
+  PERF_TALER_EXCHANGEDB_CMD_INSERT_REFRESH_REVEAL,
 
   /**
-   * Insert a new coin into the database after a melt operation
+   * Get informations about a refresh reveal data
    */
-  PERF_TALER_EXCHANGEDB_CMD_INSERT_REFRESH_OUT,
+  PERF_TALER_EXCHANGEDB_CMD_GET_REFRESH_REVEAL,
 
   /**
    * Get the link data list of a coin
    */
-  PERF_TALER_EXCHANGEDB_CMD_GET_LINK_DATA_LIST,
-
-  /**
-   * Get the shared secret and the transfere public key
-   */
-  PERF_TALER_EXCHANGEDB_CMD_GET_TRANSFER
+  PERF_TALER_EXCHANGEDB_CMD_GET_LINK_DATA
 
 };
 
@@ -1029,6 +996,18 @@ union PERF_TALER_EXCHANGEDB_CMD_Details
   } get_deposit;
 
  /**
+   * Data requiered for the #PERF_TALER_EXCHANGEDB_CMD_CREATE_REFRESH_SESSION 
command
+   */
+  struct PERF_TALER_EXCHANGEDB_CMD_createRefreshSessionDetails
+  {
+    /**
+     * label of the source of the hash of the session
+     */
+    const char *label_coin;
+    unsigned int index_coin;
+  } create_refresh_session;
+
+   /**
    * Data requiered for the #PERF_TALER_EXCHANGEDB_CMD_GET_REFRESH_SESSION 
command
    */
   struct PERF_TALER_EXCHANGEDB_CMD_getRefreshSessionDetails
@@ -1041,9 +1020,9 @@ union PERF_TALER_EXCHANGEDB_CMD_Details
   } get_refresh_session;
 
   /**
-   * Data requiered for the #PERF_TALER_EXCHANGEDB_CMD_INSERT_REFRESH_ORDER 
command
+   * Data requiered for the #PERF_TALER_EXCHANGEDB_CMD_INSERT_REFRESH_REVEAL 
command
    */
-  struct PERF_TALER_EXCHANGEDB_CMD_insertRefreshOrderDetails
+  struct PERF_TALER_EXCHANGEDB_CMD_insertRefreshRevealDetails
   {
    /**
     * The refresh session hash
@@ -1056,12 +1035,12 @@ union PERF_TALER_EXCHANGEDB_CMD_Details
     */
    const char *label_denom;
    unsigned int index_denom;
-  } insert_refresh_order;
+  } insert_refresh_reveal;
 
   /**
-   * Data requiered for the #PERF_TALER_EXCHANGEDB_CMD_GET_REFRESH_ORDER 
command
+   * Data requiered for the #PERF_TALER_EXCHANGEDB_CMD_GET_REFRESH_REVEAL 
command
    */
-  struct PERF_TALER_EXCHANGEDB_CMD_getRefreshOrderDetails
+  struct PERF_TALER_EXCHANGEDB_CMD_getRefreshRevealDetails
   {
     /**
      * The session hash
@@ -1069,82 +1048,7 @@ union PERF_TALER_EXCHANGEDB_CMD_Details
     const char *label_hash;
     unsigned int index_hash;
 
-  } get_refresh_order;
-
-  /**
-   * Data requiered for the 
#PERF_TALER_EXCHANGEDB_CMD_INSERT_REFRESH_COMMIT_COIN command
-   */
-  struct PERF_TALER_EXCHANGEDB_CMD_insertRefreshCommitCoinDetails
-  {
-    /**
-     * The refresh session hash
-     */
-    const char *label_hash;
-    unsigned int index_hash;
-
-  } insert_refresh_commit_coin;
-
-  /**
-   * Data requiered for the #PERF_TALER_EXCHANGEDB_CMD_GET_REFRESH_COMMIT_COIN 
command
-   */
-  struct PERF_TALER_EXCHANGEDB_CMD_getRefreshCommitCoinDetails
-  {
-    /**
-     * The refresh session hash
-     */
-    const char *label_hash;
-    unsigned int index_hash;
-
-  } get_refresh_commit_coin;
-
-  /**
-   * Data requiered for the 
#PERF_TALER_EXCHANGEDB_CMD_INSERT_REFRESH_COMMIT_LINK command
-   */
-  struct PERF_TALER_EXCHANGEDB_CMD_insertRefreshCommitLinkDetails
-  {
-    /**
-     * The refresh session hash
-     */
-    const char *label_hash;
-    unsigned int index_hash;
-
-  } insert_refresh_commit_link;
-
-  /**
-   * Data requiered by the #PERF_TALER_EXCHANGEDB_CMD_GET_REFRESH_COMMIT_LINK 
command
-   */
-  struct PERF_TALER_EXCHANGEDB_CMD_getRefreshCommitLinkDetails
-  {
-    /**
-     * The refresh session hash
-     */
-    const char *label_hash;
-    unsigned int index_hash;
-  } get_refresh_commit_link;
-
-  /**
-   * Data requiered for the #PERF_TALER_EXCHANGEDB_CMD_GET_MELT_COMMITMENT 
command
-   */
-  struct PERF_TALER_EXCHANGEDB_CMD_getMeltCommitmentDaetails
-  {
-    /**
-     * The refresh session hash
-     */
-    const char *label_hash;
-    unsigned int index_hash;
-  } get_melt_commitment;
-
-  /**
-   * Data requiered by the #PERF_TALER_EXCHANGEDB_CMD_INSERT_REFRESH_OUT 
command
-   */
-  struct PERF_TALER_EXCHANGEDB_CMD_insertRefreshOutDetails
-  {
-    /**
-     * The refresh session hash
-     */
-    const char *label_hash;
-    unsigned int index_hash;
-  } insert_refresh_out;
+  } get_refresh_reveal;
 
   /**
    * Data requiered by the #PERF_TALER_EXCHANGEDB_CMD_GET_LINK_DATA_LIST 
command
@@ -1158,17 +1062,6 @@ union PERF_TALER_EXCHANGEDB_CMD_Details
     unsigned int index_hash;
   } get_link_data_list;
 
-  /**
-   * Data requiered by the #PERF_TALER_EXCHANGEDB_CMD_GET_TRANSFER command
-   */
-  struct PERF_TALER_EXCHANGEDB_CMD_getTransferDetails
-  {
-    /**
-     * The refresh session hash
-     */
-    const char *label_hash;
-    unsigned int index_hash;
-  } get_transfer;
 
 };
 
diff --git a/src/exchangedb/plugin_exchangedb_common.c 
b/src/exchangedb/plugin_exchangedb_common.c
index 61c00c1..e4b8324 100644
--- a/src/exchangedb/plugin_exchangedb_common.c
+++ b/src/exchangedb/plugin_exchangedb_common.c
@@ -74,31 +74,6 @@ common_free_reserve_history (void *cls,
 
 
 /**
- * Free memory of the link data list.
- *
- * @param cls the @e cls of this struct with the plugin-specific state (unused)
- * @param ldl link data list to release
- */
-static void
-common_free_link_data_list (void *cls,
-                            struct TALER_EXCHANGEDB_LinkDataList *ldl)
-{
-  struct TALER_EXCHANGEDB_LinkDataList *next;
-
-  while (NULL != ldl)
-  {
-    next = ldl->next;
-    if (NULL != ldl->denom_pub.rsa_public_key)
-        GNUNET_CRYPTO_rsa_public_key_free (ldl->denom_pub.rsa_public_key);
-      if (NULL != ldl->ev_sig.rsa_signature)
-        GNUNET_CRYPTO_rsa_signature_free (ldl->ev_sig.rsa_signature);
-    GNUNET_free (ldl);
-    ldl = next;
-  }
-}
-
-
-/**
  * Free linked list of transactions.
  *
  * @param cls the @e cls of this struct with the plugin-specific state (unused)
@@ -125,10 +100,10 @@ common_free_coin_transaction_list (void *cls,
       GNUNET_free (list->details.deposit);
       break;
     case TALER_EXCHANGEDB_TT_REFRESH_MELT:
-      if (NULL != list->details.melt->coin.denom_pub.rsa_public_key)
-        GNUNET_CRYPTO_rsa_public_key_free 
(list->details.melt->coin.denom_pub.rsa_public_key);
-      if (NULL != list->details.melt->coin.denom_sig.rsa_signature)
-        GNUNET_CRYPTO_rsa_signature_free 
(list->details.melt->coin.denom_sig.rsa_signature);
+      if (NULL != list->details.melt->session.coin.denom_pub.rsa_public_key)
+        GNUNET_CRYPTO_rsa_public_key_free 
(list->details.melt->session.coin.denom_pub.rsa_public_key);
+      if (NULL != list->details.melt->session.coin.denom_sig.rsa_signature)
+        GNUNET_CRYPTO_rsa_signature_free 
(list->details.melt->session.coin.denom_sig.rsa_signature);
       GNUNET_free (list->details.melt);
       break;
     case TALER_EXCHANGEDB_TT_REFUND:
diff --git a/src/exchangedb/plugin_exchangedb_postgres.c 
b/src/exchangedb/plugin_exchangedb_postgres.c
index 93a8c1a..36ae3e5 100644
--- a/src/exchangedb/plugin_exchangedb_postgres.c
+++ b/src/exchangedb/plugin_exchangedb_postgres.c
@@ -23,6 +23,7 @@
  * @author Marcello Stanisci
  */
 #include "platform.h"
+#include "taler_error_codes.h"
 #include "taler_pq_lib.h"
 #include "taler_exchangedb_plugin.h"
 #include <pthread.h>
@@ -105,12 +106,10 @@ postgres_drop_tables (void *cls)
     GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS wire_out CASCADE;"),
     GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS wire_fee CASCADE;"),
     GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS deposits CASCADE;"),
-    GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS refresh_out CASCADE;"),
-    GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS refresh_commit_coin 
CASCADE;"),
-    GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS refresh_transfer_public_key 
CASCADE;"),
     GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS refunds CASCADE;"),
-    GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS refresh_order CASCADE;"),
-    GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS refresh_sessions CASCADE;"),
+    GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS refresh_commitments 
CASCADE;"),
+    GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS refresh_revealed_coins 
CASCADE;"),
+    GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS refresh_transfer_keys 
CASCADE;"),
     GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS known_coins CASCADE;"),
     GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS reserves_close CASCADE;"),
     GNUNET_PQ_make_execute ("DROP TABLE IF EXISTS reserves_out CASCADE;"),
@@ -266,67 +265,46 @@ postgres_create_tables (void *cls)
                            ",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 "
+
+    /* Table with the commitments made when melting a coin. */
+    GNUNET_PQ_make_execute("CREATE TABLE IF NOT EXISTS refresh_commitments "
                            "(melt_serial_id BIGSERIAL UNIQUE"
-                           ",session_hash BYTEA PRIMARY KEY CHECK 
(LENGTH(session_hash)=64)"
+                           ",rc BYTEA PRIMARY KEY CHECK (LENGTH(rc)=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"
-                           ");"),
-    GNUNET_PQ_make_try_execute ("CREATE INDEX 
refresh_sessions_old_coin_pub_index ON "
-                                "refresh_sessions (old_coin_pub);"),
-
-    /* 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)"
+                           ",noreveal_index INT4 NOT NULL"
                            ");"),
-    /* Table with the commitments for the new coins that are to be created
+    GNUNET_PQ_make_try_execute ("CREATE INDEX 
refresh_commitments_old_coin_pub_index ON "
+                                "refresh_commitments (old_coin_pub);"),
+
+    /* Table with the revelations about 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"
+    GNUNET_PQ_make_execute("CREATE TABLE IF NOT EXISTS refresh_revealed_coins "
+                           "(rc BYTEA NOT NULL REFERENCES refresh_commitments 
(rc) ON DELETE CASCADE"
+                           ",newcoin_index INT4 NOT NULL"
+                           ",denom_pub_hash BYTEA NOT NULL REFERENCES 
denominations (denom_pub_hash) ON DELETE CASCADE"
                            ",coin_ev BYTEA NOT NULL"
-                           ",PRIMARY KEY (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"
-                           ",PRIMARY KEY (session_hash, newcoin_index)"
+                           ",PRIMARY KEY (rc, newcoin_index)"
+                           ");"),
+
+    /* Table with the transfer keys of a refresh operation; includes
+       the rc for which this is the link information, the
+       transfer public key (for gamma) and the revealed transfer private
+       keys (array of TALER_CNC_KAPPA - 1 entries, with gamma being skipped) */
+    GNUNET_PQ_make_execute("CREATE TABLE IF NOT EXISTS refresh_transfer_keys "
+                           "(rc BYTEA NOT NULL PRIMARY KEY REFERENCES 
refresh_commitments (rc) ON DELETE CASCADE"
+                           ",transfer_pub BYTEA NOT NULL 
CHECK(LENGTH(transfer_pub)=32)"
+                           ",transfer_privs BYTEA NOT NULL"
                            ");"),
+
     /* 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 "
@@ -726,64 +704,7 @@ postgres_prepare (PGconn *db_conn)
                             " 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. */
@@ -807,30 +728,68 @@ postgres_prepare (PGconn *db_conn)
                             ") 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 "
+
+    /* Used in #postgres_insert_melt() to store
+       high-level information about a melt operation */
+    GNUNET_PQ_make_prepare ("insert_melt",
+                            "INSERT INTO refresh_commitments "
+                            "(rc "
+                            ",old_coin_pub "
+                            ",old_coin_sig "
+                            ",amount_with_fee_val "
+                            ",amount_with_fee_frac "
+                            ",amount_with_fee_curr "
+                            ",noreveal_index "
                             ") 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 */
+                            "($1, $2, $3, $4, $5, $6, $7);",
+                            7),
+    /* Used in #postgres_get_melt() to fetch
+       high-level information about a melt operation */
+    GNUNET_PQ_make_prepare ("get_melt",
+                            "SELECT"
+                            " denom.denom_pub"
+                            ",denom.fee_refresh_val"
+                            ",denom.fee_refresh_frac"
+                            ",denom.fee_refresh_curr"
+                            ",kc.denom_sig"
+                            ",old_coin_pub"
+                            ",old_coin_sig"
+                            ",amount_with_fee_val"
+                            ",amount_with_fee_frac"
+                            ",amount_with_fee_curr"
+                            ",noreveal_index"
+                            " FROM refresh_commitments"
+                            "   JOIN known_coins kc"
+                            "     ON (refresh_commitments.old_coin_pub = 
kc.coin_pub)"
+                            "   JOIN denominations denom"
+                            "     ON (kc.denom_pub_hash = 
denom.denom_pub_hash)"
+                            " WHERE rc=$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_commitments_incr",
+                            "SELECT"
+                            " denom.denom_pub"
+                            ",old_coin_pub"
+                            ",old_coin_sig"
+                            ",amount_with_fee_val"
+                            ",amount_with_fee_frac"
+                            ",amount_with_fee_curr"
+                            ",noreveal_index"
+                            ",melt_serial_id"
+                            ",rc"
+                            " FROM refresh_commitments"
+                            "   JOIN known_coins kc"
+                            "     ON (refresh_commitments.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),
+    /* Query the 'refresh_commitments' by coin public key */
     GNUNET_PQ_make_prepare ("get_refresh_session_by_coin",
                             "SELECT"
-                            " session_hash"
+                            " rc"
                             ",old_coin_sig"
                             ",amount_with_fee_val"
                             ",amount_with_fee_frac"
@@ -838,31 +797,75 @@ postgres_prepare (PGconn *db_conn)
                             ",denom.fee_refresh_val "
                             ",denom.fee_refresh_frac "
                             ",denom.fee_refresh_curr "
-                            " FROM refresh_sessions"
+                            " FROM refresh_commitments"
                             "    JOIN known_coins "
-                            "      ON (refresh_sessions.old_coin_pub = 
known_coins.coin_pub)"
+                            "      ON (refresh_commitments.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"
+
+    /* Store information about the desired denominations for a
+       refresh operation, used in #postgres_insert_refresh_reveal() */
+    GNUNET_PQ_make_prepare ("insert_refresh_revealed_coin",
+                            "INSERT INTO refresh_revealed_coins "
+                            "(rc "
+                            ",newcoin_index "
+                            ",denom_pub_hash "
+                            ",coin_ev"
+                            ",ev_sig"
+                            ") VALUES "
+                            "($1, $2, $3, $4, $5);",
+                            5),
+    /* Obtain information about the coins created in a refresh
+       operation, used in #postgres_get_refresh_reveal() */
+    GNUNET_PQ_make_prepare ("get_refresh_revealed_coins",
+                            "SELECT "
+                            " newcoin_index"
                             ",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;",
+                            ",coin_ev"
+                            ",ev_sig"
+                            " FROM refresh_revealed_coins"
+                            "    JOIN denominations denom "
+                            "      USING (denom_pub_hash)"
+                            " WHERE rc=$1"
+                            "   ORDER BY newcoin_index ASC;",
                             1),
+
+    /* Used in #postgres_insert_refresh_reveal() to store the transfer
+       keys we learned */
+    GNUNET_PQ_make_prepare ("insert_refresh_transfer_keys",
+                            "INSERT INTO refresh_transfer_keys "
+                            "(rc"
+                            ",transfer_pub"
+                            ",transfer_privs"
+                            ") VALUES "
+                            "($1, $2, $3);",
+                            3),
+    /* Used in #postgres_get_refresh_reveal() to retrieve transfer
+       keys from /refresh/reveal */
+    GNUNET_PQ_make_prepare ("get_refresh_transfer_keys",
+                            "SELECT"
+                            " transfer_pub"
+                            ",transfer_privs"
+                            " FROM refresh_transfer_keys"
+                            " WHERE rc=$1;",
+                            1),
+
+
+    /* 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),
     /* Query the 'refunds' by coin public key */
     GNUNET_PQ_make_prepare ("get_refunds_by_coin",
                             "SELECT"
@@ -881,43 +884,26 @@ postgres_prepare (PGconn *db_conn)
                             "    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",
+    /* Fetch refunds with rowid '\geq' the given parameter */
+    GNUNET_PQ_make_prepare ("audit_get_refunds_incr",
                             "SELECT"
-                            " transfer_pub"
-                            " FROM refresh_transfer_public_key"
-                            " WHERE session_hash=$1;",
+                            " 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),
-    /* 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",
@@ -938,20 +924,6 @@ postgres_prepare (PGconn *db_conn)
                             "($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",
@@ -1113,68 +1085,22 @@ postgres_prepare (PGconn *db_conn)
                             "      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.  */
+
+    /* Used in #postgres_get_link_data(). */
     GNUNET_PQ_make_prepare ("get_link",
                             "SELECT "
-                            " ev_sig"
+                            " tp.transfer_pub"
                             ",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)"
+                            ",rrc.ev_sig"
+                            " FROM refresh_commitments"
+                            "     JOIN refresh_revealed_coins rrc"
+                            "       USING (rc)"
+                            "     JOIN refresh_transfer_keys tp"
+                            "       USING (rc)"
                             "     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;",
+                            "       ON (rrc.denom_pub_hash = 
denoms.denom_pub_hash)"
+                            " WHERE old_coin_pub=$1"
+                            " ORDER BY tp.transfer_pub",
                             1),
     /* Used in #postgres_lookup_wire_transfer */
     GNUNET_PQ_make_prepare ("lookup_transactions",
@@ -1686,10 +1612,10 @@ postgres_insert_denomination_info (void *cls,
     GNUNET_PQ_query_param_rsa_public_key (denom_pub->rsa_public_key),
     GNUNET_PQ_query_param_auto_from_type (&issue->properties.master),
     GNUNET_PQ_query_param_auto_from_type (&issue->signature),
-    GNUNET_PQ_query_param_absolute_time_nbo (&issue->properties.start),
-    GNUNET_PQ_query_param_absolute_time_nbo 
(&issue->properties.expire_withdraw),
-    GNUNET_PQ_query_param_absolute_time_nbo 
(&issue->properties.expire_deposit),
-    GNUNET_PQ_query_param_absolute_time_nbo (&issue->properties.expire_legal),
+    TALER_PQ_query_param_absolute_time_nbo (&issue->properties.start),
+    TALER_PQ_query_param_absolute_time_nbo 
(&issue->properties.expire_withdraw),
+    TALER_PQ_query_param_absolute_time_nbo (&issue->properties.expire_deposit),
+    TALER_PQ_query_param_absolute_time_nbo (&issue->properties.expire_legal),
     TALER_PQ_query_param_amount_nbo (&issue->properties.value),
     TALER_PQ_query_param_amount_nbo (&issue->properties.fee_withdraw),
     TALER_PQ_query_param_amount_nbo (&issue->properties.fee_deposit),
@@ -1744,13 +1670,13 @@ postgres_get_denomination_info (void *cls,
                                          &issue->properties.master),
     GNUNET_PQ_result_spec_auto_from_type ("master_sig",
                                          &issue->signature),
-    GNUNET_PQ_result_spec_absolute_time_nbo ("valid_from",
+    TALER_PQ_result_spec_absolute_time_nbo ("valid_from",
                                             &issue->properties.start),
-    GNUNET_PQ_result_spec_absolute_time_nbo ("expire_withdraw",
+    TALER_PQ_result_spec_absolute_time_nbo ("expire_withdraw",
                                             
&issue->properties.expire_withdraw),
-    GNUNET_PQ_result_spec_absolute_time_nbo ("expire_deposit",
+    TALER_PQ_result_spec_absolute_time_nbo ("expire_deposit",
                                             &issue->properties.expire_deposit),
-    GNUNET_PQ_result_spec_absolute_time_nbo ("expire_legal",
+    TALER_PQ_result_spec_absolute_time_nbo ("expire_legal",
                                             &issue->properties.expire_legal),
     TALER_PQ_result_spec_amount_nbo ("coin",
                                     &issue->properties.value),
@@ -1802,7 +1728,7 @@ postgres_reserve_get (void *cls,
   };
   struct GNUNET_PQ_ResultSpec rs[] = {
     TALER_PQ_result_spec_amount("current_balance", &reserve->balance),
-    GNUNET_PQ_result_spec_absolute_time("expiration_date", &reserve->expiry),
+    TALER_PQ_result_spec_absolute_time("expiration_date", &reserve->expiry),
     GNUNET_PQ_result_spec_end
   };
 
@@ -1828,7 +1754,7 @@ reserves_update (void *cls,
                  const struct TALER_EXCHANGEDB_Reserve *reserve)
 {
   struct GNUNET_PQ_QueryParam params[] = {
-    GNUNET_PQ_query_param_absolute_time (&reserve->expiry),
+    TALER_PQ_query_param_absolute_time (&reserve->expiry),
     TALER_PQ_query_param_amount (&reserve->balance),
     GNUNET_PQ_query_param_auto_from_type (&reserve->pub),
     GNUNET_PQ_query_param_end
@@ -1916,7 +1842,7 @@ postgres_reserves_in_insert (void *cls,
       GNUNET_PQ_query_param_auto_from_type (reserve_pub),
       TALER_PQ_query_param_json (sender_account_details),
       TALER_PQ_query_param_amount (balance),
-      GNUNET_PQ_query_param_absolute_time (&expiry),
+      TALER_PQ_query_param_absolute_time (&expiry),
       GNUNET_PQ_query_param_end
     };
 
@@ -1945,7 +1871,7 @@ postgres_reserves_in_insert (void *cls,
                                         wire_reference_size),
       TALER_PQ_query_param_amount (balance),
       TALER_PQ_query_param_json (sender_account_details),
-      GNUNET_PQ_query_param_absolute_time (&execution_time),
+      TALER_PQ_query_param_absolute_time (&execution_time),
       GNUNET_PQ_query_param_end
     };
 
@@ -2090,13 +2016,14 @@ postgres_insert_withdraw_info (void *cls,
     GNUNET_PQ_query_param_rsa_signature (collectable->sig.rsa_signature),
     GNUNET_PQ_query_param_auto_from_type (&collectable->reserve_pub),
     GNUNET_PQ_query_param_auto_from_type (&collectable->reserve_sig),
-    GNUNET_PQ_query_param_absolute_time (&now),
+    TALER_PQ_query_param_absolute_time (&now),
     TALER_PQ_query_param_amount (&collectable->amount_with_fee),
     GNUNET_PQ_query_param_end
   };
   enum GNUNET_DB_QueryStatus qs;
 
   now = GNUNET_TIME_absolute_get ();
+  (void) GNUNET_TIME_round_abs (&now);
   GNUNET_CRYPTO_rsa_public_key_hash (collectable->denom_pub.rsa_public_key,
                                     &denom_pub_hash);
   qs = GNUNET_PQ_eval_prepared_non_select (session->conn,
@@ -2234,7 +2161,7 @@ add_bank_to_exchange (void *cls,
                                             &bt->wire_reference_size),
        TALER_PQ_result_spec_amount ("credit",
                                     &bt->amount),
-       GNUNET_PQ_result_spec_absolute_time ("execution_date",
+       TALER_PQ_result_spec_absolute_time ("execution_date",
                                             &bt->execution_date),
        TALER_PQ_result_spec_json ("sender_account_details",
                                   &bt->sender_account_details),
@@ -2346,7 +2273,7 @@ add_payback (void *cls,
                                              &payback->coin_blind),
        GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
                                              &payback->coin_sig),
-       GNUNET_PQ_result_spec_absolute_time ("timestamp",
+       TALER_PQ_result_spec_absolute_time ("timestamp",
                                             &payback->timestamp),
        GNUNET_PQ_result_spec_rsa_public_key ("denom_pub",
                                              
&payback->coin.denom_pub.rsa_public_key),
@@ -2401,7 +2328,7 @@ add_exchange_to_bank (void *cls,
                                     &closing->amount),
        TALER_PQ_result_spec_amount ("closing_fee",
                                     &closing->closing_fee),
-       GNUNET_PQ_result_spec_absolute_time ("execution_date",
+       TALER_PQ_result_spec_absolute_time ("execution_date",
                                             &closing->execution_date),
        TALER_PQ_result_spec_json ("receiver_account",
                                   &closing->receiver_account_details),
@@ -2536,11 +2463,11 @@ postgres_have_deposit (void *cls,
   struct GNUNET_PQ_ResultSpec rs[] = {
     TALER_PQ_result_spec_amount ("amount_with_fee",
                                 &deposit2.amount_with_fee),
-    GNUNET_PQ_result_spec_absolute_time ("timestamp",
+    TALER_PQ_result_spec_absolute_time ("timestamp",
                                         &deposit2.timestamp),
-    GNUNET_PQ_result_spec_absolute_time ("refund_deadline",
+    TALER_PQ_result_spec_absolute_time ("refund_deadline",
                                         &deposit2.refund_deadline),
-    GNUNET_PQ_result_spec_absolute_time ("wire_deadline",
+    TALER_PQ_result_spec_absolute_time ("wire_deadline",
                                         &deposit2.wire_deadline),
     GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
                                          &deposit2.h_contract_terms),
@@ -2697,7 +2624,7 @@ postgres_get_ready_deposit (void *cls,
 {
   struct GNUNET_TIME_Absolute now = GNUNET_TIME_absolute_get ();
   struct GNUNET_PQ_QueryParam params[] = {
-    GNUNET_PQ_query_param_absolute_time (&now),
+    TALER_PQ_query_param_absolute_time (&now),
     GNUNET_PQ_query_param_end
   };
   struct TALER_Amount amount_with_fee;
@@ -2715,7 +2642,7 @@ postgres_get_ready_deposit (void *cls,
                                 &amount_with_fee),
     TALER_PQ_result_spec_amount ("fee_deposit",
                                 &deposit_fee),
-    GNUNET_PQ_result_spec_absolute_time ("wire_deadline",
+    TALER_PQ_result_spec_absolute_time ("wire_deadline",
                                         &wire_deadline),
     GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
                                          &h_contract_terms),
@@ -2729,6 +2656,7 @@ postgres_get_ready_deposit (void *cls,
   };
   enum GNUNET_DB_QueryStatus qs;
 
+  (void) GNUNET_TIME_round_abs (&now);
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
              "Finding ready deposits by deadline %s (%llu)\n",
              GNUNET_STRINGS_absolute_time_to_string (now),
@@ -2829,7 +2757,7 @@ match_deposit_cb (void *cls,
                                    &amount_with_fee),
       TALER_PQ_result_spec_amount ("fee_deposit",
                                    &deposit_fee),
-      GNUNET_PQ_result_spec_absolute_time ("wire_deadline",
+      TALER_PQ_result_spec_absolute_time ("wire_deadline",
                                            &wire_deadline),
       GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
                                             &h_contract_terms),
@@ -3008,7 +2936,11 @@ ensure_coin_known (struct PostgresClosure *cls,
     return GNUNET_SYSERR;
   }
   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
+  {
+    GNUNET_CRYPTO_rsa_public_key_free (known_coin.denom_pub.rsa_public_key);
+    GNUNET_CRYPTO_rsa_signature_free (known_coin.denom_sig.rsa_signature);
     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,
@@ -3042,9 +2974,9 @@ postgres_insert_deposit (void *cls,
   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),
-    GNUNET_PQ_query_param_absolute_time (&deposit->timestamp),
-    GNUNET_PQ_query_param_absolute_time (&deposit->refund_deadline),
-    GNUNET_PQ_query_param_absolute_time (&deposit->wire_deadline),
+    TALER_PQ_query_param_absolute_time (&deposit->timestamp),
+    TALER_PQ_query_param_absolute_time (&deposit->refund_deadline),
+    TALER_PQ_query_param_absolute_time (&deposit->wire_deadline),
     GNUNET_PQ_query_param_auto_from_type (&deposit->merchant_pub),
     GNUNET_PQ_query_param_auto_from_type (&deposit->h_contract_terms),
     GNUNET_PQ_query_param_auto_from_type (&deposit->h_wire),
@@ -3101,461 +3033,349 @@ postgres_insert_refund (void *cls,
 
 
 /**
- * Lookup refresh session data under the given @a session_hash.
+ * Lookup refresh melt commitment data under the given @a rc.
  *
  * @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
+ * @param rc commitment hash to use to locate the operation
+ * @param[out] refresh_melt where to store the result
  * @return transaction status
  */
 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)
+postgres_get_melt (void *cls,
+                   struct TALER_EXCHANGEDB_Session *session,
+                   const struct TALER_RefreshCommitmentP *rc,
+                   struct TALER_EXCHANGEDB_RefreshMelt *refresh_melt)
 {
   struct GNUNET_PQ_QueryParam params[] = {
-    GNUNET_PQ_query_param_auto_from_type (session_hash),
+    GNUNET_PQ_query_param_auto_from_type (rc),
     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_rsa_public_key ("denom_pub",
+                                         
&refresh_melt->session.coin.denom_pub.rsa_public_key),
+    TALER_PQ_result_spec_amount ("fee_refresh",
+                                &refresh_melt->melt_fee),
+    GNUNET_PQ_result_spec_rsa_signature ("denom_sig",
+                                        
&refresh_melt->session.coin.denom_sig.rsa_signature),
+    GNUNET_PQ_result_spec_uint32 ("noreveal_index",
+                                 &refresh_melt->session.noreveal_index),
     GNUNET_PQ_result_spec_auto_from_type ("old_coin_pub",
-                                         &refresh_session->melt.coin.coin_pub),
+                                         &refresh_melt->session.coin.coin_pub),
     GNUNET_PQ_result_spec_auto_from_type ("old_coin_sig",
-                                         &refresh_session->melt.coin_sig),
+                                         &refresh_melt->session.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),
+                                &refresh_melt->session.amount_with_fee),
     GNUNET_PQ_result_spec_end
   };
   enum GNUNET_DB_QueryStatus qs;
 
-  memset (refresh_session,
-          0,
-          sizeof (struct TALER_EXCHANGEDB_RefreshSession));
   qs = GNUNET_PQ_eval_prepared_singleton_select (session->conn,
-                                                "get_refresh_session",
+                                                "get_melt",
                                                 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;
+  refresh_melt->session.rc = *rc;
   return qs;
 }
 
 
 /**
- * Store new refresh session data under the given @a session_hash.
+ * Store new refresh melt commitment data.
  *
  * @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 refresh_session session data to store
  * @return query status for the transaction
  */
 static enum GNUNET_DB_QueryStatus
-postgres_create_refresh_session (void *cls,
-                                 struct TALER_EXCHANGEDB_Session *session,
-                                 const struct GNUNET_HashCode *session_hash,
-                                 const struct TALER_EXCHANGEDB_RefreshSession 
*refresh_session)
+postgres_insert_melt (void *cls,
+                      struct TALER_EXCHANGEDB_Session *session,
+                      const struct TALER_EXCHANGEDB_RefreshSession 
*refresh_session)
 {
   struct GNUNET_PQ_QueryParam params[] = {
-    GNUNET_PQ_query_param_auto_from_type (session_hash),
-    GNUNET_PQ_query_param_auto_from_type 
(&refresh_session->melt.coin.coin_pub),
-    GNUNET_PQ_query_param_auto_from_type (&refresh_session->melt.coin_sig),
-    TALER_PQ_query_param_amount (&refresh_session->melt.amount_with_fee),
-    GNUNET_PQ_query_param_uint16 (&refresh_session->num_newcoins),
-    GNUNET_PQ_query_param_uint16 (&refresh_session->noreveal_index),
+    GNUNET_PQ_query_param_auto_from_type (&refresh_session->rc),
+    GNUNET_PQ_query_param_auto_from_type (&refresh_session->coin.coin_pub),
+    GNUNET_PQ_query_param_auto_from_type (&refresh_session->coin_sig),
+    TALER_PQ_query_param_amount (&refresh_session->amount_with_fee),
+    GNUNET_PQ_query_param_uint32 (&refresh_session->noreveal_index),
     GNUNET_PQ_query_param_end
   };
   enum GNUNET_DB_QueryStatus qs;
 
   if (0 > (qs = ensure_coin_known (cls,
                                   session,
-                                  &refresh_session->melt.coin)))
+                                  &refresh_session->coin)))
     return qs;
   return GNUNET_PQ_eval_prepared_non_select (session->conn,
-                                            "insert_refresh_session",
+                                            "insert_melt",
                                             params);
 }
 
 
 /**
- * Store in the database which coin(s) we want to create
- * in a given refresh operation.
+ * Store in the database which coin(s) the wallet wanted to create
+ * in a given refresh operation and all of the other information
+ * we learned or created in the /refresh/reveal step.
  *
- * @param cls the `struct PostgresClosure` with the plugin-specific state
+ * @param cls the @e cls of this struct with the plugin-specific state
  * @param session database connection
- * @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
+ * @param rc identify commitment and thus refresh operation
+ * @param num_rrcs_newcoins number of coins to generate, size of the
+ *            @a rrcs array
+ * @param rrcs information about the new coins
+ * @param num_tprivs number of entries in @a tprivs, should be 
#TALER_CNC_KAPPA - 1
+ * @param tprivs transfer private keys to store
+ * @param tp public key to store
  * @return query status for the transaction
  */
 static enum GNUNET_DB_QueryStatus
-postgres_insert_refresh_order (void *cls,
-                               struct TALER_EXCHANGEDB_Session *session,
-                               const struct GNUNET_HashCode *session_hash,
-                               uint16_t num_newcoins,
-                               const struct TALER_DenominationPublicKey 
*denom_pubs)
+postgres_insert_refresh_reveal (void *cls,
+                                struct TALER_EXCHANGEDB_Session *session,
+                                const struct TALER_RefreshCommitmentP *rc,
+                                uint32_t num_rrcs,
+                                const struct 
TALER_EXCHANGEDB_RefreshRevealedCoin *rrcs,
+                                unsigned int num_tprivs,
+                                const struct TALER_TransferPrivateKeyP *tprivs,
+                                const struct TALER_TransferPublicKeyP *tp)
 {
-  for (unsigned int i=0;i<(unsigned int) num_newcoins;i++)
+  if (TALER_CNC_KAPPA != num_tprivs + 1)
+  {
+    GNUNET_break (0);
+    return GNUNET_DB_STATUS_HARD_ERROR;
+  }
+  for (uint32_t i=0;i<num_rrcs;i++)
   {
-    uint16_t newcoin_off = (uint16_t) i;
+    const struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrc = &rrcs[i];
+    struct GNUNET_HashCode denom_pub_hash;
+    struct GNUNET_PQ_QueryParam params[] = {
+      GNUNET_PQ_query_param_auto_from_type (rc),
+      GNUNET_PQ_query_param_uint32 (&i),
+      GNUNET_PQ_query_param_auto_from_type (&denom_pub_hash),
+      GNUNET_PQ_query_param_fixed_size (rrc->coin_ev,
+                                        rrc->coin_ev_size),
+      GNUNET_PQ_query_param_rsa_signature (rrc->coin_sig.rsa_signature),
+      GNUNET_PQ_query_param_end
+    };
+    enum GNUNET_DB_QueryStatus qs;
 
-    {
-      struct GNUNET_HashCode denom_pub_hash;
-      struct GNUNET_PQ_QueryParam params[] = {
-        GNUNET_PQ_query_param_uint16 (&newcoin_off),
-        GNUNET_PQ_query_param_auto_from_type (session_hash),
-        GNUNET_PQ_query_param_auto_from_type (&denom_pub_hash),
-        GNUNET_PQ_query_param_end
-      };
-      enum GNUNET_DB_QueryStatus qs;
-
-      GNUNET_CRYPTO_rsa_public_key_hash (denom_pubs[i].rsa_public_key,
-                                        &denom_pub_hash);
-      qs = GNUNET_PQ_eval_prepared_non_select (session->conn,
-                                              "insert_refresh_order",
-                                              params);
-      if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
-        return qs;
-    }
+    GNUNET_CRYPTO_rsa_public_key_hash (rrc->denom_pub.rsa_public_key,
+                                       &denom_pub_hash);
+
+    qs = GNUNET_PQ_eval_prepared_non_select (session->conn,
+                                             "insert_refresh_revealed_coin",
+                                             params);
+    if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
+      return qs;
+  }
+
+  {
+    struct GNUNET_PQ_QueryParam params[] = {
+      GNUNET_PQ_query_param_auto_from_type (rc),
+      GNUNET_PQ_query_param_auto_from_type (tp),
+      GNUNET_PQ_query_param_fixed_size (tprivs,
+                                        num_tprivs * sizeof (struct 
TALER_TransferPrivateKeyP)),
+      GNUNET_PQ_query_param_end
+    };
+
+    return GNUNET_PQ_eval_prepared_non_select (session->conn,
+                                               "insert_refresh_transfer_keys",
+                                               params);
   }
-  return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
 }
 
 
 /**
- * We allocated some @a denom_pubs information, but now need
- * to abort. Free allocated memory.
- *
- * @param denom_pubs data to free (but not the array itself)
- * @param denom_pubs_len length of @a denom_pubs array
+ * Context where we aggregate data from the database.
+ * Closure for #add_revealed_coins().
  */
-static void
-free_dpk_result (struct TALER_DenominationPublicKey *denom_pubs,
-                 unsigned int denom_pubs_len)
+struct GetRevealContext
 {
-  for (unsigned int i=0;i<denom_pubs_len;i++)
-  {
-    GNUNET_CRYPTO_rsa_public_key_free (denom_pubs[i].rsa_public_key);
-    denom_pubs[i].rsa_public_key = NULL;
-  }
-}
+  /**
+   * Array of revealed coins we obtained from the DB.
+   */
+  struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrcs;
 
+  /**
+   * Length of the @a rrcs array.
+   */
+  unsigned int rrcs_len;
 
-/**
- * Lookup in the database the coins that we want to
- * create in the given refresh operation.
- *
- * @param cls the `struct PostgresClosure` with the plugin-specific state
- * @param session database connection
- * @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 transaction status
- */
-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)
-{
-  for (unsigned i=0;i<(unsigned int) num_newcoins;i++)
-  {
-    uint16_t newcoin_off = (uint16_t) i;
-    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)
-    {
-    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 qs;
-    case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
-      break;
-    default:
-      GNUNET_break (0);
-      break;
-    }
-  }
-  return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
-}
-
-
-/**
- * Store information about the commitment of the
- * given coin for the given refresh session in the database.
- *
- * @param cls the `struct PostgresClosure` with the plugin-specific state
- * @param session database connection to use
- * @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 query transaction status
- */
-static enum GNUNET_DB_QueryStatus
-postgres_insert_refresh_commit_coins (void *cls,
-                                      struct TALER_EXCHANGEDB_Session *session,
-                                      const struct GNUNET_HashCode 
*session_hash,
-                                      uint16_t num_newcoins,
-                                      const struct 
TALER_EXCHANGEDB_RefreshCommitCoin *commit_coins)
-{
-  for (uint16_t coin_off=0;coin_off<num_newcoins;coin_off++)
-  {
-    struct GNUNET_PQ_QueryParam params[] = {
-      GNUNET_PQ_query_param_auto_from_type (session_hash),
-      GNUNET_PQ_query_param_uint16 (&coin_off),
-      GNUNET_PQ_query_param_fixed_size (commit_coins[coin_off].coin_ev,
-                                        commit_coins[coin_off].coin_ev_size),
-      GNUNET_PQ_query_param_end
-    };
-    enum GNUNET_DB_QueryStatus qs;
-
-    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_DB_STATUS_SUCCESS_ONE_RESULT;
-}
+  /**
+   * Set to an error code if we ran into trouble.
+   */
+  enum GNUNET_DB_QueryStatus qs;
+};
 
 
 /**
- * We allocated some @a commit_coin information, but now need
- * to abort. Free allocated memory.
+ * Function to be called with the results of a SELECT statement
+ * that has returned @a num_results results.
  *
- * @param cls unused
- * @param commit_coins_len length of @a commit_coins array
- * @param commit_coins data to free (but not the array itself)
+ * @param cls closure of type `struct GetRevealContext`
+ * @param result the postgres result
+ * @param num_result the number of results in @a result
  */
 static void
-postgres_free_refresh_commit_coins (void *cls,
-                                    unsigned int commit_coins_len,
-                                    struct TALER_EXCHANGEDB_RefreshCommitCoin 
*commit_coins)
+add_revealed_coins (void *cls,
+                    PGresult *result,
+                    unsigned int num_results)
 {
-  for (unsigned int i=0;i<commit_coins_len;i++)
-  {
-    GNUNET_free (commit_coins[i].coin_ev);
-    commit_coins[i].coin_ev = NULL;
-    commit_coins[i].coin_ev_size = 0;
-  }
-}
+  struct GetRevealContext *grctx = cls;
 
-
-/**
- * Obtain information about the commitment of the
- * given coin of the given refresh session from the database.
- *
- * @param cls the `struct PostgresClosure` with the plugin-specific state
- * @param session database connection to use
- * @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 transaction status
- */
-static enum GNUNET_DB_QueryStatus
-postgres_get_refresh_commit_coins (void *cls,
-                                   struct TALER_EXCHANGEDB_Session *session,
-                                   const struct GNUNET_HashCode *session_hash,
-                                   uint16_t num_newcoins,
-                                   struct TALER_EXCHANGEDB_RefreshCommitCoin 
*commit_coins)
-{
-  for (unsigned int i=0;i<(unsigned int) num_newcoins;i++)
+  if (0 == num_results)
+    return;
+  grctx->rrcs = GNUNET_new_array (num_results,
+                                  struct TALER_EXCHANGEDB_RefreshRevealedCoin);
+  grctx->rrcs_len = num_results;
+  for (unsigned int i = 0; i < num_results; i++)
   {
-    uint16_t newcoin_off = (uint16_t) i;
-    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
-    };
-    void *c_buf;
-    size_t c_buf_size;
+    struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrc = &grctx->rrcs[i];
+    uint32_t off;
     struct GNUNET_PQ_ResultSpec rs[] = {
+      GNUNET_PQ_result_spec_uint32 ("newcoin_index",
+                                    &off),
+      GNUNET_PQ_result_spec_rsa_public_key ("denom_pub",
+                                           &rrc->denom_pub.rsa_public_key),
       GNUNET_PQ_result_spec_variable_size ("coin_ev",
-                                          &c_buf,
-                                          &c_buf_size),
+                                          (void **) &rrc->coin_ev,
+                                          &rrc->coin_ev_size),
+      GNUNET_PQ_result_spec_rsa_signature ("ev_sig",
+                                           &rrc->coin_sig.rsa_signature),
       GNUNET_PQ_result_spec_end
     };
-    enum GNUNET_DB_QueryStatus qs;
 
-    qs = GNUNET_PQ_eval_prepared_singleton_select (session->conn,
-                                                  "get_refresh_commit_coin",
-                                                  params,
-                                                  rs);
-    if (0 >= qs)
+    if (GNUNET_OK !=
+        GNUNET_PQ_extract_result (result,
+                                  rs,
+                                  i))
     {
-      postgres_free_refresh_commit_coins (cls,
-                                          i,
-                                          commit_coins);
-      return qs;
+      GNUNET_break (0);
+      grctx->qs = GNUNET_DB_STATUS_HARD_ERROR;
+      return;
+    }
+    if (off != i)
+    {
+      GNUNET_break (0);
+      grctx->qs = GNUNET_DB_STATUS_HARD_ERROR;
+      return;
     }
-    commit_coins[i].coin_ev = c_buf;
-    commit_coins[i].coin_ev_size = c_buf_size;
   }
-  return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
-}
-
-
-/**
- * Store the commitment to the given (encrypted) refresh link data
- * for the given refresh session.
- *
- * @param cls the `struct PostgresClosure` with the plugin-specific state
- * @param session database connection to use
- * @param session_hash hash to identify refresh session
- * @param tp transfer public key to store
- * @return transaction status
- */
-static enum GNUNET_DB_QueryStatus
-postgres_insert_refresh_transfer_public_key (void *cls,
-                                             struct TALER_EXCHANGEDB_Session 
*session,
-                                             const struct GNUNET_HashCode 
*session_hash,
-                                             const struct 
TALER_TransferPublicKeyP *tp)
-{
-  struct GNUNET_PQ_QueryParam params[] = {
-    GNUNET_PQ_query_param_auto_from_type (session_hash),
-    GNUNET_PQ_query_param_auto_from_type (tp),
-    GNUNET_PQ_query_param_end
-  };
-
-  return GNUNET_PQ_eval_prepared_non_select (session->conn,
-                                            "insert_transfer_public_key",
-                                            params);
 }
 
 
 /**
- * Obtain the commited (encrypted) refresh link data
- * for the given refresh session.
+ * Lookup in the database the coins that we want to
+ * create in the given refresh operation.
  *
  * @param cls the `struct PostgresClosure` with the plugin-specific state
- * @param session database connection to use
- * @param session_hash hash to identify refresh session
- * @param[out] tp information to return
+ * @param session database connection
+ * @param rc identify commitment and thus refresh operation
+ * @param cb function to call with the results
+ * @param cb_cls closure for @a cb
  * @return transaction status
  */
 static enum GNUNET_DB_QueryStatus
-postgres_get_refresh_transfer_public_key (void *cls,
-                                          struct TALER_EXCHANGEDB_Session 
*session,
-                                          const struct GNUNET_HashCode 
*session_hash,
-                                          struct TALER_TransferPublicKeyP *tp)
+postgres_get_refresh_reveal (void *cls,
+                             struct TALER_EXCHANGEDB_Session *session,
+                             const struct TALER_RefreshCommitmentP *rc,
+                             TALER_EXCHANGEDB_RefreshCallback cb,
+                             void *cb_cls)
 {
+  struct GetRevealContext grctx;
+  enum GNUNET_DB_QueryStatus qs;
+  struct TALER_TransferPublicKeyP tp;
+  void *tpriv;
+  size_t tpriv_size;
   struct GNUNET_PQ_QueryParam params[] = {
-    GNUNET_PQ_query_param_auto_from_type (session_hash),
+    GNUNET_PQ_query_param_auto_from_type (rc),
     GNUNET_PQ_query_param_end
   };
   struct GNUNET_PQ_ResultSpec rs[] = {
     GNUNET_PQ_result_spec_auto_from_type ("transfer_pub",
-                                         tp),
+                                         &tp),
+    GNUNET_PQ_result_spec_variable_size ("transfer_privs",
+                                         &tpriv,
+                                         &tpriv_size),
     GNUNET_PQ_result_spec_end
   };
 
-  return GNUNET_PQ_eval_prepared_singleton_select (session->conn,
-                                                  
"get_refresh_transfer_public_key",
-                                                  params,
-                                                  rs);
-}
-
+  /* First get the coins */
+  memset (&grctx,
+          0,
+          sizeof (grctx));
+  qs = GNUNET_PQ_eval_prepared_multi_select (session->conn,
+                                             "get_refresh_revealed_coins",
+                                             params,
+                                             &add_revealed_coins,
+                                             &grctx);
+  switch (qs)
+  {
+  case GNUNET_DB_STATUS_HARD_ERROR:
+  case GNUNET_DB_STATUS_SOFT_ERROR:
+  case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+    goto cleanup;
+  case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+  default: /* can have more than one result */
+    break;
+  }
+  switch (grctx.qs)
+  {
+  case GNUNET_DB_STATUS_HARD_ERROR:
+  case GNUNET_DB_STATUS_SOFT_ERROR:
+    goto cleanup;
+  case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+  case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: /* should be impossible */
+    break;
+  }
 
-/**
- * Get signature of a new coin generated during refresh into
- * the database indexed by the refresh session and the index
- * of the coin.
- *
- * @param cls the `struct PostgresClosure` with the plugin-specific state
- * @param session database connection
- * @param session_hash hash to identify refresh session
- * @param newcoin_index coin index
- * @param ev_sig coin signature
- * @return transaction result status
- */
-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)
-{
-  struct GNUNET_PQ_QueryParam params[] = {
-    GNUNET_PQ_query_param_auto_from_type (session_hash),
-    GNUNET_PQ_query_param_uint16 (&newcoin_index),
-    GNUNET_PQ_query_param_end
-  };
-  struct GNUNET_PQ_ResultSpec rs[] = {
-    GNUNET_PQ_result_spec_rsa_signature ("ev_sig",
-                                         &ev_sig->rsa_signature),
-    GNUNET_PQ_result_spec_end
-  };
+  /* now also get the transfer keys (public and private) */
+  qs = GNUNET_PQ_eval_prepared_singleton_select (session->conn,
+                                                 "get_refresh_transfer_keys",
+                                                 params,
+                                                 rs);
+  switch (qs)
+  {
+  case GNUNET_DB_STATUS_HARD_ERROR:
+  case GNUNET_DB_STATUS_SOFT_ERROR:
+  case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+    goto cleanup;
+  case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+    break;
+  default:
+    GNUNET_assert (0);
+  }
+  if ( (0 != tpriv_size % sizeof (struct TALER_TransferPrivateKeyP)) ||
+       (TALER_CNC_KAPPA - 1 != tpriv_size / sizeof (struct 
TALER_TransferPrivateKeyP)) )
+  {
+    GNUNET_break (0);
+    qs = GNUNET_DB_STATUS_HARD_ERROR;
+    GNUNET_PQ_cleanup_result (rs);
+    goto cleanup;
+  }
 
-  return GNUNET_PQ_eval_prepared_singleton_select (session->conn,
-                                                  "get_refresh_out",
-                                                  params,
-                                                  rs);
-}
+  /* Pass result back to application */
+  cb (cb_cls,
+      grctx.rrcs_len,
+      grctx.rrcs,
+      tpriv_size / sizeof (struct TALER_TransferPrivateKeyP),
+      (const struct TALER_TransferPrivateKeyP *) tpriv,
+      &tp);
+  GNUNET_PQ_cleanup_result (rs);
 
+ cleanup:
+  for (unsigned int i = 0; i < grctx.rrcs_len; i++)
+  {
+    struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrc = &grctx.rrcs[i];
 
-/**
- * Insert signature of a new coin generated during refresh into
- * the database indexed by the refresh session and the index
- * of the coin.  This data is later used should an old coin
- * be used to try to obtain the private keys during "/refresh/link".
- *
- * @param cls the `struct PostgresClosure` with the plugin-specific state
- * @param session database connection
- * @param session_hash hash to identify refresh session
- * @param newcoin_index coin index
- * @param ev_sig coin signature
- * @return transaction result status
- */
-static enum GNUNET_DB_QueryStatus
-postgres_insert_refresh_out (void *cls,
-                             struct TALER_EXCHANGEDB_Session *session,
-                             const struct GNUNET_HashCode *session_hash,
-                             uint16_t newcoin_index,
-                             const struct TALER_DenominationSignature *ev_sig)
-{
-  struct GNUNET_PQ_QueryParam params[] = {
-    GNUNET_PQ_query_param_auto_from_type (session_hash),
-    GNUNET_PQ_query_param_uint16 (&newcoin_index),
-    GNUNET_PQ_query_param_rsa_signature (ev_sig->rsa_signature),
-    GNUNET_PQ_query_param_end
-  };
+    if (NULL != rrc->denom_pub.rsa_public_key)
+      GNUNET_CRYPTO_rsa_public_key_free (rrc->denom_pub.rsa_public_key);
+    if (NULL != rrc->coin_sig.rsa_signature)
+      GNUNET_CRYPTO_rsa_signature_free (rrc->coin_sig.rsa_signature);
+    GNUNET_free_non_null (rrc->coin_ev);
+  }
+  GNUNET_free_non_null (grctx.rrcs);
 
-  return GNUNET_PQ_eval_prepared_non_select (session->conn,
-                                            "insert_refresh_out",
-                                            params);
+  return qs;
 }
 
 
@@ -3565,9 +3385,25 @@ postgres_insert_refresh_out (void *cls,
 struct LinkDataContext
 {
   /**
-   * List we are building.
+   * Function to call on each result.
+   */
+  TALER_EXCHANGEDB_LinkDataCallback ldc;
+
+  /**
+   * Closure for @e ldc.
    */
-  struct TALER_EXCHANGEDB_LinkDataList *ldl;
+  void *ldc_cls;
+
+  /**
+   * Last transfer public key for which we have information in @e last.
+   * Only valid if @e last is non-NULL.
+   */
+  struct TALER_TransferPublicKeyP transfer_pub;
+
+  /**
+   * Link data for @e transfer_pub
+   */
+  struct TALER_EXCHANGEDB_LinkDataList *last;
 
   /**
    * Status, set to #GNUNET_SYSERR on errors,
@@ -3577,6 +3413,31 @@ struct LinkDataContext
 
 
 /**
+ * Free memory of the link data list.
+ *
+ * @param cls the @e cls of this struct with the plugin-specific state (unused)
+ * @param ldl link data list to release
+ */
+static void
+free_link_data_list (void *cls,
+                     struct TALER_EXCHANGEDB_LinkDataList *ldl)
+{
+  struct TALER_EXCHANGEDB_LinkDataList *next;
+
+  while (NULL != ldl)
+  {
+    next = ldl->next;
+    if (NULL != ldl->denom_pub.rsa_public_key)
+        GNUNET_CRYPTO_rsa_public_key_free (ldl->denom_pub.rsa_public_key);
+      if (NULL != ldl->ev_sig.rsa_signature)
+        GNUNET_CRYPTO_rsa_signature_free (ldl->ev_sig.rsa_signature);
+    GNUNET_free (ldl);
+    ldl = next;
+  }
+}
+
+
+/**
  * Function to be called with the results of a SELECT statement
  * that has returned @a num_results results.
  *
@@ -3589,21 +3450,22 @@ add_ldl (void *cls,
         PGresult *result,
         unsigned int num_results)
 {
-  struct LinkDataContext *ldc = cls;
+  struct LinkDataContext *ldctx = cls;
 
   for (int i = num_results - 1; i >= 0; i--)
   {
-    struct GNUNET_CRYPTO_RsaPublicKey *denom_pub;
-    struct GNUNET_CRYPTO_RsaSignature *sig;
     struct TALER_EXCHANGEDB_LinkDataList *pos;
+    struct TALER_TransferPublicKeyP transfer_pub;
 
     pos = GNUNET_new (struct TALER_EXCHANGEDB_LinkDataList);
     {
       struct GNUNET_PQ_ResultSpec rs[] = {
+        GNUNET_PQ_result_spec_auto_from_type ("transfer_pub",
+                                              &transfer_pub),
        GNUNET_PQ_result_spec_rsa_signature ("ev_sig",
-                                            &sig),
+                                            &pos->ev_sig.rsa_signature),
        GNUNET_PQ_result_spec_rsa_public_key ("denom_pub",
-                                             &denom_pub),
+                                             &pos->denom_pub.rsa_public_key),
        GNUNET_PQ_result_spec_end
       };
 
@@ -3613,18 +3475,31 @@ add_ldl (void *cls,
                                     i))
       {
        GNUNET_break (0);
-       common_free_link_data_list (cls,
-                                   ldc->ldl);
-       ldc->ldl = NULL;
        GNUNET_free (pos);
-       ldc->status = GNUNET_SYSERR;
+       ldctx->status = GNUNET_SYSERR;
        return;
       }
     }
-    pos->next = ldc->ldl;
-    pos->denom_pub.rsa_public_key = denom_pub;
-    pos->ev_sig.rsa_signature = sig;
-    ldc->ldl = pos;
+    if ( (NULL != ldctx->last) &&
+         (0 == memcmp (&transfer_pub,
+                       &ldctx->transfer_pub,
+                       sizeof (struct TALER_TransferPublicKeyP))) )
+    {
+      pos->next = ldctx->last;
+    }
+    else
+    {
+      if (NULL != ldctx->last)
+      {
+        ldctx->ldc (ldctx->ldc_cls,
+                    &ldctx->transfer_pub,
+                    ldctx->last);
+        free_link_data_list (cls,
+                             ldctx->last);
+      }
+      ldctx->transfer_pub = transfer_pub;
+    }
+    ldctx->last = pos;
   }
 }
 
@@ -3635,137 +3510,49 @@ add_ldl (void *cls,
  *
  * @param cls the `struct PostgresClosure` with the plugin-specific state
  * @param session database connection
- * @param session_hash refresh session to get linkage data for
- * @param[out] ldlp set to all known link data for the session
+ * @param coin_pub public key of the coin
+ * @param ldc function to call for each session the coin was melted into
+ * @param ldc_cls closure for @a tdc
  * @return transaction status code
  */
 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)
+postgres_get_link_data (void *cls,
+                        struct TALER_EXCHANGEDB_Session *session,
+                        const struct TALER_CoinSpendPublicKeyP *coin_pub,
+                        TALER_EXCHANGEDB_LinkDataCallback ldc,
+                        void *ldc_cls)
 {
-  struct LinkDataContext ldc;
   struct GNUNET_PQ_QueryParam params[] = {
-    GNUNET_PQ_query_param_auto_from_type (session_hash),
+    GNUNET_PQ_query_param_auto_from_type (coin_pub),
     GNUNET_PQ_query_param_end
   };
   enum GNUNET_DB_QueryStatus qs;
+  struct LinkDataContext ldctx;
 
-  ldc.status = GNUNET_OK;
-  ldc.ldl = NULL;
+  ldctx.ldc = ldc;
+  ldctx.ldc_cls = ldc_cls;
+  ldctx.last = NULL;
+  ldctx.status = GNUNET_OK;
   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;
-}
-
-
-/**
- * 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++)
+                                            &ldctx);
+  if (NULL != ldctx.last)
   {
-    struct GNUNET_HashCode session_hash;
-    struct TALER_TransferPublicKeyP transfer_pub;
-    struct GNUNET_PQ_ResultSpec rs[] = {
-      GNUNET_PQ_result_spec_auto_from_type ("transfer_pub", &transfer_pub),
-      GNUNET_PQ_result_spec_auto_from_type ("session_hash", &session_hash),
-      GNUNET_PQ_result_spec_end
-    };
-
-    if (GNUNET_OK !=
-        GNUNET_PQ_extract_result (result,
-                                  rs,
-                                  i))
+    if (GNUNET_OK == ldctx.status)
     {
-      GNUNET_break (0);
-      alc->status = GNUNET_SYSERR;
-      return;
+      /* call callback one more time! */
+      ldc (ldc_cls,
+           &ldctx.transfer_pub,
+           ldctx.last);
     }
-    alc->tdc (alc->tdc_cls,
-             &session_hash,
-             &transfer_pub);
+    free_link_data_list (cls,
+                         ldctx.last);
+    ldctx.last = NULL;
   }
-}
-
-
-/**
- * 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 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 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;
+  if (GNUNET_OK != ldctx.status)
+    return GNUNET_DB_STATUS_HARD_ERROR;
   return qs;
 }
 
@@ -3830,11 +3617,11 @@ add_coin_deposit (void *cls,
                                     &deposit->amount_with_fee),
        TALER_PQ_result_spec_amount ("fee_deposit",
                                     &deposit->deposit_fee),
-       GNUNET_PQ_result_spec_absolute_time ("timestamp",
+       TALER_PQ_result_spec_absolute_time ("timestamp",
                                             &deposit->timestamp),
-       GNUNET_PQ_result_spec_absolute_time ("refund_deadline",
+       TALER_PQ_result_spec_absolute_time ("refund_deadline",
                                             &deposit->refund_deadline),
-       GNUNET_PQ_result_spec_absolute_time ("wire_deadline",
+       TALER_PQ_result_spec_absolute_time ("wire_deadline",
                                             &deposit->wire_deadline),
        GNUNET_PQ_result_spec_auto_from_type ("merchant_pub",
                                              &deposit->merchant_pub),
@@ -3905,13 +3692,13 @@ add_coin_melt (void *cls,
     melt = GNUNET_new (struct TALER_EXCHANGEDB_RefreshMelt);
     {
       struct GNUNET_PQ_ResultSpec rs[] = {
-       GNUNET_PQ_result_spec_auto_from_type ("session_hash",
-                                             &melt->session_hash),
+       GNUNET_PQ_result_spec_auto_from_type ("rc",
+                                             &melt->session.rc),
        /* oldcoin_index not needed */
        GNUNET_PQ_result_spec_auto_from_type ("old_coin_sig",
-                                             &melt->coin_sig),
+                                             &melt->session.coin_sig),
        TALER_PQ_result_spec_amount ("amount_with_fee",
-                                    &melt->amount_with_fee),
+                                    &melt->session.amount_with_fee),
        TALER_PQ_result_spec_amount ("fee_refresh",
                                     &melt->melt_fee),
        GNUNET_PQ_result_spec_end
@@ -3927,16 +3714,17 @@ add_coin_melt (void *cls,
        chc->status = GNUNET_DB_STATUS_HARD_ERROR;
        return;
       }
-      melt->coin.coin_pub = *chc->coin_pub;
+      melt->session.coin.coin_pub = *chc->coin_pub;
     }
     tl = GNUNET_new (struct TALER_EXCHANGEDB_TransactionList);
     tl->next = chc->head;
     tl->type = TALER_EXCHANGEDB_TT_REFRESH_MELT;
     tl->details.melt = melt;
+    /* FIXME: integrate via JOIN in main select, instead of using separate 
query */
     qs = get_known_coin (chc->db_cls,
                         chc->session,
                         chc->coin_pub,
-                        &melt->coin);
+                        &melt->session.coin);
     if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
     {
       GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
@@ -4051,7 +3839,7 @@ add_coin_payback (void *cls,
                                              &payback->coin_blind),
        GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
                                              &payback->coin_sig),
-       GNUNET_PQ_result_spec_absolute_time ("timestamp",
+       TALER_PQ_result_spec_absolute_time ("timestamp",
                                             &payback->timestamp),
        GNUNET_PQ_result_spec_rsa_public_key ("denom_pub",
                                              
&payback->coin.denom_pub.rsa_public_key),
@@ -4218,7 +4006,7 @@ handle_wt_result (void *cls,
       GNUNET_PQ_result_spec_auto_from_type ("h_wire", &h_wire),
       GNUNET_PQ_result_spec_auto_from_type ("coin_pub", &coin_pub),
       GNUNET_PQ_result_spec_auto_from_type ("merchant_pub", &merchant_pub),
-      GNUNET_PQ_result_spec_absolute_time ("execution_date", &exec_time),
+      TALER_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
@@ -4341,7 +4129,7 @@ postgres_wire_lookup_deposit_wtid (void *cls,
   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_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
@@ -4384,7 +4172,7 @@ postgres_wire_lookup_deposit_wtid (void *cls,
     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),
+      TALER_PQ_result_spec_absolute_time ("wire_deadline", &exec_time),
       GNUNET_PQ_result_spec_end
     };
 
@@ -4461,12 +4249,12 @@ postgres_get_wire_fee (void *cls,
 {
   struct GNUNET_PQ_QueryParam params[] = {
     GNUNET_PQ_query_param_string (type),
-    GNUNET_PQ_query_param_absolute_time (&date),
+    TALER_PQ_query_param_absolute_time (&date),
     GNUNET_PQ_query_param_end
   };
   struct GNUNET_PQ_ResultSpec rs[] = {
-    GNUNET_PQ_result_spec_absolute_time ("start_date", start_date),
-    GNUNET_PQ_result_spec_absolute_time ("end_date", end_date),
+    TALER_PQ_result_spec_absolute_time ("start_date", start_date),
+    TALER_PQ_result_spec_absolute_time ("end_date", end_date),
     TALER_PQ_result_spec_amount ("wire_fee", wire_fee),
     GNUNET_PQ_result_spec_auto_from_type ("master_sig", master_sig),
     GNUNET_PQ_result_spec_end
@@ -4502,8 +4290,8 @@ postgres_insert_wire_fee (void *cls,
 {
   struct GNUNET_PQ_QueryParam params[] = {
     GNUNET_PQ_query_param_string (type),
-    GNUNET_PQ_query_param_absolute_time (&start_date),
-    GNUNET_PQ_query_param_absolute_time (&end_date),
+    TALER_PQ_query_param_absolute_time (&start_date),
+    TALER_PQ_query_param_absolute_time (&end_date),
     TALER_PQ_query_param_amount (wire_fee),
     GNUNET_PQ_query_param_auto_from_type (master_sig),
     GNUNET_PQ_query_param_end
@@ -4601,7 +4389,7 @@ reserve_expired_cb (void *cls,
     struct TALER_ReservePublicKeyP reserve_pub;
     struct TALER_Amount remaining_balance;
     struct GNUNET_PQ_ResultSpec rs[] = {
-      GNUNET_PQ_result_spec_absolute_time ("expiration_date",
+      TALER_PQ_result_spec_absolute_time ("expiration_date",
                                           &exp_date),
       TALER_PQ_result_spec_json ("account_details",
                                 &account_details),
@@ -4653,7 +4441,7 @@ postgres_get_expired_reserves (void *cls,
                               void *rec_cls)
 {
   struct GNUNET_PQ_QueryParam params[] = {
-    GNUNET_PQ_query_param_absolute_time (&now),
+    TALER_PQ_query_param_absolute_time (&now),
     GNUNET_PQ_query_param_end
   };
   struct ExpiredReserveContext ectx;
@@ -4699,7 +4487,7 @@ postgres_insert_reserve_closed (void *cls,
   struct TALER_EXCHANGEDB_Reserve reserve;
   struct GNUNET_PQ_QueryParam params[] = {
     GNUNET_PQ_query_param_auto_from_type (reserve_pub),
-    GNUNET_PQ_query_param_absolute_time (&execution_date),
+    TALER_PQ_query_param_absolute_time (&execution_date),
     GNUNET_PQ_query_param_auto_from_type (wtid),
     TALER_PQ_query_param_json (receiver_account),
     TALER_PQ_query_param_amount (amount_with_fee),
@@ -4918,7 +4706,7 @@ postgres_store_wire_transfer_out (void *cls,
                                   const struct TALER_Amount *amount)
 {
   struct GNUNET_PQ_QueryParam params[] = {
-    GNUNET_PQ_query_param_absolute_time (&date),
+    TALER_PQ_query_param_absolute_time (&date),
     GNUNET_PQ_query_param_auto_from_type (wtid),
     TALER_PQ_query_param_json (wire_account),
     TALER_PQ_query_param_amount (amount),
@@ -4949,17 +4737,18 @@ postgres_gc (void *cls)
     GNUNET_PQ_query_param_end
   };
   struct GNUNET_PQ_QueryParam params_time[] = {
-    GNUNET_PQ_query_param_absolute_time (&now),
+    TALER_PQ_query_param_absolute_time (&now),
     GNUNET_PQ_query_param_end
   };
   struct GNUNET_PQ_QueryParam params_ancient_time[] = {
-    GNUNET_PQ_query_param_absolute_time (&long_ago),
+    TALER_PQ_query_param_absolute_time (&long_ago),
     GNUNET_PQ_query_param_end
   };
   PGconn *conn;
   int ret;
 
   now = GNUNET_TIME_absolute_get ();
+  (void) GNUNET_TIME_round_abs (&now);
   /* Keep wire fees for 10 years, that should always
      be enough _and_ they are tiny so it does not
      matter to make this tight */
@@ -5045,7 +4834,7 @@ deposit_serial_helper_cb (void *cls,
     struct GNUNET_PQ_ResultSpec rs[] = {
       TALER_PQ_result_spec_amount ("amount_with_fee",
                                    &deposit.amount_with_fee),
-      GNUNET_PQ_result_spec_absolute_time ("timestamp",
+      TALER_PQ_result_spec_absolute_time ("timestamp",
                                           &deposit.timestamp),
       GNUNET_PQ_result_spec_auto_from_type ("merchant_pub",
                                             &deposit.merchant_pub),
@@ -5055,9 +4844,9 @@ deposit_serial_helper_cb (void *cls,
                                            &deposit.coin.coin_pub),
       GNUNET_PQ_result_spec_auto_from_type ("coin_sig",
                                            &deposit.csig),
-      GNUNET_PQ_result_spec_absolute_time ("refund_deadline",
+      TALER_PQ_result_spec_absolute_time ("refund_deadline",
                                            &deposit.refund_deadline),
-      GNUNET_PQ_result_spec_absolute_time ("wire_deadline",
+      TALER_PQ_result_spec_absolute_time ("wire_deadline",
                                            &deposit.wire_deadline),
       GNUNET_PQ_result_spec_auto_from_type ("h_contract_terms",
                                            &deposit.h_contract_terms),
@@ -5184,10 +4973,9 @@ refreshs_serial_helper_cb (void *cls,
     struct TALER_CoinSpendPublicKeyP coin_pub;
     struct TALER_CoinSpendSignatureP coin_sig;
     struct TALER_Amount amount_with_fee;
-    uint16_t num_newcoins;
-    uint16_t noreveal_index;
+    uint32_t noreveal_index;
     uint64_t rowid;
-    struct GNUNET_HashCode session_hash;
+    struct TALER_RefreshCommitmentP rc;
     struct GNUNET_PQ_ResultSpec rs[] = {
       GNUNET_PQ_result_spec_rsa_public_key ("denom_pub",
                                             &denom_pub.rsa_public_key),
@@ -5197,14 +4985,12 @@ refreshs_serial_helper_cb (void *cls,
                                             &coin_sig),
       TALER_PQ_result_spec_amount ("amount_with_fee",
                                    &amount_with_fee),
-      GNUNET_PQ_result_spec_uint16 ("num_newcoins",
-                                    &num_newcoins),
-      GNUNET_PQ_result_spec_uint16 ("noreveal_index",
+      GNUNET_PQ_result_spec_uint32 ("noreveal_index",
                                     &noreveal_index),
       GNUNET_PQ_result_spec_uint64 ("melt_serial_id",
                                     &rowid),
-      GNUNET_PQ_result_spec_auto_from_type ("session_hash",
-                                            &session_hash),
+      GNUNET_PQ_result_spec_auto_from_type ("rc",
+                                            &rc),
       GNUNET_PQ_result_spec_end
     };
     int ret;
@@ -5224,9 +5010,8 @@ refreshs_serial_helper_cb (void *cls,
                   &coin_pub,
                   &coin_sig,
                   &amount_with_fee,
-                  num_newcoins,
                   noreveal_index,
-                  &session_hash);
+                  &rc);
     GNUNET_PQ_cleanup_result (rs);
     if (GNUNET_OK != ret)
       break;
@@ -5264,7 +5049,7 @@ postgres_select_refreshs_above_serial_id (void *cls,
   enum GNUNET_DB_QueryStatus qs;
 
   qs = GNUNET_PQ_eval_prepared_multi_select (session->conn,
-                                            "audit_get_refresh_sessions_incr",
+                                            
"audit_get_refresh_commitments_incr",
                                             params,
                                             &refreshs_serial_helper_cb,
                                             &rsc);
@@ -5458,7 +5243,7 @@ reserves_in_serial_helper_cb (void *cls,
                                            &wire_reference_size),
       TALER_PQ_result_spec_amount ("credit",
                                    &credit),
-      GNUNET_PQ_result_spec_absolute_time("execution_date",
+      TALER_PQ_result_spec_absolute_time("execution_date",
                                           &execution_date),
       TALER_PQ_result_spec_json ("sender_account_details",
                                  &sender_account_details),
@@ -5591,7 +5376,7 @@ reserves_out_serial_helper_cb (void *cls,
                                             &reserve_pub),
       GNUNET_PQ_result_spec_auto_from_type ("reserve_sig",
                                             &reserve_sig),
-      GNUNET_PQ_result_spec_absolute_time ("execution_date",
+      TALER_PQ_result_spec_absolute_time ("execution_date",
                                            &execution_date),
       TALER_PQ_result_spec_amount ("amount_with_fee",
                                    &amount_with_fee),
@@ -5714,7 +5499,7 @@ wire_out_serial_helper_cb (void *cls,
     struct GNUNET_PQ_ResultSpec rs[] = {
       GNUNET_PQ_result_spec_uint64 ("wireout_uuid",
                                     &rowid),
-      GNUNET_PQ_result_spec_absolute_time ("execution_date",
+      TALER_PQ_result_spec_absolute_time ("execution_date",
                                            &date),
       GNUNET_PQ_result_spec_auto_from_type ("wtid_raw",
                                             &wtid),
@@ -5839,7 +5624,7 @@ payback_serial_helper_cb (void *cls,
     struct GNUNET_PQ_ResultSpec rs[] = {
       GNUNET_PQ_result_spec_uint64 ("payback_uuid",
                                     &rowid),
-      GNUNET_PQ_result_spec_absolute_time ("timestamp",
+      TALER_PQ_result_spec_absolute_time ("timestamp",
                                            &timestamp),
       GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
                                             &reserve_pub),
@@ -5977,7 +5762,7 @@ reserve_closed_serial_helper_cb (void *cls,
                                     &rowid),
       GNUNET_PQ_result_spec_auto_from_type ("reserve_pub",
                                             &reserve_pub),
-      GNUNET_PQ_result_spec_absolute_time ("execution_date",
+      TALER_PQ_result_spec_absolute_time ("execution_date",
                                            &execution_date),
       GNUNET_PQ_result_spec_auto_from_type ("wtid",
                                            &wtid),
@@ -6091,7 +5876,7 @@ postgres_insert_payback_request (void *cls,
     GNUNET_PQ_query_param_auto_from_type (coin_sig),
     GNUNET_PQ_query_param_auto_from_type (coin_blind),
     TALER_PQ_query_param_amount (amount),
-    GNUNET_PQ_query_param_absolute_time (&timestamp),
+    TALER_PQ_query_param_absolute_time (&timestamp),
     GNUNET_PQ_query_param_auto_from_type (h_blind_ev),
     GNUNET_PQ_query_param_end
   };
@@ -6296,7 +6081,7 @@ missing_wire_cb (void *cls,
                                   &amount),
       TALER_PQ_result_spec_json ("wire",
                                 &wire),
-      GNUNET_PQ_result_spec_absolute_time ("wire_deadline",
+      TALER_PQ_result_spec_absolute_time ("wire_deadline",
                                           &deadline),
       GNUNET_PQ_result_spec_uint32 ("tiny",
                                    &tiny),
@@ -6349,8 +6134,8 @@ postgres_select_deposits_missing_wire (void *cls,
                                       void *cb_cls)
 {
   struct GNUNET_PQ_QueryParam params[] = {
-    GNUNET_PQ_query_param_absolute_time (&start_date),
-    GNUNET_PQ_query_param_absolute_time (&end_date),
+    TALER_PQ_query_param_absolute_time (&start_date),
+    TALER_PQ_query_param_absolute_time (&end_date),
     GNUNET_PQ_query_param_end
   };
   struct MissingWireContext mwc = {
@@ -6451,20 +6236,11 @@ libtaler_plugin_exchangedb_postgres_init (void *cls)
   plugin->iterate_matching_deposits = &postgres_iterate_matching_deposits;
   plugin->insert_deposit = &postgres_insert_deposit;
   plugin->insert_refund = &postgres_insert_refund;
-  plugin->get_refresh_session = &postgres_get_refresh_session;
-  plugin->create_refresh_session = &postgres_create_refresh_session;
-  plugin->insert_refresh_order = &postgres_insert_refresh_order;
-  plugin->get_refresh_order = &postgres_get_refresh_order;
-  plugin->insert_refresh_commit_coins = &postgres_insert_refresh_commit_coins;
-  plugin->get_refresh_commit_coins = &postgres_get_refresh_commit_coins;
-  plugin->free_refresh_commit_coins = &postgres_free_refresh_commit_coins;
-  plugin->insert_refresh_transfer_public_key = 
&postgres_insert_refresh_transfer_public_key;
-  plugin->get_refresh_transfer_public_key = 
&postgres_get_refresh_transfer_public_key;
-  plugin->get_refresh_out = &postgres_get_refresh_out;
-  plugin->insert_refresh_out = &postgres_insert_refresh_out;
-  plugin->get_link_data_list = &postgres_get_link_data_list;
-  plugin->free_link_data_list = &common_free_link_data_list;
-  plugin->get_transfer = &postgres_get_transfer;
+  plugin->insert_melt = &postgres_insert_melt;
+  plugin->get_melt = &postgres_get_melt;
+  plugin->insert_refresh_reveal = &postgres_insert_refresh_reveal;
+  plugin->get_refresh_reveal = &postgres_get_refresh_reveal;
+  plugin->get_link_data = &postgres_get_link_data;
   plugin->get_coin_transactions = &postgres_get_coin_transactions;
   plugin->free_coin_transaction_list = &common_free_coin_transaction_list;
   plugin->lookup_wire_transfer = &postgres_lookup_wire_transfer;
diff --git a/src/exchangedb/test_exchangedb.c b/src/exchangedb/test_exchangedb.c
index f04eaf1..5891a08 100644
--- a/src/exchangedb/test_exchangedb.c
+++ b/src/exchangedb/test_exchangedb.c
@@ -308,162 +308,85 @@ static struct TALER_Amount amount_with_fee;
  */
 #define MELT_NOREVEAL_INDEX 1
 
-
-static struct TALER_EXCHANGEDB_RefreshCommitCoin *commit_coins;
-
 /**
- * Test APIs related to the "insert_refresh_commit_coins" function.
- *
- * @param session database sesison to use
- * @param refresh_session details about the refresh session to use
- * @param session_hash refresh melt session hash to use
- * @return #GNUNET_OK on success
+ * How big do we make the coin envelopes?
  */
-static int
-test_refresh_commit_coins (struct TALER_EXCHANGEDB_Session *session,
-                           const struct TALER_EXCHANGEDB_RefreshSession 
*refresh_session,
-                           const struct GNUNET_HashCode *session_hash)
-{
-  struct TALER_EXCHANGEDB_RefreshCommitCoin *ret_commit_coins;
-  struct TALER_EXCHANGEDB_RefreshCommitCoin *a_ccoin;
-  struct TALER_EXCHANGEDB_RefreshCommitCoin *b_ccoin;
-  unsigned int cnt;
-  int ret;
-
 #define COIN_ENC_MAX_SIZE 512
-  ret = GNUNET_SYSERR;
-  ret_commit_coins = NULL;
-  commit_coins
-    = GNUNET_new_array (MELT_NEW_COINS,
-                        struct TALER_EXCHANGEDB_RefreshCommitCoin);
-  for (cnt=0; cnt < MELT_NEW_COINS; cnt++)
-  {
-    struct TALER_EXCHANGEDB_RefreshCommitCoin *ccoin;
 
-    ccoin = &commit_coins[cnt];
-    ccoin->coin_ev_size = GNUNET_CRYPTO_random_u64
-      (GNUNET_CRYPTO_QUALITY_WEAK, COIN_ENC_MAX_SIZE);
-    ccoin->coin_ev = GNUNET_malloc (ccoin->coin_ev_size);
-    GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
-                                ccoin->coin_ev,
-                                ccoin->coin_ev_size);
-  }
-  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
-          plugin->insert_refresh_commit_coins (plugin->cls,
-                                               session,
-                                               session_hash,
-                                               MELT_NEW_COINS,
-                                               commit_coins));
-  ret_commit_coins = GNUNET_new_array (MELT_NEW_COINS,
-                                       struct 
TALER_EXCHANGEDB_RefreshCommitCoin);
-  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
-          plugin->get_refresh_commit_coins (plugin->cls,
-                                            session,
-                                            session_hash,
-                                            MELT_NEW_COINS,
-                                            ret_commit_coins));
-  /* compare the refresh commit coin arrays */
-  for (cnt = 0; cnt < MELT_NEW_COINS; cnt++)
-  {
-    a_ccoin = &commit_coins[cnt];
-    b_ccoin = &ret_commit_coins[cnt];
-    FAILIF (a_ccoin->coin_ev_size != b_ccoin->coin_ev_size);
-    FAILIF (0 != memcmp (a_ccoin->coin_ev,
-                         a_ccoin->coin_ev,
-                         a_ccoin->coin_ev_size));
-    GNUNET_free (ret_commit_coins[cnt].coin_ev);
-  }
-  GNUNET_free (ret_commit_coins);
-  ret_commit_coins = NULL;
-  ret = GNUNET_OK;
- drop:
-  if (NULL != ret_commit_coins)
-  {
-    plugin->free_refresh_commit_coins (plugin->cls,
-                                       MELT_NEW_COINS,
-                                       ret_commit_coins);
-    GNUNET_free (ret_commit_coins);
-  }
-  return ret;
-}
+static struct TALER_EXCHANGEDB_RefreshRevealedCoin *revealed_coins;
 
+static struct TALER_TransferPrivateKeyP tprivs[TALER_CNC_KAPPA];
 
-static struct TALER_TransferPublicKeyP rctp[TALER_CNC_KAPPA];
+static struct TALER_TransferPublicKeyP tpub;
 
 
 /**
- * Test APIs related to the "insert_refresh_commit_coins" function.
+ * Function called with information about a refresh order.  This
+ * one should not be called in a successful test.
  *
- * @param session database sesison to use
- * @param refresh_session details about the refresh session to use
- * @param session_hash refresh melt session hash to use
- * @return #GNUNET_OK on success
+ * @param cls closure
+ * @param rowid unique serial ID for the row in our database
+ * @param num_newcoins size of the @a rrcs array
+ * @param rrcs array of @a num_newcoins information about coins to be created
+ * @param num_tprivs number of entries in @a tprivs, should be 
#TALER_CNC_KAPPA - 1
+ * @param tprivs array of @e num_tprivs transfer private keys
+ * @param tp transfer public key information
  */
-static int
-test_refresh_commit_links (struct TALER_EXCHANGEDB_Session *session,
-                           const struct TALER_EXCHANGEDB_RefreshSession 
*refresh_session,
-                           const struct GNUNET_HashCode *session_hash)
+static void
+never_called_cb (void *cls,
+                 uint32_t num_newcoins,
+                 const struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrcs,
+                 unsigned int num_tprivs,
+                 const struct TALER_TransferPrivateKeyP *tprivs,
+                 const struct TALER_TransferPublicKeyP *tp)
 {
-  int ret;
-  struct TALER_TransferPublicKeyP tp;
-  unsigned int i;
-
-  ret = GNUNET_SYSERR;
-  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_DB_STATUS_SUCCESS_ONE_RESULT !=
-          plugin->insert_refresh_transfer_public_key (plugin->cls,
-                                                      session,
-                                                      session_hash,
-                                                      
&rctp[MELT_NOREVEAL_INDEX]));
-  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
-          plugin->get_refresh_transfer_public_key (plugin->cls,
-                                                   session,
-                                                   session_hash,
-                                                   &tp));
-  FAILIF (0 !=
-          memcmp (&rctp[MELT_NOREVEAL_INDEX],
-                  &tp,
-                  sizeof (struct TALER_TransferPublicKeyP)));
-  ret = GNUNET_OK;
- drop:
-  return ret;
+  GNUNET_assert (0); /* should never be called! */
 }
 
 
-static struct GNUNET_HashCode session_hash;
-
-
 /**
- * Function called with the session hashes and transfer secret
- * information for a given coin.  Checks if they are as expected.
+ * Function called with information about a refresh order.
+ * Checks that the response matches what we expect to see.
  *
  * @param cls closure
- * @param sh a session the coin was melted in
- * @param transfer_pub public transfer key for the session
+ * @param rowid unique serial ID for the row in our database
+ * @param num_newcoins size of the @a rrcs array
+ * @param rrcs array of @a num_newcoins information about coins to be created
+ * @param num_tprivs number of entries in @a tprivs, should be 
#TALER_CNC_KAPPA - 1
+ * @param tprivsr array of @e num_tprivs transfer private keys
+ * @param tpr transfer public key information
  */
 static void
-check_transfer_data (void *cls,
-                     const struct GNUNET_HashCode *sh,
-                     const struct TALER_TransferPublicKeyP *transfer_pub)
+check_refresh_reveal_cb (void *cls,
+                         uint32_t num_newcoins,
+                         const struct TALER_EXCHANGEDB_RefreshRevealedCoin 
*rrcs,
+                         unsigned int num_tprivs,
+                         const struct TALER_TransferPrivateKeyP *tprivsr,
+                         const struct TALER_TransferPublicKeyP *tpr)
 {
-  int *ok = cls;
-
-  FAILIF (0 != memcmp (&rctp[MELT_NOREVEAL_INDEX],
-                       transfer_pub,
-                       sizeof (struct TALER_TransferPublicKeyP)));
-  FAILIF (0 != memcmp (&session_hash,
-                       sh,
-                       sizeof (struct GNUNET_HashCode)));
-  *ok = GNUNET_OK;
-  return;
- drop:
-  *ok = GNUNET_SYSERR;
+  /* compare the refresh commit coin arrays */
+  for (unsigned int cnt = 0; cnt < num_newcoins; cnt++)
+  {
+    const struct TALER_EXCHANGEDB_RefreshRevealedCoin *acoin = 
&revealed_coins[cnt];
+    const struct TALER_EXCHANGEDB_RefreshRevealedCoin *bcoin = &rrcs[cnt];
+
+    GNUNET_assert (acoin->coin_ev_size == bcoin->coin_ev_size);
+    GNUNET_assert (0 ==
+                   memcmp (acoin->coin_ev,
+                           bcoin->coin_ev,
+                           acoin->coin_ev_size));
+    GNUNET_assert (0 ==
+                   GNUNET_CRYPTO_rsa_public_key_cmp 
(acoin->denom_pub.rsa_public_key,
+                                                     
bcoin->denom_pub.rsa_public_key));
+  }
+  GNUNET_assert (0 ==
+                 memcmp (&tpub,
+                         tpr,
+                         sizeof (tpub)));
+  GNUNET_assert (0 ==
+                 memcmp (tprivs,
+                         tprivsr,
+                         sizeof (struct TALER_TransferPrivateKeyP) * 
(TALER_CNC_KAPPA - 1)));
 }
 
 
@@ -487,7 +410,7 @@ static unsigned int auditor_row_cnt;
  * @param amount_with_fee amount that was deposited including fee
  * @param num_newcoins how many coins were issued
  * @param noreveal_index which index was picked by the exchange in 
cut-and-choose
- * @param session_hash what is the session hash
+ * @param rc what is the session hash
  * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
  */
 static int
@@ -497,9 +420,8 @@ audit_refresh_session_cb (void *cls,
                           const struct TALER_CoinSpendPublicKeyP *coin_pub,
                           const struct TALER_CoinSpendSignatureP *coin_sig,
                           const struct TALER_Amount *amount_with_fee,
-                          uint16_t num_newcoins,
-                          uint16_t noreveal_index,
-                          const struct GNUNET_HashCode *session_hash)
+                          uint32_t noreveal_index,
+                          const struct TALER_RefreshCommitmentP *rc)
 {
   auditor_row_cnt++;
   return GNUNET_OK;
@@ -507,6 +429,51 @@ audit_refresh_session_cb (void *cls,
 
 
 /**
+ * Denomination keys used for fresh coins in melt test.
+ */
+static struct DenomKeyPair **new_dkp;
+
+
+/**
+ * Function called with the session hashes and transfer secret
+ * information for a given coin.
+ *
+ * @param cls closure
+ * @param transfer_pub public transfer key for the session
+ * @param ldl link data for @a transfer_pub
+ */
+static void
+handle_link_data_cb (void *cls,
+                     const struct TALER_TransferPublicKeyP *transfer_pub,
+                     const struct TALER_EXCHANGEDB_LinkDataList *ldl)
+{
+  for (const struct TALER_EXCHANGEDB_LinkDataList *ldlp = ldl;
+       NULL != ldlp;
+       ldlp = ldlp->next)
+  {
+    int found;
+
+    found = GNUNET_NO;
+    for (unsigned int cnt=0;cnt < MELT_NEW_COINS;cnt++)
+    {
+      GNUNET_assert (NULL != ldlp->ev_sig.rsa_signature);
+      if ( (0 ==
+            GNUNET_CRYPTO_rsa_public_key_cmp (ldlp->denom_pub.rsa_public_key,
+                                              
new_dkp[cnt]->pub.rsa_public_key)) &&
+           (0 ==
+            GNUNET_CRYPTO_rsa_signature_cmp (ldlp->ev_sig.rsa_signature,
+                                             
revealed_coins[cnt].coin_sig.rsa_signature)) )
+      {
+        found = GNUNET_YES;
+        break;
+      }
+    }
+    GNUNET_assert (GNUNET_NO != found);
+  }
+}
+
+
+/**
  * Function to test melting of coins as part of a refresh session
  *
  * @param session the database session
@@ -517,35 +484,28 @@ static int
 test_melting (struct TALER_EXCHANGEDB_Session *session)
 {
   struct TALER_EXCHANGEDB_RefreshSession refresh_session;
-  struct TALER_EXCHANGEDB_RefreshSession ret_refresh_session;
+  struct TALER_EXCHANGEDB_RefreshMelt ret_refresh_session;
   struct DenomKeyPair *dkp;
-  struct DenomKeyPair **new_dkp;
-  /* struct TALER_CoinPublicInfo *coins; */
-  struct TALER_EXCHANGEDB_RefreshMelt *meltp;
   struct TALER_DenominationPublicKey *new_denom_pubs;
   struct TALER_DenominationPublicKey *ret_denom_pubs;
-  struct TALER_EXCHANGEDB_LinkDataList *ldl;
-  struct TALER_EXCHANGEDB_LinkDataList *ldlp;
-  struct TALER_DenominationSignature ev_sigs[MELT_NEW_COINS];
-  unsigned int cnt;
   int ret;
   enum GNUNET_DB_QueryStatus qs;
+  struct GNUNET_TIME_Absolute now;
 
   ret = GNUNET_SYSERR;
-  memset (ev_sigs, 0, sizeof (ev_sigs));
   RND_BLK (&refresh_session);
-  RND_BLK (&session_hash);
   dkp = NULL;
   new_dkp = NULL;
   new_denom_pubs = NULL;
   ret_denom_pubs = NULL;
   /* create and test a refresh session */
-  refresh_session.num_newcoins = MELT_NEW_COINS;
   refresh_session.noreveal_index = MELT_NOREVEAL_INDEX;
   /* create a denomination (value: 1; fraction: 100) */
+  now = GNUNET_TIME_absolute_get ();
+  GNUNET_TIME_round_abs (&now);
   dkp = create_denom_key_pair (512,
                                session,
-                               GNUNET_TIME_absolute_get (),
+                               now,
                                &value,
                                &fee_withdraw,
                                &fee_deposit,
@@ -556,33 +516,62 @@ test_melting (struct TALER_EXCHANGEDB_Session *session)
   {
     struct GNUNET_HashCode hc;
 
-    meltp = &refresh_session.melt;
-    RND_BLK (&meltp->coin.coin_pub);
-    GNUNET_CRYPTO_hash (&meltp->coin.coin_pub,
-                        sizeof (meltp->coin.coin_pub),
+    RND_BLK (&refresh_session.coin.coin_pub);
+    GNUNET_CRYPTO_hash (&refresh_session.coin.coin_pub,
+                        sizeof (refresh_session.coin.coin_pub),
                         &hc);
-    meltp->coin.denom_sig.rsa_signature =
+    refresh_session.coin.denom_sig.rsa_signature =
         GNUNET_CRYPTO_rsa_sign_fdh (dkp->priv.rsa_private_key,
                                     &hc);
-    GNUNET_assert (NULL != meltp->coin.denom_sig.rsa_signature);
-    meltp->coin.denom_pub = dkp->pub;
-    RND_BLK (&meltp->coin_sig);
-    meltp->session_hash = session_hash;
-    meltp->amount_with_fee = amount_with_fee;
-    meltp->melt_fee = fee_refresh;
+    GNUNET_assert (NULL != refresh_session.coin.denom_sig.rsa_signature);
+    refresh_session.coin.denom_pub = dkp->pub;
+    refresh_session.amount_with_fee = amount_with_fee;
   }
 
+  /* test insert_melt & get_melt */
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
+          plugin->get_melt (plugin->cls,
+                            session,
+                            &refresh_session.rc,
+                            &ret_refresh_session));
   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
-         plugin->create_refresh_session (plugin->cls,
-                                         session,
-                                         &session_hash,
-                                         &refresh_session));
+         plugin->insert_melt (plugin->cls,
+                               session,
+                               &refresh_session));
   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
-         plugin->get_refresh_session (plugin->cls,
-                                      session,
-                                      &session_hash,
-                                      &ret_refresh_session));
+         plugin->get_melt (plugin->cls,
+                            session,
+                            &refresh_session.rc,
+                            &ret_refresh_session));
+  FAILIF (refresh_session.noreveal_index !=
+          ret_refresh_session.session.noreveal_index);
+  FAILIF (0 !=
+          TALER_amount_cmp (&refresh_session.amount_with_fee,
+                            &ret_refresh_session.session.amount_with_fee));
+  FAILIF (0 !=
+          TALER_amount_cmp (&fee_refresh,
+                            &ret_refresh_session.melt_fee));
+  FAILIF (0 !=
+          memcmp (&refresh_session.rc,
+                  &ret_refresh_session.session.rc,
+                  sizeof (struct TALER_RefreshCommitmentP)));
+  FAILIF (0 !=
+          memcmp (&refresh_session.coin_sig,
+                  &ret_refresh_session.session.coin_sig,
+                  sizeof (struct TALER_CoinSpendSignatureP)));
+  FAILIF (0 !=
+          GNUNET_CRYPTO_rsa_signature_cmp 
(refresh_session.coin.denom_sig.rsa_signature,
+                                           
ret_refresh_session.session.coin.denom_sig.rsa_signature));
+  FAILIF (0 != memcmp (&refresh_session.coin.coin_pub,
+                       &ret_refresh_session.session.coin.coin_pub,
+                       sizeof (refresh_session.coin.coin_pub)));
+  FAILIF (0 !=
+          GNUNET_CRYPTO_rsa_public_key_cmp 
(refresh_session.coin.denom_pub.rsa_public_key,
+                                            
ret_refresh_session.session.coin.denom_pub.rsa_public_key));
+  GNUNET_CRYPTO_rsa_signature_free 
(ret_refresh_session.session.coin.denom_sig.rsa_signature);
+  GNUNET_CRYPTO_rsa_public_key_free 
(ret_refresh_session.session.coin.denom_pub.rsa_public_key);
 
+  /* test 'select_refreshs_above_serial_id' */
   auditor_row_cnt = 0;
   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
          plugin->select_refreshs_above_serial_id (plugin->cls,
@@ -591,44 +580,25 @@ test_melting (struct TALER_EXCHANGEDB_Session *session)
                                                   &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);
 
-  /* check refresh session melt data */
-  {
-    struct TALER_EXCHANGEDB_RefreshMelt *ret_melt;
-
-    ret_melt = &ret_refresh_session.melt;
-    FAILIF (0 != GNUNET_CRYPTO_rsa_signature_cmp
-            (ret_melt->coin.denom_sig.rsa_signature,
-             meltp->coin.denom_sig.rsa_signature));
-    FAILIF (0 != memcmp (&ret_melt->coin.coin_pub,
-                         &meltp->coin.coin_pub,
-                         sizeof (ret_melt->coin.coin_pub)));
-    FAILIF (0 != GNUNET_CRYPTO_rsa_public_key_cmp
-            (ret_melt->coin.denom_pub.rsa_public_key,
-             meltp->coin.denom_pub.rsa_public_key));
-    FAILIF (0 != memcmp (&ret_melt->coin_sig,
-                         &meltp->coin_sig,
-                         sizeof (ret_melt->coin_sig)));
-    FAILIF (0 != memcmp (&ret_melt->session_hash,
-                         &meltp->session_hash,
-                         sizeof (ret_melt->session_hash)));
-    FAILIF (0 != TALER_amount_cmp (&ret_melt->amount_with_fee,
-                                   &meltp->amount_with_fee));
-    FAILIF (0 != TALER_amount_cmp (&ret_melt->melt_fee,
-                                   &meltp->melt_fee));
-    GNUNET_CRYPTO_rsa_signature_free (ret_melt->coin.denom_sig.rsa_signature);
-    GNUNET_CRYPTO_rsa_public_key_free 
(ret_melt->coin.denom_pub.rsa_public_key);
-  }
-  new_dkp = GNUNET_new_array (MELT_NEW_COINS, struct DenomKeyPair *);
+  new_dkp = GNUNET_new_array (MELT_NEW_COINS,
+                              struct DenomKeyPair *);
   new_denom_pubs = GNUNET_new_array (MELT_NEW_COINS,
                                      struct TALER_DenominationPublicKey);
-  for (cnt=0; cnt < MELT_NEW_COINS; cnt++)
+  revealed_coins
+    = GNUNET_new_array (MELT_NEW_COINS,
+                        struct TALER_EXCHANGEDB_RefreshRevealedCoin);
+  for (unsigned int cnt=0; cnt < MELT_NEW_COINS; cnt++)
   {
+    struct TALER_EXCHANGEDB_RefreshRevealedCoin *ccoin;
+    struct GNUNET_HashCode hc;
+    struct GNUNET_TIME_Absolute now;
+
+    now = GNUNET_TIME_absolute_get ();
+    GNUNET_TIME_round_abs (&now);
     new_dkp[cnt] = create_denom_key_pair (1024,
                                           session,
-                                          GNUNET_TIME_absolute_get (),
+                                          now,
                                           &value,
                                           &fee_withdraw,
                                           &fee_deposit,
@@ -636,100 +606,50 @@ test_melting (struct TALER_EXCHANGEDB_Session *session)
                                          &fee_refund);
     GNUNET_assert (NULL != new_dkp[cnt]);
     new_denom_pubs[cnt] = new_dkp[cnt]->pub;
-  }
-  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
-          plugin->insert_refresh_order (plugin->cls,
-                                        session,
-                                        &session_hash,
-                                        MELT_NEW_COINS,
-                                        new_denom_pubs));
-  ret_denom_pubs = GNUNET_new_array (MELT_NEW_COINS,
-                                     struct TALER_DenominationPublicKey);
-  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
-          plugin->get_refresh_order (plugin->cls,
-                                     session,
-                                     &session_hash,
-                                     MELT_NEW_COINS,
-                                     ret_denom_pubs));
-  for (cnt=0; cnt < MELT_NEW_COINS; cnt++)
-  {
-    FAILIF (0 != GNUNET_CRYPTO_rsa_public_key_cmp
-            (ret_denom_pubs[cnt].rsa_public_key,
-             new_denom_pubs[cnt].rsa_public_key));
-  }
-  FAILIF (GNUNET_OK !=
-          test_refresh_commit_coins (session,
-                                     &refresh_session,
-                                     &session_hash));
-  FAILIF (GNUNET_OK !=
-          test_refresh_commit_links (session,
-                                     &refresh_session,
-                                     &session_hash));
-
-  for (cnt=0; cnt < MELT_NEW_COINS; cnt++)
-  {
-    struct GNUNET_HashCode hc;
-    struct TALER_DenominationSignature test_sig;
-
+    ccoin = &revealed_coins[cnt];
+    ccoin->coin_ev_size = (size_t) GNUNET_CRYPTO_random_u64 
(GNUNET_CRYPTO_QUALITY_WEAK,
+                                                             
COIN_ENC_MAX_SIZE);
+    ccoin->coin_ev = GNUNET_malloc (ccoin->coin_ev_size);
+    GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_WEAK,
+                                ccoin->coin_ev,
+                                ccoin->coin_ev_size);
     RND_BLK (&hc);
-    ev_sigs[cnt].rsa_signature
+    ccoin->denom_pub = new_dkp[cnt]->pub;
+    ccoin->coin_sig.rsa_signature
       = GNUNET_CRYPTO_rsa_sign_fdh (new_dkp[cnt]->priv.rsa_private_key,
                                     &hc);
-    GNUNET_assert (NULL != ev_sigs[cnt].rsa_signature);
-    FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
-            plugin->get_refresh_out (plugin->cls,
-                                     session,
-                                     &session_hash,
-                                     cnt,
-                                     &test_sig));
-    FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
-            plugin->insert_refresh_out (plugin->cls,
-                                        session,
-                                        &session_hash,
-                                        cnt,
-                                        &ev_sigs[cnt]));
-    FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
-            plugin->get_refresh_out (plugin->cls,
-                                     session,
-                                     &session_hash,
-                                     cnt,
-                                     &test_sig));
-    FAILIF (0 !=
-            GNUNET_CRYPTO_rsa_signature_cmp (test_sig.rsa_signature,
-                                             ev_sigs[cnt].rsa_signature));
-    GNUNET_CRYPTO_rsa_signature_free (test_sig.rsa_signature);
   }
+  RND_BLK (&tprivs);
+  RND_BLK (&tpub);
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
+          plugin->get_refresh_reveal (plugin->cls,
+                                      session,
+                                      &refresh_session.rc,
+                                      &never_called_cb,
+                                      NULL));
+  FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
+          plugin->insert_refresh_reveal (plugin->cls,
+                                         session,
+                                         &refresh_session.rc,
+                                         MELT_NEW_COINS,
+                                         revealed_coins,
+                                         TALER_CNC_KAPPA - 1,
+                                         tprivs,
+                                         &tpub));
+  FAILIF (0 >=
+          plugin->get_refresh_reveal (plugin->cls,
+                                      session,
+                                      &refresh_session.rc,
+                                      &check_refresh_reveal_cb,
+                                      NULL));
 
-  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)
-  {
-    int found;
-
-    found = GNUNET_NO;
-    for (cnt=0;cnt < MELT_NEW_COINS;cnt++)
-    {
-      FAILIF (NULL == ldlp->ev_sig.rsa_signature);
-      if ( (0 ==
-            GNUNET_CRYPTO_rsa_public_key_cmp (ldlp->denom_pub.rsa_public_key,
-                                              
new_dkp[cnt]->pub.rsa_public_key)) &&
-           (0 ==
-            GNUNET_CRYPTO_rsa_signature_cmp (ldlp->ev_sig.rsa_signature,
-                                             ev_sigs[cnt].rsa_signature)) )
-      {
-        found = GNUNET_YES;
-        break;
-      }
-    }
-    FAILIF (GNUNET_NO == found);
-  }
-  plugin->free_link_data_list (plugin->cls,
-                               ldl);
 
+  qs = plugin->get_link_data (plugin->cls,
+                              session,
+                              &refresh_session.coin.coin_pub,
+                              &handle_link_data_cb,
+                              NULL);
+  FAILIF (0 >= qs);
   {
     /* Just to test fetching a coin with melt history */
     struct TALER_EXCHANGEDB_TransactionList *tl;
@@ -737,7 +657,7 @@ test_melting (struct TALER_EXCHANGEDB_Session *session)
 
     qs = plugin->get_coin_transactions (plugin->cls,
                                         session,
-                                        &meltp->coin.coin_pub,
+                                        &refresh_session.coin.coin_pub,
                                        &tl);
     FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs);
     plugin->free_coin_transaction_list (plugin->cls,
@@ -745,42 +665,29 @@ test_melting (struct TALER_EXCHANGEDB_Session *session)
   }
 
 
-  {
-    int ok;
-
-    ok = GNUNET_NO;
-    FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
-            plugin->get_transfer (plugin->cls,
-                                  session,
-                                  &meltp->coin.coin_pub,
-                                  &check_transfer_data,
-                                  &ok));
-    FAILIF (GNUNET_OK != ok);
-  }
-
   ret = GNUNET_OK;
  drop:
-  for (cnt=0; cnt < MELT_NEW_COINS; cnt++)
-    if (NULL != ev_sigs[cnt].rsa_signature)
-      GNUNET_CRYPTO_rsa_signature_free (ev_sigs[cnt].rsa_signature);
-  if (NULL != commit_coins)
+  if (NULL != revealed_coins)
   {
-    plugin->free_refresh_commit_coins (plugin->cls,
-                                       MELT_NEW_COINS,
-                                       commit_coins);
-    GNUNET_free (commit_coins);
-    commit_coins = NULL;
+    for (unsigned int cnt=0; cnt < MELT_NEW_COINS; cnt++)
+    {
+      if (NULL != revealed_coins[cnt].coin_sig.rsa_signature)
+        GNUNET_CRYPTO_rsa_signature_free 
(revealed_coins[cnt].coin_sig.rsa_signature);
+      GNUNET_free (revealed_coins[cnt].coin_ev);
+    }
+    GNUNET_free (revealed_coins);
+    revealed_coins = NULL;
   }
   destroy_denom_key_pair (dkp);
-  GNUNET_CRYPTO_rsa_signature_free (meltp->coin.denom_sig.rsa_signature);
-  for (cnt = 0;
+  GNUNET_CRYPTO_rsa_signature_free 
(refresh_session.coin.denom_sig.rsa_signature);
+  for (unsigned int cnt = 0;
        (NULL != ret_denom_pubs) && (cnt < MELT_NEW_COINS)
          && (NULL != ret_denom_pubs[cnt].rsa_public_key);
        cnt++)
     GNUNET_CRYPTO_rsa_public_key_free (ret_denom_pubs[cnt].rsa_public_key);
   GNUNET_free_non_null (ret_denom_pubs);
   GNUNET_free_non_null (new_denom_pubs);
-  for (cnt = 0;
+  for (unsigned int cnt = 0;
        (NULL != new_dkp) && (cnt < MELT_NEW_COINS) && (NULL != new_dkp[cnt]);
        cnt++)
     destroy_denom_key_pair (new_dkp[cnt]);
@@ -1108,6 +1015,7 @@ test_gc (struct TALER_EXCHANGEDB_Session *session)
   struct TALER_EXCHANGEDB_DenominationKeyInformationP issue2;
 
   now = GNUNET_TIME_absolute_get ();
+  GNUNET_TIME_round_abs (&now);
   past = GNUNET_TIME_absolute_subtract (now,
                                         GNUNET_TIME_relative_multiply 
(GNUNET_TIME_UNIT_HOURS,
                                                                        4));
@@ -1161,7 +1069,9 @@ test_wire_fees (struct TALER_EXCHANGEDB_Session *session)
   struct TALER_MasterSignatureP ms;
 
   start_date = GNUNET_TIME_absolute_get ();
+  GNUNET_TIME_round_abs (&start_date);
   end_date = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_MINUTES);
+  GNUNET_TIME_round_abs (&end_date);
   GNUNET_assert (GNUNET_OK ==
                  TALER_string_to_amount (CURRENCY ":1.424242",
                                          &wire_fee));
@@ -1439,7 +1349,7 @@ wire_missing_cb (void *cls,
                  /* bool? */ int tiny,
                  /* bool? */ int done)
 {
-  struct TALER_EXCHANGEDB_Deposit *deposit = cls;
+  const struct TALER_EXCHANGEDB_Deposit *deposit = cls;
   struct GNUNET_HashCode h_wire;
 
   if (NULL != wire)
@@ -1450,16 +1360,32 @@ wire_missing_cb (void *cls,
     memset (&h_wire,
             0,
             sizeof (h_wire));
-  if ( (GNUNET_NO != tiny) ||
-       (GNUNET_NO != done) ||
-       (0 != TALER_amount_cmp (amount,
-                               &deposit->amount_with_fee)) ||
-       (0 != memcmp (coin_pub,
-                     &deposit->coin.coin_pub,
-                     sizeof (struct TALER_CoinSpendPublicKeyP))) ||
-       (0 != memcmp (&h_wire,
-                     &deposit->h_wire,
-                     sizeof (struct GNUNET_HashCode))) )
+  if (GNUNET_NO != tiny)
+  {
+    GNUNET_break (0);
+    result = 66;
+  }
+  if (GNUNET_NO != done)
+  {
+    GNUNET_break (0);
+    result = 66;
+  }
+  if (0 != TALER_amount_cmp (amount,
+                             &deposit->amount_with_fee))
+  {
+    GNUNET_break (0);
+    result = 66;
+  }
+  if (0 != memcmp (coin_pub,
+                   &deposit->coin.coin_pub,
+                   sizeof (struct TALER_CoinSpendPublicKeyP)))
+  {
+    GNUNET_break (0);
+    result = 66;
+  }
+  if (0 != memcmp (&h_wire,
+                   &deposit->h_wire,
+                   sizeof (struct GNUNET_HashCode)))
   {
     GNUNET_break (0);
     result = 66;
@@ -1511,6 +1437,7 @@ run (void *cls)
   void *rr;
   size_t rr_size;
   enum GNUNET_DB_QueryStatus qs;
+  struct GNUNET_TIME_Absolute now;
 
   dkp = NULL;
   rh = NULL;
@@ -1578,12 +1505,14 @@ run (void *cls)
                                                    session,
                                                    &rr,
                                                    &rr_size));
+  now = GNUNET_TIME_absolute_get ();
+  (void) GNUNET_TIME_round_abs (&now);
   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->reserves_in_insert (plugin->cls,
                                       session,
                                       &reserve_pub,
                                       &value,
-                                      GNUNET_TIME_absolute_get (),
+                                      now,
                                       sndr,
                                       "TEST",
                                       4));
@@ -1601,12 +1530,14 @@ run (void *cls)
                          value.value,
                          value.fraction,
                          value.currency));
+  now = GNUNET_TIME_absolute_get ();
+  (void) GNUNET_TIME_round_abs (&now);
   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->reserves_in_insert (plugin->cls,
                                       session,
                                       &reserve_pub,
                                       &value,
-                                      GNUNET_TIME_absolute_get (),
+                                      now,
                                      sndr,
                                       "TEST2",
                                       5));
@@ -1615,6 +1546,7 @@ run (void *cls)
                                                    session,
                                                    &rr,
                                                    &rr_size));
+  GNUNET_free (rr);
   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->get_latest_reserve_in_reference (plugin->cls,
                                                    session,
@@ -1631,9 +1563,11 @@ run (void *cls)
                          value.fraction * 2,
                          value.currency));
   result = 5;
+  now = GNUNET_TIME_absolute_get ();
+  (void) GNUNET_TIME_round_abs (&now);
   dkp = create_denom_key_pair (1024,
                                session,
-                               GNUNET_TIME_absolute_get (),
+                               now,
                                &value,
                                &fee_withdraw,
                                &fee_deposit,
@@ -1723,11 +1657,13 @@ run (void *cls)
   GNUNET_assert (GNUNET_OK ==
                  TALER_string_to_amount (CURRENCY ":0.000010",
                                          &fee_closing));
+  now = GNUNET_TIME_absolute_get ();
+  (void) GNUNET_TIME_round_abs (&now);
   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
          plugin->insert_reserve_closed (plugin->cls,
                                         session,
                                         &reserve_pub,
-                                        GNUNET_TIME_absolute_get (),
+                                        now,
                                         sndr,
                                         &wire_out_wtid,
                                         &amount_with_fee,
@@ -1885,7 +1821,7 @@ run (void *cls)
                                              &deposit.merchant_pub,
                                              &deposit_cb, &deposit,
                                              2));
-
+  sleep (2); /* giv deposit time to be ready */
   FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
           plugin->get_ready_deposit (plugin->cls,
                                      session,
diff --git a/src/include/taler_bank_service.h b/src/include/taler_bank_service.h
index 246174d..6349326 100644
--- a/src/include/taler_bank_service.h
+++ b/src/include/taler_bank_service.h
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  Copyright (C) 2015, 2016, 2017 GNUnet e.V. & Inria
+  Copyright (C) 2015, 2016, 2017 Taler Systems SA
 
   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
@@ -25,6 +25,7 @@
 #include <jansson.h>
 #include <gnunet/gnunet_curl_lib.h>
 #include "taler_util.h"
+#include "taler_error_codes.h"
 
 
 /**
@@ -98,12 +99,14 @@ struct TALER_BANK_AdminAddIncomingHandle;
  * @param cls closure
  * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful 
status request
  *                    0 if the bank's reply is bogus (fails to follow the 
protocol)
+ * @param ec detailed error code
  * @param serial_id unique ID of the wire transfer in the bank's records; 
UINT64_MAX on error
  * @param json detailed response from the HTTPD, or NULL if reply was not in 
JSON
  */
 typedef void
 (*TALER_BANK_AdminAddIncomingResultCallback) (void *cls,
                                               unsigned int http_status,
+                                              enum TALER_ErrorCode ec,
                                               uint64_t serial_id,
                                               const json_t *json);
 
@@ -118,7 +121,7 @@ typedef void
  * @param bank_base_url URL of the bank (used to execute this request)
  * @param auth authentication data to use
  * @param exchange_base_url base URL of the exchange (for tracking)
- * @param wtid wire transfer identifier for the transfer
+ * @param subject wire transfer subject for the transfer
  * @param amount amount that was deposited
  * @param debit_account_no account number to withdraw from (53 bits at most)
  * @param credit_account_no account number to deposit into (53 bits at most)
@@ -133,7 +136,7 @@ TALER_BANK_admin_add_incoming (struct GNUNET_CURL_Context 
*ctx,
                                const char *bank_base_url,
                                const struct TALER_BANK_AuthenticationData 
*auth,
                                const char *exchange_base_url,
-                               const struct TALER_WireTransferIdentifierRawP 
*wtid,
+                               const char *subject,
                                const struct TALER_Amount *amount,
                                uint64_t debit_account_no,
                                uint64_t credit_account_no,
@@ -174,7 +177,15 @@ enum TALER_BANK_Direction {
   /**
    * Return both types of transactions.
    */
-  TALER_BANK_DIRECTION_BOTH = (TALER_BANK_DIRECTION_CREDIT | 
TALER_BANK_DIRECTION_DEBIT)
+  TALER_BANK_DIRECTION_BOTH = (TALER_BANK_DIRECTION_CREDIT | 
TALER_BANK_DIRECTION_DEBIT),
+
+  /**
+   * Bit mask that is applied to view transactions that have been
+   * cancelled. The bit is set for cancelled transactions that are
+   * returned from /history, and must also be set in order for
+   * cancelled transactions to show up in the /history.
+   */
+  TALER_BANK_DIRECTION_CANCEL = 4
 
 };
 
@@ -222,6 +233,7 @@ struct TALER_BANK_TransferDetails
  *                    #MHD_HTTP_NO_CONTENT if there are no more results; on 
success the
  *                    last callback is always of this status (even if 
`abs(num_results)` were
  *                    already returned).
+ * @param ec detailed error code
  * @param dir direction of the transfer
  * @param serial_id monotonically increasing counter corresponding to the 
transaction
  * @param details details about the wire transfer
@@ -230,6 +242,7 @@ struct TALER_BANK_TransferDetails
 typedef void
 (*TALER_BANK_HistoryResultCallback) (void *cls,
                                      unsigned int http_status,
+                                     enum TALER_ErrorCode ec,
                                      enum TALER_BANK_Direction dir,
                                      uint64_t serial_id,
                                      const struct TALER_BANK_TransferDetails 
*details,
@@ -277,5 +290,61 @@ void
 TALER_BANK_history_cancel (struct TALER_BANK_HistoryHandle *hh);
 
 
+/**
+ * Handle for #TALER_BANK_reject() operation.
+ */
+struct TALER_BANK_RejectHandle;
+
+
+/**
+ * Callbacks of this type are used to serve the result of asking
+ * the bank to reject an incoming wire transfer.
+ *
+ * @param cls closure
+ * @param http_status HTTP response code, #MHD_HTTP_NO_CONTENT (204) for 
successful status request;
+ *                    #MHD_HTTP_NOT_FOUND if the rowid is unknown;
+ *                    0 if the bank's reply is bogus (fails to follow the 
protocol),
+ * @param ec detailed error code
+ */
+typedef void
+(*TALER_BANK_RejectResultCallback) (void *cls,
+                                    unsigned int http_status,
+                                    enum TALER_ErrorCode ec);
+
+
+/**
+ * Request rejection of a wire transfer, marking it as cancelled and voiding
+ * its effects.
+ *
+ * @param ctx curl context for the event loop
+ * @param bank_base_url URL of the bank (used to execute this request)
+ * @param auth authentication data to use
+ * @param account_number which account number should we query
+ * @param rowid transfer to reject
+ * @param rcb the callback to call with the operation result
+ * @param rcb_cls closure for @a rcb
+ * @return NULL
+ *         if the inputs are invalid.
+ *         In this case, the callback is not called.
+ */
+struct TALER_BANK_RejectHandle *
+TALER_BANK_reject (struct GNUNET_CURL_Context *ctx,
+                   const char *bank_base_url,
+                   const struct TALER_BANK_AuthenticationData *auth,
+                   uint64_t account_number,
+                   uint64_t rowid,
+                   TALER_BANK_RejectResultCallback rcb,
+                   void *rcb_cls);
+
+
+/**
+ * Cancel an reject request.  This function cannot be used on a request
+ * handle if the response was is already served for it.
+ *
+ * @param rh the reject request handle
+ */
+void
+TALER_BANK_reject_cancel (struct TALER_BANK_RejectHandle *rh);
+
 
 #endif  /* _TALER_BANK_SERVICE_H */
diff --git a/src/include/taler_crypto_lib.h b/src/include/taler_crypto_lib.h
index d5024e6..d45e7bb 100644
--- a/src/include/taler_crypto_lib.h
+++ b/src/include/taler_crypto_lib.h
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  Copyright (C) 2014, 2015 GNUnet e.V.
+  Copyright (C) 2014, 2015, 2016, 2017 Taler Systems SA
 
   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
@@ -289,6 +289,19 @@ struct TALER_DenominationBlindingKeyP
 };
 
 
+/**
+ * Commitment value for the refresh protocol.
+ * See #TALER_refresh_get_commitment().
+ */
+struct TALER_RefreshCommitmentP
+{
+  /**
+   * The commitment is a hash code.
+   */
+  struct GNUNET_HashCode session_hash;
+};
+
+
 GNUNET_NETWORK_STRUCT_END
 
 
@@ -557,12 +570,12 @@ GNUNET_NETWORK_STRUCT_END
  *
  * @param secret_seed seed to use for KDF to derive coin keys
  * @param coin_num_salt number of the coin to include in KDF
- * @param[out] fc value to initialize
+ * @param[out] ps value to initialize
  */
 void
 TALER_planchet_setup_refresh (const struct TALER_TransferSecretP *secret_seed,
-                              unsigned int coin_num_salt,
-                              struct TALER_PlanchetSecretsP *fc);
+                              uint32_t coin_num_salt,
+                              struct TALER_PlanchetSecretsP *ps);
 
 
 /**
@@ -656,4 +669,65 @@ TALER_link_recover_transfer_secret (const struct 
TALER_TransferPublicKeyP *trans
                                     struct TALER_TransferSecretP 
*transfer_secret);
 
 
+/**
+ * Information about a coin to be created during a refresh operation.
+ */
+struct TALER_RefreshCoinData
+{
+
+  /**
+   * The denomination's public key.
+   */
+  const struct TALER_DenominationPublicKey *dk;
+
+  /**
+   * The envelope with the blinded coin.
+   */
+  char *coin_ev;
+
+  /**
+   * Number of bytes in @a coin_ev
+   */
+  size_t coin_ev_size;
+
+};
+
+
+/**
+ * One of the #TALER_CNC_KAPPA commitments.
+ */
+struct TALER_RefreshCommitmentEntry
+{
+  /**
+   * Transfer public key of this commitment.
+   */
+  struct TALER_TransferPublicKeyP transfer_pub;
+
+  /**
+   * Array of @e num_new_coins new coins to be created.
+   */
+  struct TALER_RefreshCoinData *new_coins;
+};
+
+
+/**
+ * Compute the commitment for a /refresh/melt operation from
+ * the respective public inputs.
+ *
+ * @param[out] rc set to the value the wallet must commit to
+ * @param kappa number of transfer public keys involved (must be 
#TALER_CNC_KAPPA)
+ * @param num_new_coins number of new coins to be created
+ * @param commitments array of @a kappa commitments
+ * @param coin_pub public key of the coin to be melted
+ * @param amount_with_fee amount to be melted, including fee
+ */
+void
+TALER_refresh_get_commitment (struct TALER_RefreshCommitmentP *rc,
+                              uint32_t kappa,
+                              uint32_t num_new_coins,
+                              const struct TALER_RefreshCommitmentEntry *rcs,
+                              const struct TALER_CoinSpendPublicKeyP *coin_pub,
+                              const struct TALER_Amount *amount_with_fee);
+
+
 #endif
diff --git a/src/include/taler_error_codes.h b/src/include/taler_error_codes.h
index acc7228..24d738f 100644
--- a/src/include/taler_error_codes.h
+++ b/src/include/taler_error_codes.h
@@ -68,6 +68,11 @@ enum TALER_ErrorCode
    */
   TALER_EC_TIMEOUT = 6,
 
+  /**
+   * Exchange failed to allocate memory for building JSON reply.
+   */
+  TALER_EC_JSON_ALLOCATION_FAILURE = 7,
+
   /* ********** generic error codes ************* */
 
   /**
@@ -428,6 +433,7 @@ enum TALER_ErrorCode
    */
   TALER_EC_DEPOSIT_INVALID_TIMESTAMP = 1218,
 
+
   /**
    * The respective coin did not have sufficient residual value
    * for the /refresh/melt operation.  The "history" in this
@@ -469,85 +475,25 @@ enum TALER_ErrorCode
   TALER_EC_REFRESH_MELT_DB_STORE_SESSION_ERROR = 1304,
 
   /**
-   * The exchange failed to store commit data in the
-   * database.
-   * This response is provided with HTTP status code
-   * MHD_HTTP_INTERNAL_ERROR.
-   */
-  TALER_EC_REFRESH_MELT_DB_STORE_COMMIT_ERROR = 1306,
-
-  /**
-   * 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.
-   */
-  TALER_EC_REFRESH_MELT_FRESH_DENOMINATION_KEY_NOT_FOUND = 1308,
-
-  /**
-   * The exchange encountered a numeric overflow totaling up
-   * the cost for the refresh operation.  This response is provided
-   * with HTTP status code MHD_HTTP_INTERNAL_SERVER_ERROR.
-   */
-  TALER_EC_REFRESH_MELT_COST_CALCULATION_OVERFLOW = 1309,
-
-  /**
-   * During the transaction phase, the exchange could suddenly
-   * no longer find the denomination key that was
-   * used to sign the melted coin.  This response is provided
-   * with HTTP status code MHD_HTTP_INTERNAL_SERVER_ERROR.
-   */
-  TALER_EC_REFRESH_MELT_DB_DENOMINATION_KEY_NOT_FOUND = 1310,
-
-  /**
    * The exchange encountered melt fees exceeding the melted
    * coin's contribution.  This response is provided
    * with HTTP status code MHD_HTTP_BAD_REQUEST.
    */
-  TALER_EC_REFRESH_MELT_FEES_EXCEED_CONTRIBUTION = 1311,
-
-  /**
-   * The exchange's cost calculation does not add up to the
-   * melt fees specified in the request.  This response is provided
-   * with HTTP status code MHD_HTTP_BAD_REQUEST.
-   */
-  TALER_EC_REFRESH_MELT_FEES_MISSMATCH = 1312,
+  TALER_EC_REFRESH_MELT_FEES_EXCEED_CONTRIBUTION = 1305,
 
   /**
    * The denomination key signature on the melted coin is invalid.
    * This response is provided with HTTP status code
    * MHD_HTTP_BAD_REQUEST.
    */
-  TALER_EC_REFRESH_MELT_DENOMINATION_SIGNATURE_INVALID = 1313,
-
-  /**
-   * The exchange's cost calculation shows that the melt amount
-   * is below the costs of the transaction.  This response is provided
-   * with HTTP status code MHD_HTTP_BAD_REQUEST.
-   */
-  TALER_EC_REFRESH_MELT_AMOUNT_INSUFFICIENT = 1314,
+  TALER_EC_REFRESH_MELT_DENOMINATION_SIGNATURE_INVALID = 1306,
 
   /**
    * The signature made with the coin to be melted is invalid.
    * This response is provided with HTTP status code
    * MHD_HTTP_BAD_REQUEST.
    */
-  TALER_EC_REFRESH_MELT_COIN_SIGNATURE_INVALID = 1315,
-
-  /**
-   * The size of the cut-and-choose dimension of the
-   * blinded coins request does not match #TALER_CNC_KAPPA.
-   * This response is provided with HTTP status code
-   * MHD_HTTP_BAD_REQUEST.
-   */
-  TALER_EC_REFRESH_MELT_CNC_COIN_ARRAY_SIZE_INVALID = 1316,
-
-  /**
-   * The size of the cut-and-choose dimension of the
-   * transfer keys request does not match #TALER_CNC_KAPPA.
-   * This response is provided with HTTP status code
-   * MHD_HTTP_BAD_REQUEST.
-   */
-  TALER_EC_REFRESH_MELT_CNC_TRANSFER_ARRAY_SIZE_INVALID = 1317,
+  TALER_EC_REFRESH_MELT_COIN_SIGNATURE_INVALID = 1307,
 
   /**
    * The exchange failed to obtain the transaction history of the
@@ -556,7 +502,8 @@ enum TALER_ErrorCode
    * This response is provided with HTTP status code
    * MHD_HTTP_INTERNAL_SERVER_ERROR.
    */
-  TALER_EC_REFRESH_MELT_HISTORY_DB_ERROR_INSUFFICIENT_FUNDS = 1318,
+  TALER_EC_REFRESH_MELT_HISTORY_DB_ERROR_INSUFFICIENT_FUNDS = 1308,
+
 
   /**
    * The provided transfer keys do not match up with the
@@ -567,20 +514,12 @@ enum TALER_ErrorCode
   TALER_EC_REFRESH_REVEAL_COMMITMENT_VIOLATION = 1350,
 
   /**
-   * Failed to blind the envelope to reconstruct the blinded
-   * coins for revealation checks.
-   * This response is provided with HTTP status code
-   * MHD_HTTP_INTERNAL_ERROR.
-   */
-  TALER_EC_REFRESH_REVEAL_BLINDING_ERROR = 1351,
-
-  /**
    * Failed to produce the blinded signatures over the coins
    * to be returned.
    * This response is provided with HTTP status code
    * MHD_HTTP_INTERNAL_ERROR.
    */
-  TALER_EC_REFRESH_REVEAL_SIGNING_ERROR = 1352,
+  TALER_EC_REFRESH_REVEAL_SIGNING_ERROR = 1351,
 
   /**
    * The exchange is unaware of the refresh sessino specified in
@@ -588,7 +527,7 @@ enum TALER_ErrorCode
    * This response is provided with HTTP status code
    * MHD_HTTP_BAD_REQUEST.
    */
-  TALER_EC_REFRESH_REVEAL_SESSION_UNKNOWN = 1353,
+  TALER_EC_REFRESH_REVEAL_SESSION_UNKNOWN = 1352,
 
   /**
    * The exchange failed to retrieve valid session data from the
@@ -596,39 +535,68 @@ enum TALER_ErrorCode
    * This response is provided with HTTP status code
    * MHD_HTTP_INTERNAL_ERROR.
    */
-  TALER_EC_REFRESH_REVEAL_DB_FETCH_SESSION_ERROR = 1354,
+  TALER_EC_REFRESH_REVEAL_DB_FETCH_SESSION_ERROR = 1353,
 
   /**
-   * The exchange failed to retrieve order data from the
-   * database.
-   * This response is provided with HTTP status code
+   * The exchange failed to retrieve previously revealed data from the
+   * database.  This response is provided with HTTP status code
    * MHD_HTTP_INTERNAL_ERROR.
    */
-  TALER_EC_REFRESH_REVEAL_DB_FETCH_ORDER_ERROR = 1355,
+  TALER_EC_REFRESH_REVEAL_DB_FETCH_REVEAL_ERROR = 1354,
 
   /**
-   * The exchange failed to retrieve transfer keys from the
+   * The exchange failed to retrieve commitment data from the
    * database.
    * This response is provided with HTTP status code
    * MHD_HTTP_INTERNAL_ERROR.
    */
-  TALER_EC_REFRESH_REVEAL_DB_FETCH_TRANSFER_ERROR = 1356,
+  TALER_EC_REFRESH_REVEAL_DB_COMMIT_ERROR = 1355,
 
   /**
-   * The exchange failed to retrieve commitment data from the
-   * database.
+   * The size of the cut-and-choose dimension of the
+   * private transfer keys request does not match #TALER_CNC_KAPPA - 1.
    * This response is provided with HTTP status code
-   * MHD_HTTP_INTERNAL_ERROR.
+   * MHD_HTTP_BAD_REQUEST.
    */
-  TALER_EC_REFRESH_REVEAL_DB_FETCH_COMMIT_ERROR = 1357,
+  TALER_EC_REFRESH_REVEAL_CNC_TRANSFER_ARRAY_SIZE_INVALID = 1356,
 
   /**
-   * The size of the cut-and-choose dimension of the
+   * The number of coins to be created in refresh exceeds the limits
+   * of the exchange.
    * private transfer keys request does not match #TALER_CNC_KAPPA - 1.
    * This response is provided with HTTP status code
    * MHD_HTTP_BAD_REQUEST.
    */
-  TALER_EC_REFRESH_REVEAL_CNC_TRANSFER_ARRAY_SIZE_INVALID = 1358,
+  TALER_EC_REFRESH_REVEAL_NEW_DENOMS_ARRAY_SIZE_EXCESSIVE = 1357,
+
+  /**
+   * The number of envelopes given does not match the number
+   * of denomination keys given.
+   * This response is provided with HTTP status code
+   * MHD_HTTP_BAD_REQUEST.
+   */
+  TALER_EC_REFRESH_REVEAL_NEW_DENOMS_ARRAY_SIZE_MISSMATCH = 1358,
+
+  /**
+   * The exchange encountered a numeric overflow totaling up
+   * the cost for the refresh operation.  This response is provided
+   * with HTTP status code MHD_HTTP_INTERNAL_SERVER_ERROR.
+   */
+  TALER_EC_REFRESH_REVEAL_COST_CALCULATION_OVERFLOW = 1359,
+
+  /**
+   * The exchange's cost calculation shows that the melt amount
+   * is below the costs of the transaction.  This response is provided
+   * with HTTP status code MHD_HTTP_BAD_REQUEST.
+   */
+  TALER_EC_REFRESH_REVEAL_AMOUNT_INSUFFICIENT = 1360,
+
+  /**
+   * 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.
+   */
+  TALER_EC_REFRESH_REVEAL_FRESH_DENOMINATION_KEY_NOT_FOUND = 1361,
 
 
   /**
@@ -1303,13 +1271,25 @@ enum TALER_ErrorCode
    */
   TALER_EC_PROPOSAL_LOOKUP_DB_ERROR = 2502,
 
-
   /**
    * The proposal being looked up is not found on this merchant.
    */
   TALER_EC_PROPOSAL_LOOKUP_NOT_FOUND = 2503,
 
   /**
+   * The proposal had no timestamp and the backend failed to obtain the local 
time.
+   * Likely to be an internal error.
+   */
+  TALER_EC_PROPOSAL_NO_LOCALTIME = 2504,
+
+  /**
+   * The order provided to the backend could not be parsed, some
+   * required fields were missing or ill-formed.
+   * Returned as a bad request.
+   */
+  TALER_EC_PROPOSAL_ORDER_PARSE_ERROR = 2505,
+
+  /**
    * The client specified an unknown instance for any of the /refund operations
    */
   TALER_EC_REFUND_INSTANCE_UNKNOWN = 2600,
@@ -1520,6 +1500,34 @@ enum TALER_ErrorCode
    */
   TALER_EC_TEST_RSA_SIGN_ERROR = 4005,
 
+  /* *************** Taler BANK/FAKEBANK error codes *************** */
+
+
+  /**
+   * Authentication failed for the /admin/add/incoming request.
+   * Returned with a status code of MHD_HTTP_FORBIDDEN.
+   */
+  TALER_EC_BANK_TRANSFER_NOT_AUHTORIZED = 4101,
+
+  /**
+   * Authentication failed for the /history request.
+   * Returned with a status code of MHD_HTTP_FORBIDDEN.
+   */
+  TALER_EC_BANK_HISTORY_NOT_AUHTORIZED = 4151,
+
+  /**
+   * The bank could not find the wire transfer that was supposed to
+   * be rejected.
+   * Returned with a status code of MHD_HTTP_NOT_FOUND.
+   */
+  TALER_EC_BANK_REJECT_NOT_FOUND = 4250,
+
+  /**
+   * Authentication failed for the /reject request.
+   * Returned with a status code of MHD_HTTP_FORBIDDEN.
+   */
+  TALER_EC_BANK_REJECT_NOT_AUHTORIZED = 4251,
+
 
   /**
    * End of error code range.
diff --git a/src/include/taler_exchange_service.h 
b/src/include/taler_exchange_service.h
index fadcbf8..7e9ad91 100644
--- a/src/include/taler_exchange_service.h
+++ b/src/include/taler_exchange_service.h
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  Copyright (C) 2014-2017 GNUnet e.V.
+  Copyright (C) 2014-2017 Taler Systems SA
 
   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
@@ -1110,7 +1110,7 @@ struct TALER_EXCHANGE_RefreshMeltHandle;
  *                    0 if the exchange's reply is bogus (fails to follow the 
protocol)
  * @param ec taler-specific error code, #TALER_EC_NONE on success
  * @param noreveal_index choice by the exchange in the cut-and-choose protocol,
- *                    UINT16_MAX on error
+ *                    UINT32_MAX on error
  * @param sign_key exchange key used to sign @a full_response, or NULL
  * @param full_response full response from the exchange (for logging, in case 
of errors)
  */
@@ -1118,7 +1118,7 @@ typedef void
 (*TALER_EXCHANGE_RefreshMeltCallback) (void *cls,
                                        unsigned int http_status,
                                       enum TALER_ErrorCode ec,
-                                      uint16_t noreveal_index,
+                                      uint32_t noreveal_index,
                                        const struct TALER_ExchangePublicKeyP 
*sign_key,
                                        const json_t *full_response);
 
@@ -1223,7 +1223,7 @@ struct TALER_EXCHANGE_RefreshRevealHandle *
 TALER_EXCHANGE_refresh_reveal (struct TALER_EXCHANGE_Handle *exchange,
                                size_t refresh_data_length,
                                const char *refresh_data,
-                               uint16_t noreveal_index,
+                               uint32_t noreveal_index,
                                TALER_EXCHANGE_RefreshRevealCallback reveal_cb,
                                void *reveal_cb_cls);
 
diff --git a/src/include/taler_exchangedb_plugin.h 
b/src/include/taler_exchangedb_plugin.h
index 006ea39..e64b0ad 100644
--- a/src/include/taler_exchangedb_plugin.h
+++ b/src/include/taler_exchangedb_plugin.h
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  Copyright (C) 2014-2017 GNUnet e.V.
+  Copyright (C) 2014-2017 Taler Systems SA
 
   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
@@ -465,7 +465,7 @@ struct TALER_EXCHANGEDB_Refund
 /**
  * @brief Specification for coin in a /refresh/melt operation.
  */
-struct TALER_EXCHANGEDB_RefreshMelt
+struct TALER_EXCHANGEDB_RefreshSession
 {
   /**
    * Information about the coin that is being melted.
@@ -478,9 +478,9 @@ struct TALER_EXCHANGEDB_RefreshMelt
   struct TALER_CoinSpendSignatureP coin_sig;
 
   /**
-   * Hash of the refresh session this coin is melted into.
+   * Refresh commitment this coin is melted into.
    */
-  struct GNUNET_HashCode session_hash;
+  struct TALER_RefreshCommitmentP rc;
 
   /**
    * How much value is being melted?  This amount includes the fees,
@@ -493,64 +493,29 @@ struct TALER_EXCHANGEDB_RefreshMelt
   struct TALER_Amount amount_with_fee;
 
   /**
-   * Melting fee charged by the exchange.  This must match the Exchange's
-   * denomination key's melting fee.  If the client puts in an invalid
-   * melting fee (too high or too low) that does not match the Exchange's
-   * denomination key, the melting operation is invalid and will be
-   * rejected by the exchange.  The @e amount_with_fee minus the @e
-   * melt_fee is the amount that will be credited to the melting
-   * session.
-   */
-  struct TALER_Amount melt_fee;
-
-};
-
-
-/**
- * @brief Global information for a refreshing session.  Includes
- * dimensions of the operation, security parameters and
- * client signatures from "/refresh/melt" and "/refresh/commit".
- */
-struct TALER_EXCHANGEDB_RefreshSession
-{
-
-  /**
-   * Melt operation details.
-   */
-  struct TALER_EXCHANGEDB_RefreshMelt melt;
-
-  /**
-   * Number of new coins we are creating.
-   */
-  uint16_t num_newcoins;
-
-  /**
    * Index (smaller #TALER_CNC_KAPPA) which the exchange has chosen to not
    * have revealed during cut and choose.
    */
-  uint16_t noreveal_index;
+  uint32_t noreveal_index;
 
 };
 
 
 /**
- * @brief We have as many `struct TALER_EXCHANGEDB_RefreshCommitCoin` as there 
are new
- * coins being created by the refresh (for each of the #TALER_CNC_KAPPA
- * sets).  These are the coins we ask the exchange to sign if the
- * respective set is selected.
+ * Information about a /refresh/melt operation in the transaction history.
  */
-struct TALER_EXCHANGEDB_RefreshCommitCoin
+struct TALER_EXCHANGEDB_RefreshMelt
 {
 
   /**
-   * Blinded message to be signed (in envelope), with @e coin_env_size bytes.
+   * Overall session data.
    */
-  char *coin_ev;
+  struct TALER_EXCHANGEDB_RefreshSession session;
 
   /**
-   * Number of bytes in @e coin_ev.
+   * Melt fee the exchange charged.
    */
-  size_t coin_ev_size;
+  struct TALER_Amount melt_fee;
 
 };
 
@@ -752,9 +717,8 @@ typedef int
  * @param coin_pub public key of the coin
  * @param coin_sig signature from the coin
  * @param amount_with_fee amount that was deposited including fee
- * @param num_newcoins how many coins were issued
  * @param noreveal_index which index was picked by the exchange in 
cut-and-choose
- * @param session_hash what is the session hash
+ * @param rc what is the commitment
  * @return #GNUNET_OK to continue to iterate, #GNUNET_SYSERR to stop
  */
 typedef int
@@ -764,9 +728,56 @@ typedef int
                                            const struct 
TALER_CoinSpendPublicKeyP *coin_pub,
                                            const struct 
TALER_CoinSpendSignatureP *coin_sig,
                                            const struct TALER_Amount 
*amount_with_fee,
-                                           uint16_t num_newcoins,
-                                           uint16_t noreveal_index,
-                                           const struct GNUNET_HashCode 
*session_hash);
+                                           uint32_t noreveal_index,
+                                           const struct 
TALER_RefreshCommitmentP *rc);
+
+
+/**
+ * Information about a coin that was revealed to the exchange
+ * during /refresh/reveal.
+ */
+struct TALER_EXCHANGEDB_RefreshRevealedCoin
+{
+  /**
+   * Public denomination key of the coin.
+   */
+  struct TALER_DenominationPublicKey denom_pub;
+
+  /**
+   * Blinded message to be signed (in envelope), with @e coin_env_size bytes.
+   */
+  char *coin_ev;
+
+  /**
+   * Number of bytes in @e coin_ev.
+   */
+  size_t coin_ev_size;
+
+  /**
+   * Signature generated by the exchange over the coin (in blinded format).
+   */
+  struct TALER_DenominationSignature coin_sig;
+};
+
+
+/**
+ * Function called with information about a refresh order.
+ *
+ * @param cls closure
+ * @param rowid unique serial ID for the row in our database
+ * @param num_newcoins size of the @a rrcs array
+ * @param rrcs array of @a num_newcoins information about coins to be created
+ * @param num_tprivs number of entries in @a tprivs, should be 
#TALER_CNC_KAPPA - 1
+ * @param tprivs array of @e num_tprivs transfer private keys
+ * @param tp transfer public key information
+ */
+typedef void
+(*TALER_EXCHANGEDB_RefreshCallback)(void *cls,
+                                    uint32_t num_newcoins,
+                                    const struct 
TALER_EXCHANGEDB_RefreshRevealedCoin *rrcs,
+                                    unsigned int num_tprivs,
+                                    const struct TALER_TransferPrivateKeyP 
*tprivs,
+                                    const struct TALER_TransferPublicKeyP *tp);
 
 
 /**
@@ -851,14 +862,13 @@ typedef int
  * information for a given coin.
  *
  * @param cls closure
- * @param session_hash a session the coin was melted in
  * @param transfer_pub public transfer key for the session
- * @param shared_secret_enc set to shared secret for the session
+ * @param ldl link data for @a transfer_pub
  */
 typedef void
-(*TALER_EXCHANGEDB_TransferDataCallback)(void *cls,
-                                         const struct GNUNET_HashCode 
*session_hash,
-                                         const struct TALER_TransferPublicKeyP 
*transfer_pub);
+(*TALER_EXCHANGEDB_LinkDataCallback)(void *cls,
+                                     const struct TALER_TransferPublicKeyP 
*transfer_pub,
+                                     const struct 
TALER_EXCHANGEDB_LinkDataList *ldl);
 
 
 /**
@@ -1235,7 +1245,7 @@ struct TALER_EXCHANGEDB_Plugin
    * @param[out] wire_reference_size set to number of bytes in @a 
wire_reference
    * @return transaction status code
    */
-  enum GNUNET_DB_QueryStatus 
+  enum GNUNET_DB_QueryStatus
   (*get_latest_reserve_in_reference)(void *cls,
                                      struct TALER_EXCHANGEDB_Session *db,
                                      void **wire_reference,
@@ -1254,7 +1264,7 @@ struct TALER_EXCHANGEDB_Plugin
    *                    if a coin is found
    * @return statement execution status
    */
-  enum GNUNET_DB_QueryStatus 
+  enum GNUNET_DB_QueryStatus
   (*get_withdraw_info) (void *cls,
                         struct TALER_EXCHANGEDB_Session *session,
                         const struct GNUNET_HashCode *h_blind,
@@ -1454,225 +1464,79 @@ struct TALER_EXCHANGEDB_Plugin
 
 
   /**
-   * Lookup refresh session data under the given @a session_hash.
+   * Store new refresh melt commitment data.
    *
    * @param cls the @e cls of this struct with the plugin-specific state
    * @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 transaction status
-   */
-  enum GNUNET_DB_QueryStatus
-  (*get_refresh_session) (void *cls,
-                          struct TALER_EXCHANGEDB_Session *session,
-                          const struct GNUNET_HashCode *session_hash,
-                          struct TALER_EXCHANGEDB_RefreshSession 
*refresh_session);
-
-
-  /**
-   * Store new refresh session data under the given @a session_hash.
-   *
-   * @param cls the @e cls of this struct 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 refresh_session session data to store
-   * @return query status for the transaction
-   */
-  enum GNUNET_DB_QueryStatus
-  (*create_refresh_session) (void *cls,
-                             struct TALER_EXCHANGEDB_Session *session,
-                             const struct GNUNET_HashCode *session_hash,
-                             const struct TALER_EXCHANGEDB_RefreshSession 
*refresh_session);
-
-
-  /**
-   * Store in the database which coin(s) we want to create
-   * in a given refresh operation.
-   *
-   * @param cls the @e cls of this struct with the plugin-specific state
-   * @param session database connection
-   * @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 query status for the transaction
-   */
-  enum GNUNET_DB_QueryStatus
-  (*insert_refresh_order) (void *cls,
-                           struct TALER_EXCHANGEDB_Session *session,
-                           const struct GNUNET_HashCode *session_hash,
-                           uint16_t num_newcoins,
-                           const struct TALER_DenominationPublicKey 
*denom_pubs);
-
-
-  /**
-   * Lookup in the database for the @a num_newcoins coins that we want to
-   * create in the given refresh operation.
-   *
-   * @param cls the @e cls of this struct with the plugin-specific state
-   * @param session database connection
-   * @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 transaction status
-   */
-  enum GNUNET_DB_QueryStatus
-  (*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);
-
-
-  /**
-   * Store information about the commitments of the given index @a i
-   * for the given refresh session in the database.
-   *
-   * @param cls the @e cls of this struct with the plugin-specific state
-   * @param session database connection to use
-   * @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
+   * @param refresh_session operational data to store
    * @return query status for the transaction
    */
   enum GNUNET_DB_QueryStatus
-  (*insert_refresh_commit_coins) (void *cls,
-                                  struct TALER_EXCHANGEDB_Session *session,
-                                  const struct GNUNET_HashCode *session_hash,
-                                  uint16_t num_newcoins,
-                                  const struct 
TALER_EXCHANGEDB_RefreshCommitCoin *commit_coins);
+  (*insert_melt) (void *cls,
+                  struct TALER_EXCHANGEDB_Session *session,
+                  const struct TALER_EXCHANGEDB_RefreshSession 
*refresh_session);
 
 
   /**
-   * Obtain information about the commitment of the
-   * given coin of the given refresh session from the database.
+   * Lookup refresh metl commitment data under the given @a rc.
    *
    * @param cls the @e cls of this struct with the plugin-specific state
-   * @param session database connection to use
-   * @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
+   * @param session database handle to use
+   * @param rc commitment to use for the lookup
+   * @param[out] refresh_melt where to store the result
    * @return transaction status
    */
   enum GNUNET_DB_QueryStatus
-  (*get_refresh_commit_coins) (void *cls,
-                               struct TALER_EXCHANGEDB_Session *session,
-                               const struct GNUNET_HashCode *session_hash,
-                               uint16_t num_coins,
-                               struct TALER_EXCHANGEDB_RefreshCommitCoin 
*commit_coins);
-
-  /**
-   * Free refresh @a commit_coins data obtained via @e 
get_refresh_commit_coins.
-   *
-   * @param cls the @e cls of this struct with the plugin-specific state
-   * @param num_coins size of the @a commit_coins array
-   * @param commit_coins array of coin commitments to free
-   */
-  void
-  (*free_refresh_commit_coins) (void *cls,
-                                unsigned int num_coins,
-                                struct TALER_EXCHANGEDB_RefreshCommitCoin 
*commit_coins);
+  (*get_melt) (void *cls,
+               struct TALER_EXCHANGEDB_Session *session,
+               const struct TALER_RefreshCommitmentP *rc,
+               struct TALER_EXCHANGEDB_RefreshMelt *refresh_melt);
 
 
   /**
-   * Store the commitment to the given (encrypted) refresh link data
-   * for the given refresh session.
+   * Store in the database which coin(s) the wallet wanted to create
+   * in a given refresh operation and all of the other information
+   * we learned or created in the /refresh/reveal step.
    *
    * @param cls the @e cls of this struct with the plugin-specific state
-   * @param session database connection to use
-   * @param session_hash hash to identify refresh session
+   * @param session database connection
+   * @param rc identify commitment and thus refresh operation
+   * @param num_rrcs_newcoins number of coins to generate, size of the
+   *            @a rrcs array
+   * @param rrcs information about the new coins
+   * @param num_tprivs number of entries in @a tprivs, should be 
#TALER_CNC_KAPPA - 1
+   * @param tprivs transfer private keys to store
    * @param tp public key to store
    * @return query status for the transaction
    */
   enum GNUNET_DB_QueryStatus
-  (*insert_refresh_transfer_public_key) (void *cls,
-                                         struct TALER_EXCHANGEDB_Session 
*session,
-                                         const struct GNUNET_HashCode 
*session_hash,
-                                         const struct TALER_TransferPublicKeyP 
*tp);
-
-  /**
-   * Obtain the commited (encrypted) refresh link data
-   * for the given refresh session.
-   *
-   * @param cls the @e cls of this struct with the plugin-specific state
-   * @param session database connection to use
-   * @param session_hash hash to identify refresh session
-   * @param[out] tp information to return
-   * @return transaction status
-   */
-  enum GNUNET_DB_QueryStatus
-  (*get_refresh_transfer_public_key) (void *cls,
-                                      struct TALER_EXCHANGEDB_Session *session,
-                                      const struct GNUNET_HashCode 
*session_hash,
-                                      struct TALER_TransferPublicKeyP *tp);
-
-
-  /**
-   * Get signature of a new coin generated during refresh into
-   * the database indexed by the refresh session and the index
-   * of the coin.
-   *
-   * @param cls the @e cls of this struct with the plugin-specific state
-   * @param session database connection
-   * @param session_hash hash to identify refresh session
-   * @param newcoin_index coin index
-   * @param[out] ev_sig coin signature
-   * @return transaction result status
-   */
-  enum GNUNET_DB_QueryStatus
-  (*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);
-
-
-  /**
-   * Insert signature of a new coin generated during refresh into
-   * the database indexed by the refresh session and the index
-   * of the coin.  This data is later used should an old coin
-   * be used to try to obtain the private keys during "/refresh/link".
-   *
-   * @param cls the @e cls of this struct with the plugin-specific state
-   * @param session database connection
-   * @param session_hash hash to identify refresh session
-   * @param newcoin_index coin index
-   * @param ev_sig coin signature
-   * @return transaction result status
-   */
-  enum GNUNET_DB_QueryStatus
-  (*insert_refresh_out) (void *cls,
-                         struct TALER_EXCHANGEDB_Session *session,
-                         const struct GNUNET_HashCode *session_hash,
-                         uint16_t newcoin_index,
-                         const struct TALER_DenominationSignature *ev_sig);
+  (*insert_refresh_reveal) (void *cls,
+                            struct TALER_EXCHANGEDB_Session *session,
+                            const struct TALER_RefreshCommitmentP *rc,
+                            uint32_t num_rrcs,
+                            const struct TALER_EXCHANGEDB_RefreshRevealedCoin 
*rrcs,
+                            unsigned int num_tprivs,
+                            const struct TALER_TransferPrivateKeyP *tprivs,
+                            const struct TALER_TransferPublicKeyP *tp);
 
 
   /**
-   * Obtain the link data of a coin, that is the encrypted link
-   * information, the denomination keys and the signatures.
+   * Lookup in the database for the @a num_newcoins coins that we
+   * created in the given refresh operation.
    *
    * @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
-   * @param[out] ldldp set to all known link data for the session
-   * @return status of the transaction
+   * @param rc identify commitment and thus refresh operation
+   * @param cb function to call with the results
+   * @param cb_cls closure for @a cb
+   * @return transaction status
    */
   enum GNUNET_DB_QueryStatus
-  (*get_link_data_list) (void *cls,
+  (*get_refresh_reveal) (void *cls,
                          struct TALER_EXCHANGEDB_Session *session,
-                         const struct GNUNET_HashCode *session_hash,
-                        struct TALER_EXCHANGEDB_LinkDataList **ldlp);
-
-
-  /**
-   * Free memory of the link data list.
-   *
-   * @param cls the @e cls of this struct with the plugin-specific state
-   * @param ldl link data list to release
-   */
-  void
-  (*free_link_data_list) (void *cls,
-                          struct TALER_EXCHANGEDB_LinkDataList *ldl);
+                         const struct TALER_RefreshCommitmentP *rc,
+                         TALER_EXCHANGEDB_RefreshCallback cb,
+                         void *cb_cls);
 
 
   /**
@@ -1684,16 +1548,16 @@ struct TALER_EXCHANGEDB_Plugin
    * @param cls the @e cls of this struct 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
+   * @param ldc function to call for each session the coin was melted into
+   * @param ldc_cls closure for @a tdc
    * @return statement execution status
    */
   enum GNUNET_DB_QueryStatus
-  (*get_transfer) (void *cls,
-                   struct TALER_EXCHANGEDB_Session *session,
-                   const struct TALER_CoinSpendPublicKeyP *coin_pub,
-                   TALER_EXCHANGEDB_TransferDataCallback tdc,
-                   void *tdc_cls);
+  (*get_link_data) (void *cls,
+                    struct TALER_EXCHANGEDB_Session *session,
+                    const struct TALER_CoinSpendPublicKeyP *coin_pub,
+                    TALER_EXCHANGEDB_LinkDataCallback ldc,
+                    void *tdc_cls);
 
 
   /**
@@ -1806,7 +1670,7 @@ struct TALER_EXCHANGEDB_Plugin
                      const struct TALER_Amount *wire_fee,
                      const struct TALER_MasterSignatureP *master_sig);
 
-  
+
   /**
    * Obtain wire fee from database.
    *
@@ -2233,7 +2097,7 @@ struct TALER_EXCHANGEDB_Plugin
                                  struct GNUNET_TIME_Absolute end_date,
                                  TALER_EXCHANGEDB_WireMissingCallback cb,
                                  void *cb_cls);
-  
+
 
 };
 
diff --git a/src/include/taler_fakebank_lib.h b/src/include/taler_fakebank_lib.h
index 3df1e60..5e0d7d1 100644
--- a/src/include/taler_fakebank_lib.h
+++ b/src/include/taler_fakebank_lib.h
@@ -88,6 +88,8 @@ TALER_FAKEBANK_make_transfer (struct TALER_FAKEBANK_Handle *h,
  * to the transfer identifier and remove the transaction from the
  * list.  If the transaction was not recorded, return #GNUNET_SYSERR.
  *
+ * Rejected transfers do NOT show with "check".
+ *
  * @param h bank instance
  * @param want_amount transfer amount desired
  * @param want_debit account that should have been debited
@@ -107,6 +109,21 @@ TALER_FAKEBANK_check (struct TALER_FAKEBANK_Handle *h,
 
 
 /**
+ * Reject incoming wire transfer to account @a credit_account
+ * as identified by @a rowid.
+ *
+ * @param h fake bank handle
+ * @param rowid identifies transfer to reject
+ * @param credit_account account number of owner of credited account
+ * @return #GNUNET_YES on success, #GNUNET_NO if the wire transfer was not 
found
+ */
+int
+TALER_FAKEBANK_reject_transfer (struct TALER_FAKEBANK_Handle *h,
+                                uint64_t rowid,
+                                uint64_t credit_account);
+
+
+/**
  * Stop running the fake bank.
  *
  * @param h bank to stop
diff --git a/src/include/taler_pq_lib.h b/src/include/taler_pq_lib.h
index 1fda8e5..4ac0a6d 100644
--- a/src/include/taler_pq_lib.h
+++ b/src/include/taler_pq_lib.h
@@ -65,6 +65,32 @@ TALER_PQ_query_param_json (const json_t *x);
 
 
 /**
+ * Generate query parameter for an absolute time value.
+ * In contrast to
+ * #GNUNET_PQ_query_param_absolute_time(), this function
+ * will abort (!) if the time given is not rounded!
+ * The database must store a 64-bit integer.
+ *
+ * @param x pointer to the query parameter to pass
+ */
+struct GNUNET_PQ_QueryParam
+TALER_PQ_query_param_absolute_time (const struct GNUNET_TIME_Absolute *x);
+
+
+/**
+ * Generate query parameter for an absolute time value.
+ * In contrast to
+ * #GNUNET_PQ_query_param_absolute_time(), this function
+ * will abort (!) if the time given is not rounded!
+ * The database must store a 64-bit integer.
+ *
+ * @param x pointer to the query parameter to pass
+ */
+struct GNUNET_PQ_QueryParam
+TALER_PQ_query_param_absolute_time_nbo (const struct GNUNET_TIME_AbsoluteNBO 
*x);
+
+
+/**
  * Currency amount expected.
  *
  * @param name name of the field in the table
@@ -100,6 +126,36 @@ TALER_PQ_result_spec_json (const char *name,
                            json_t **jp);
 
 
+/**
+ * Rounded absolute time expected.
+ * In contrast to #GNUNET_PQ_query_param_absolute_time_nbo(),
+ * this function ensures that the result is rounded and can
+ * be converted to JSON.
+ *
+ * @param name name of the field in the table
+ * @param[out] at where to store the result
+ * @return array entry for the result specification to use
+ */
+struct GNUNET_PQ_ResultSpec
+TALER_PQ_result_spec_absolute_time (const char *name,
+                                    struct GNUNET_TIME_Absolute *at);
+
+
+/**
+ * Rounded absolute time expected.
+ * In contrast to #GNUNET_PQ_result_spec_absolute_time_nbo(),
+ * this function ensures that the result is rounded and can
+ * be converted to JSON.
+ *
+ * @param name name of the field in the table
+ * @param[out] at where to store the result
+ * @return array entry for the result specification to use
+ */
+struct GNUNET_PQ_ResultSpec
+TALER_PQ_result_spec_absolute_time_nbo (const char *name,
+                                        struct GNUNET_TIME_AbsoluteNBO *at);
+
+
 #endif  /* TALER_PQ_LIB_H_ */
 
 /* end of include/taler_pq_lib.h */
diff --git a/src/include/taler_signatures.h b/src/include/taler_signatures.h
index db71cca..6355303 100644
--- a/src/include/taler_signatures.h
+++ b/src/include/taler_signatures.h
@@ -532,9 +532,9 @@ struct TALER_RefreshMeltCoinAffirmationPS
   struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
 
   /**
-   * Which melting session should the coin become a part of.
+   * Which melt commitment is made by the wallet.
    */
-  struct GNUNET_HashCode session_hash GNUNET_PACKED;
+  struct TALER_RefreshCommitmentP rc GNUNET_PACKED;
 
   /**
    * How much of the value of the coin should be melted?  This amount
@@ -581,20 +581,16 @@ struct TALER_RefreshMeltConfirmationPS
   struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
 
   /**
-   * Hash of the refresh session.
+   * Commitment made in the /refresh/melt.
    */
-  struct GNUNET_HashCode session_hash GNUNET_PACKED;
+  struct TALER_RefreshCommitmentP rc GNUNET_PACKED;
 
   /**
    * Index that the client will not have to reveal, in NBO.
    * Must be smaller than #TALER_CNC_KAPPA.
    */
-  uint16_t noreveal_index GNUNET_PACKED;
+  uint32_t noreveal_index GNUNET_PACKED;
 
-  /**
-   * Zero.
-   */
-  uint16_t reserved GNUNET_PACKED;
 };
 
 
@@ -1283,12 +1279,13 @@ struct TALER_ReserveCloseConfirmationPS
 struct TALER_MerchantRefundConfirmationPS
 {
   /**
-   * Set TALER_SIGNATURE_REFUND_OK.
+   * Set to #TALER_SIGNATURE_MERCHANT_REFUND_OK.
    */
   struct GNUNET_CRYPTO_EccSignaturePurpose purpose;
 
   /**
    * Hashed order id; in case frontend wants to check it.
+   * Hashed without the 0-termination.
    */
   struct GNUNET_HashCode h_order_id GNUNET_PACKED;
 
diff --git a/src/include/taler_wire_plugin.h b/src/include/taler_wire_plugin.h
index 6e355ba..c0e2960 100644
--- a/src/include/taler_wire_plugin.h
+++ b/src/include/taler_wire_plugin.h
@@ -83,6 +83,7 @@ struct TALER_WIRE_TransferDetails
  * the bank for the transaction history.
  *
  * @param cls closure
+ * @param ec taler error code
  * @param dir direction of the transfer
  * @param row_off identification of the position at which we are querying
  * @param row_off_size number of bytes in @a row_off
@@ -91,6 +92,7 @@ struct TALER_WIRE_TransferDetails
  */
 typedef int
 (*TALER_WIRE_HistoryResultCallback) (void *cls,
+                                     enum TALER_ErrorCode ec,
                                      enum TALER_BANK_Direction dir,
                                      const void *row_off,
                                      size_t row_off_size,
diff --git a/src/pq/pq_query_helper.c b/src/pq/pq_query_helper.c
index d6418a1..6386749 100644
--- a/src/pq/pq_query_helper.c
+++ b/src/pq/pq_query_helper.c
@@ -205,4 +205,127 @@ TALER_PQ_query_param_json (const json_t *x)
 }
 
 
+/**
+ * Function called to convert input argument into SQL parameters.
+ *
+ * @param cls closure
+ * @param data pointer to input argument
+ * @param data_len number of bytes in @a data (if applicable)
+ * @param[out] param_values SQL data to set
+ * @param[out] param_lengths SQL length data to set
+ * @param[out] param_formats SQL format data to set
+ * @param param_length number of entries available in the @a param_values, @a 
param_lengths and @a param_formats arrays
+ * @param[out] scratch buffer for dynamic allocations (to be done via 
#GNUNET_malloc()
+ * @param scratch_length number of entries left in @a scratch
+ * @return -1 on error, number of offsets used in @a scratch otherwise
+ */
+static int
+qconv_round_time (void *cls,
+                  const void *data,
+                  size_t data_len,
+                  void *param_values[],
+                  int param_lengths[],
+                  int param_formats[],
+                  unsigned int param_length,
+                  void *scratch[],
+                  unsigned int scratch_length)
+{
+  const struct GNUNET_TIME_Absolute *at = data;
+  struct GNUNET_TIME_Absolute tmp;
+  struct GNUNET_TIME_AbsoluteNBO *buf;
+
+  GNUNET_break (NULL == cls);
+  if (1 != param_length)
+    return -1;
+  tmp = *at;
+  GNUNET_assert (GNUNET_OK ==
+                 GNUNET_TIME_round_abs (&tmp));
+  buf = GNUNET_new (struct GNUNET_TIME_AbsoluteNBO);
+  *buf = GNUNET_TIME_absolute_hton (tmp);
+  scratch[0] = buf;
+  param_values[0] = (void *) buf;
+  param_lengths[0] = sizeof (struct GNUNET_TIME_AbsoluteNBO);
+  param_formats[0] = 1;
+  return 1;
+}
+
+
+/**
+ * Generate query parameter for an absolute time value.
+ * In contrast to
+ * #GNUNET_PQ_query_param_absolute_time(), this function
+ * will abort (!) if the time given is not rounded!
+ * The database must store a 64-bit integer.
+ *
+ * @param x pointer to the query parameter to pass
+ * @return array entry for the query parameters to use
+ */
+struct GNUNET_PQ_QueryParam
+TALER_PQ_query_param_absolute_time (const struct GNUNET_TIME_Absolute *x)
+{
+  struct GNUNET_PQ_QueryParam res =
+    { &qconv_round_time, NULL, x, sizeof (*x), 1 };
+  return res;
+}
+
+
+/**
+ * Function called to convert input argument into SQL parameters.
+ *
+ * @param cls closure
+ * @param data pointer to input argument
+ * @param data_len number of bytes in @a data (if applicable)
+ * @param[out] param_values SQL data to set
+ * @param[out] param_lengths SQL length data to set
+ * @param[out] param_formats SQL format data to set
+ * @param param_length number of entries available in the @a param_values, @a 
param_lengths and @a param_formats arrays
+ * @param[out] scratch buffer for dynamic allocations (to be done via 
#GNUNET_malloc()
+ * @param scratch_length number of entries left in @a scratch
+ * @return -1 on error, number of offsets used in @a scratch otherwise
+ */
+static int
+qconv_round_time_abs (void *cls,
+                      const void *data,
+                      size_t data_len,
+                      void *param_values[],
+                      int param_lengths[],
+                      int param_formats[],
+                      unsigned int param_length,
+                      void *scratch[],
+                      unsigned int scratch_length)
+{
+  const struct GNUNET_TIME_AbsoluteNBO *at = data;
+  struct GNUNET_TIME_Absolute tmp;
+
+  GNUNET_break (NULL == cls);
+  if (1 != param_length)
+    return -1;
+  tmp = GNUNET_TIME_absolute_ntoh (*at);
+  GNUNET_assert (GNUNET_OK ==
+                 GNUNET_TIME_round_abs (&tmp));
+  param_values[0] = (void *) at;
+  param_lengths[0] = sizeof (struct GNUNET_TIME_AbsoluteNBO);
+  param_formats[0] = 1;
+  return 0;
+}
+
+
+/**
+ * Generate query parameter for an absolute time value.
+ * In contrast to
+ * #GNUNET_PQ_query_param_absolute_time(), this function
+ * will abort (!) if the time given is not rounded!
+ * The database must store a 64-bit integer.
+ *
+ * @param x pointer to the query parameter to pass
+ */
+struct GNUNET_PQ_QueryParam
+TALER_PQ_query_param_absolute_time_nbo(const struct GNUNET_TIME_AbsoluteNBO *x)
+{
+  struct GNUNET_PQ_QueryParam res =
+    { &qconv_round_time_abs, NULL, x, sizeof (*x), 1 };
+  return res;
+}
+
+
 /* end of pq/pq_query_helper.c */
diff --git a/src/pq/pq_result_helper.c b/src/pq/pq_result_helper.c
index d0ff78d..c1395b7 100644
--- a/src/pq/pq_result_helper.c
+++ b/src/pq/pq_result_helper.c
@@ -370,4 +370,164 @@ TALER_PQ_result_spec_json (const char *name,
   return res;
 }
 
+
+
+/**
+ * Extract data from a Postgres database @a result at row @a row.
+ *
+ * @param cls closure
+ * @param result where to extract data from
+ * @param int row to extract data from
+ * @param fname name (or prefix) of the fields to extract from
+ * @param[in,out] dst_size where to store size of result, may be NULL
+ * @param[out] dst where to store the result
+ * @return
+ *   #GNUNET_YES if all results could be extracted
+ *   #GNUNET_SYSERR if a result was invalid (non-existing field or NULL)
+ */
+static int
+extract_round_time (void *cls,
+                    PGresult *result,
+                    int row,
+                    const char *fname,
+                    size_t *dst_size,
+                    void *dst)
+{
+  struct GNUNET_TIME_Absolute *udst = dst;
+  const struct GNUNET_TIME_AbsoluteNBO *res;
+  struct GNUNET_TIME_Absolute tmp;
+  int fnum;
+
+  fnum = PQfnumber (result,
+                   fname);
+  if (fnum < 0)
+  {
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+  if (PQgetisnull (result,
+                  row,
+                  fnum))
+  {
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+  GNUNET_assert (NULL != dst);
+  if (sizeof (struct GNUNET_TIME_Absolute) != *dst_size)
+  {
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+  res = (struct GNUNET_TIME_AbsoluteNBO *) PQgetvalue (result,
+                                                       row,
+                                                       fnum);
+  tmp = GNUNET_TIME_absolute_ntoh (*res);
+  GNUNET_break (GNUNET_OK ==
+                GNUNET_TIME_round_abs (&tmp));
+  *udst = tmp;
+  return GNUNET_OK;
+}
+
+
+/**
+ * Rounded absolute time expected.
+ * In contrast to #GNUNET_PQ_query_param_absolute_time_nbo(),
+ * this function ensures that the result is rounded and can
+ * be converted to JSON.
+ *
+ * @param name name of the field in the table
+ * @param[out] at where to store the result
+ * @return array entry for the result specification to use
+ */
+struct GNUNET_PQ_ResultSpec
+TALER_PQ_result_spec_absolute_time (const char *name,
+                                    struct GNUNET_TIME_Absolute *at)
+{
+  struct GNUNET_PQ_ResultSpec res =
+    { &extract_round_time, NULL, NULL,
+      (void *) at, sizeof (struct GNUNET_TIME_Absolute),
+      name, NULL };
+  return res;
+}
+
+
+/**
+ * Extract data from a Postgres database @a result at row @a row.
+ *
+ * @param cls closure
+ * @param result where to extract data from
+ * @param int row to extract data from
+ * @param fname name (or prefix) of the fields to extract from
+ * @param[in,out] dst_size where to store size of result, may be NULL
+ * @param[out] dst where to store the result
+ * @return
+ *   #GNUNET_YES if all results could be extracted
+ *   #GNUNET_SYSERR if a result was invalid (non-existing field or NULL)
+ */
+static int
+extract_round_time_nbo (void *cls,
+                        PGresult *result,
+                        int row,
+                        const char *fname,
+                        size_t *dst_size,
+                        void *dst)
+{
+  struct GNUNET_TIME_AbsoluteNBO *udst = dst;
+  const struct GNUNET_TIME_AbsoluteNBO *res;
+  struct GNUNET_TIME_Absolute tmp;
+  int fnum;
+
+  fnum = PQfnumber (result,
+                   fname);
+  if (fnum < 0)
+  {
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+  if (PQgetisnull (result,
+                  row,
+                  fnum))
+  {
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+  GNUNET_assert (NULL != dst);
+  if (sizeof (struct GNUNET_TIME_AbsoluteNBO) != *dst_size)
+  {
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+  res = (struct GNUNET_TIME_AbsoluteNBO *) PQgetvalue (result,
+                                                       row,
+                                                       fnum);
+  tmp = GNUNET_TIME_absolute_ntoh (*res);
+  GNUNET_break (GNUNET_OK ==
+                GNUNET_TIME_round_abs (&tmp));
+  *udst = GNUNET_TIME_absolute_hton (tmp);
+  return GNUNET_OK;
+}
+
+
+/**
+ * Rounded absolute time in network byte order expected.
+ * In contrast to #GNUNET_PQ_query_param_absolute_time_nbo(),
+ * this function ensures that the result is rounded and can
+ * be converted to JSON.
+ *
+ * @param name name of the field in the table
+ * @param[out] at where to store the result
+ * @return array entry for the result specification to use
+ */
+struct GNUNET_PQ_ResultSpec
+TALER_PQ_result_spec_absolute_time_nbo (const char *name,
+                                        struct GNUNET_TIME_AbsoluteNBO *at)
+{
+  struct GNUNET_PQ_ResultSpec res =
+    { &extract_round_time_nbo, NULL, NULL,
+      (void *) at, sizeof (struct GNUNET_TIME_AbsoluteNBO),
+      name, NULL };
+  return res;
+}
+
+
 /* end of pq_result_helper.c */
diff --git a/src/util/crypto.c b/src/util/crypto.c
index efc7485..b44c31f 100644
--- a/src/util/crypto.c
+++ b/src/util/crypto.c
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  Copyright (C) 2014, 2015 GNUnet e.V.
+  Copyright (C) 2014-2017 Taler Systems SA
 
   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
@@ -206,7 +206,7 @@ patch_private_key (struct GNUNET_CRYPTO_EddsaPrivateKey *pk)
  */
 void
 TALER_planchet_setup_refresh (const struct TALER_TransferSecretP *secret_seed,
-                              unsigned int coin_num_salt,
+                              uint32_t coin_num_salt,
                               struct TALER_PlanchetSecretsP *ps)
 {
   uint32_t be_salt = htonl (coin_num_salt);
@@ -314,4 +314,87 @@ TALER_planchet_to_coin (const struct 
TALER_DenominationPublicKey *dk,
   return GNUNET_OK;
 }
 
+
+/**
+ * Compute the commitment for a /refresh/melt operation from
+ * the respective public inputs.
+ *
+ * @param[out] rc set to the value the wallet must commit to
+ * @param kappa number of transfer public keys involved (must be 
#TALER_CNC_KAPPA)
+ * @param num_new_coins number of new coins to be created
+ * @param commitments array of @a kappa commitments
+ * @param coin_pub public key of the coin to be melted
+ * @param amount_with_fee amount to be melted, including fee
+ */
+void
+TALER_refresh_get_commitment (struct TALER_RefreshCommitmentP *rc,
+                              uint32_t kappa,
+                              uint32_t num_new_coins,
+                              const struct TALER_RefreshCommitmentEntry *rcs,
+                              const struct TALER_CoinSpendPublicKeyP *coin_pub,
+                              const struct TALER_Amount *amount_with_fee)
+{
+  struct GNUNET_HashContext *hash_context;
+
+  hash_context = GNUNET_CRYPTO_hash_context_start ();
+  /* first, iterate over transfer public keys for hash_context */
+  for (unsigned int i=0;i<kappa;i++)
+  {
+    GNUNET_CRYPTO_hash_context_read (hash_context,
+                                     &rcs[i].transfer_pub,
+                                     sizeof (struct TALER_TransferPublicKeyP));
+  }
+  /* next, add all of the hashes from the denomination keys to the
+     hash_context */
+  for (unsigned int i=0;i<num_new_coins;i++)
+  {
+    char *buf;
+    size_t buf_size;
+
+    /* The denomination keys should / must all be identical regardless
+       of what offset we use, so we use [0]. */
+    GNUNET_assert (kappa > 0); /* sanity check */
+    buf_size = GNUNET_CRYPTO_rsa_public_key_encode 
(rcs[0].new_coins[i].dk->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,
+                                     coin_pub,
+                                     sizeof (struct 
TALER_CoinSpendPublicKeyP));
+    TALER_amount_hton (&melt_amountn,
+                       amount_with_fee);
+    GNUNET_CRYPTO_hash_context_read (hash_context,
+                                     &melt_amountn,
+                                     sizeof (struct TALER_AmountNBO));
+  }
+
+  /* finally, add all the envelopes */
+  for (unsigned int i=0;i<kappa;i++)
+  {
+    const struct TALER_RefreshCommitmentEntry *rce = &rcs[i];
+
+    for (unsigned int j=0;j<num_new_coins;j++)
+    {
+      const struct TALER_RefreshCoinData *rcd = &rce->new_coins[j];
+
+      GNUNET_CRYPTO_hash_context_read (hash_context,
+                                       rcd->coin_ev,
+                                       rcd->coin_ev_size);
+    }
+  }
+
+  /* Conclude */
+  GNUNET_CRYPTO_hash_context_finish (hash_context,
+                                     &rc->session_hash);
+}
+
+
 /* end of crypto.c */
diff --git a/src/wire/plugin_wire_test.c b/src/wire/plugin_wire_test.c
index 1a7443b..fa6ba3d 100644
--- a/src/wire/plugin_wire_test.c
+++ b/src/wire/plugin_wire_test.c
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  Copyright (C) 2016 GNUnet e.V. & Inria
+  Copyright (C) 2017 Taler Systems SA
 
   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
@@ -553,12 +553,14 @@ test_prepare_wire_transfer (void *cls,
  * @param cls closure with the `struct TALER_WIRE_ExecuteHandle`
  * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful 
status request
  *                    0 if the bank's reply is bogus (fails to follow the 
protocol)
+ * @param ec error code from the bank
  * @param serial_id unique ID of the wire transfer in the bank's records; 
UINT64_MAX on error
  * @param json detailed response from the HTTPD, or NULL if reply was not JSON
  */
 static void
 execute_cb (void *cls,
             unsigned int http_status,
+            enum TALER_ErrorCode ec,
             uint64_t serial_id,
             const json_t *json)
 {
@@ -578,13 +580,15 @@ execute_cb (void *cls,
   }
   if (NULL != emsg)
     GNUNET_asprintf (&s,
-                     "%u (%s)",
+                     "%u/%u (%s)",
                      http_status,
+                     (unsigned int) ec,
                      emsg);
   else
     GNUNET_asprintf (&s,
-                     "%u",
-                     http_status);
+                     "%u/%u",
+                     http_status,
+                     (unsigned int) ec);
   eh->cc (eh->cc_cls,
           (MHD_HTTP_OK == http_status) ? GNUNET_OK : GNUNET_SYSERR,
           serial_id,
@@ -676,6 +680,7 @@ test_execute_wire_transfer (void *cls,
   char *emsg;
   const char *json_s;
   const char *exchange_base_url;
+  char *wire_s;
 
   if (NULL == tc->ctx)
   {
@@ -728,16 +733,19 @@ test_execute_wire_transfer (void *cls,
   eh = GNUNET_new (struct TALER_WIRE_ExecuteHandle);
   eh->cc = cc;
   eh->cc_cls = cc_cls;
+  wire_s = GNUNET_STRINGS_data_to_string_alloc (&bf.wtid,
+                                                sizeof (bf.wtid));
   eh->aaih = TALER_BANK_admin_add_incoming (tc->ctx,
                                             tc->bank_uri,
                                             &tc->auth,
                                             exchange_base_url,
-                                            &bf.wtid,
+                                            wire_s,
                                             &amount,
                                             (uint64_t) tc->exchange_account_no,
                                            (uint64_t) account_no,
                                             &execute_cb,
                                             eh);
+  GNUNET_free (wire_s);
   json_decref (wire);
   if (NULL == eh->aaih)
   {
@@ -803,6 +811,7 @@ struct TALER_WIRE_HistoryHandle
  *                    #MHD_HTTP_NO_CONTENT if there are no more results; on 
success the
  *                    last callback is always of this status (even if 
`abs(num_results)` were
  *                    already returned).
+ * @param ec taler error code
  * @param dir direction of the transfer
  * @param serial_id monotonically increasing counter corresponding to the 
transaction
  * @param details details about the wire transfer
@@ -811,6 +820,7 @@ struct TALER_WIRE_HistoryHandle
 static void
 bhist_cb (void *cls,
           unsigned int http_status,
+          enum TALER_ErrorCode ec,
           enum TALER_BANK_Direction dir,
           uint64_t serial_id,
           const struct TALER_BANK_TransferDetails *details,
@@ -852,12 +862,17 @@ bhist_cb (void *cls,
                 sizeof (wd.wtid));
         wd.wtid_s = details->wire_transfer_subject;
       }
+      else
+      {
+        wd.wtid_s = NULL;
+      }
       GNUNET_free (subject);
       wd.account_details = details->account_details;
 
       if ( (NULL != whh->hres_cb) &&
            (GNUNET_OK !=
             whh->hres_cb (whh->hres_cb_cls,
+                          TALER_EC_NONE,
                           dir,
                           &bserial_id,
                           sizeof (bserial_id),
@@ -868,6 +883,7 @@ bhist_cb (void *cls,
   case MHD_HTTP_NO_CONTENT:
     if (NULL != whh->hres_cb)
       (void) whh->hres_cb (whh->hres_cb_cls,
+                           ec,
                            TALER_BANK_DIRECTION_NONE,
                            NULL,
                            0,
@@ -880,6 +896,7 @@ bhist_cb (void *cls,
     GNUNET_break (0);
     if (NULL != whh->hres_cb)
       (void) whh->hres_cb (whh->hres_cb_cls,
+                           ec,
                            TALER_BANK_DIRECTION_NONE,
                            NULL,
                            0,
@@ -1004,26 +1021,32 @@ struct TALER_WIRE_RejectHandle
   void *rej_cb_cls;
 
   /**
-   * Handle to task for timeout of operation.
+   * Handle for the reject operation.
    */
-  struct GNUNET_SCHEDULER_Task *timeout_task;
+  struct TALER_BANK_RejectHandle *brh;
 };
 
 
 /**
- * Rejection operation failed with timeout, notify callback
- * and clean up.
+ * Callbacks of this type are used to serve the result of asking
+ * the bank to reject an incoming wire transfer.
  *
- * @param cls closure with `struct TALER_WIRE_RejectHandle`
+ * @param cls closure
+ * @param http_status HTTP response code, #MHD_HTTP_NO_CONTENT (204) for 
successful status request;
+ *                    #MHD_HTTP_NOT_FOUND if the rowid is unknown;
+ *                    0 if the bank's reply is bogus (fails to follow the 
protocol),
+ * @param ec detailed error code
  */
 static void
-timeout_reject (void *cls)
+reject_cb (void *cls,
+           unsigned int http_status,
+           enum TALER_ErrorCode ec)
 {
   struct TALER_WIRE_RejectHandle *rh = cls;
 
-  rh->timeout_task = NULL;
+  rh->brh = NULL;
   rh->rej_cb (rh->rej_cb_cls,
-              TALER_EC_NOT_IMPLEMENTED /* in the future: TALER_EC_TIMEOUT */);
+              ec);
   GNUNET_free (rh);
 }
 
@@ -1052,14 +1075,30 @@ test_reject_transfer (void *cls,
                       TALER_WIRE_RejectTransferCallback rej_cb,
                       void *rej_cb_cls)
 {
+  struct TestClosure *tc = cls;
+  const uint64_t *rowid_b64 = start_off;
   struct TALER_WIRE_RejectHandle *rh;
 
-  GNUNET_break (0); /* not implemented, just a stub! */
+  if (sizeof (uint64_t) != start_off_len)
+  {
+    GNUNET_break (0);
+    return NULL;
+  }
   rh = GNUNET_new (struct TALER_WIRE_RejectHandle);
   rh->rej_cb = rej_cb;
   rh->rej_cb_cls = rej_cb_cls;
-  rh->timeout_task = GNUNET_SCHEDULER_add_now (&timeout_reject,
-                                               rh);
+  rh->brh = TALER_BANK_reject (tc->ctx,
+                               tc->bank_uri,
+                               &tc->auth,
+                               (uint64_t) tc->exchange_account_no,
+                               GNUNET_ntohll (*rowid_b64),
+                               &reject_cb,
+                               rh);
+  if (NULL == rh->brh)
+  {
+    GNUNET_free (rh);
+    return NULL;
+  }
   return rh;
 }
 
@@ -1082,7 +1121,8 @@ test_reject_transfer_cancel (void *cls,
 {
   void *ret = rh->rej_cb_cls;
 
-  GNUNET_SCHEDULER_cancel (rh->timeout_task);
+  if (NULL != rh->brh)
+    TALER_BANK_reject_cancel (rh->brh);
   GNUNET_free (rh);
   return ret;
 }
diff --git a/src/wire/test_wire_plugin_transactions_test.c 
b/src/wire/test_wire_plugin_transactions_test.c
index 26331b5..a020f13 100644
--- a/src/wire/test_wire_plugin_transactions_test.c
+++ b/src/wire/test_wire_plugin_transactions_test.c
@@ -159,6 +159,7 @@ timeout_cb (void *cls)
  * the bank for the transaction history.
  *
  * @param cls closure
+ * @param ec taler status code
  * @param dir direction of the transfer
  * @param row_off identification of the position at which we are querying
  * @param row_off_size number of bytes in @a row_off
@@ -167,6 +168,7 @@ timeout_cb (void *cls)
  */
 static int
 history_result_cb (void *cls,
+                   enum TALER_ErrorCode ec,
                    enum TALER_BANK_Direction dir,
                    const void *row_off,
                    size_t row_off_size,

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



reply via email to

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