gnunet-svn
[Top][All Lists]
Advanced

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

[taler-exchange] 11/12: big rename fest related to #6067 API renaming


From: gnunet
Subject: [taler-exchange] 11/12: big rename fest related to #6067 API renaming
Date: Sat, 29 Feb 2020 16:59:30 +0100

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

grothoff pushed a commit to branch master
in repository exchange.

commit 0a2b049864c8dae0c53c203d46fca89e0e66849d
Author: Christian Grothoff <address@hidden>
AuthorDate: Sat Feb 29 16:42:10 2020 +0100

    big rename fest related to #6067 API renaming
---
 src/auditor/taler-auditor.c                        |    2 +-
 src/benchmark/taler-exchange-benchmark.c           |    4 +-
 src/exchange/Makefile.am                           |    6 +-
 src/exchange/taler-exchange-httpd.c                |    6 +-
 ..._refresh_link.c => taler-exchange-httpd_link.c} |    2 +-
 ..._refresh_link.h => taler-exchange-httpd_link.h} |    0
 ..._refresh_melt.c => taler-exchange-httpd_melt.c} |   80 +-
 ..._refresh_melt.h => taler-exchange-httpd_melt.h} |    4 +-
 ...l.c => taler-exchange-httpd_refreshes_reveal.c} |   36 +-
 ...l.h => taler-exchange-httpd_refreshes_reveal.h} |    0
 src/exchangedb/plugin_exchangedb_postgres.c        |   22 +-
 src/include/taler_exchange_service.h               |  225 +--
 src/include/taler_exchangedb_plugin.h              |    6 +-
 src/include/taler_testing_lib.h                    |   18 +-
 src/lib/Makefile.am                                |   13 +-
 src/lib/exchange_api_common.c                      |  416 +++++
 ...k_transaction.c => exchange_api_deposits_get.c} |   12 +-
 ...ange_api_refresh_link.c => exchange_api_link.c} |  172 +-
 src/lib/exchange_api_melt.c                        |  505 ++++++
 src/lib/exchange_api_refresh.c                     | 1770 --------------------
 src/lib/exchange_api_refresh_common.c              |  631 +++++++
 src/lib/exchange_api_refresh_common.h              |  240 +++
 src/lib/exchange_api_refreshes_reveal.c            |  512 ++++++
 src/lib/exchange_api_reserve.c                     | 1306 ---------------
 src/lib/exchange_api_reserves_get.c                |  308 ++++
 ...ack_transfer.c => exchange_api_transfers_get.c} |   32 +-
 src/lib/exchange_api_withdraw.c                    |  611 +++++++
 src/testing/test_auditor_api.c                     |    8 +-
 src/testing/test_exchange_api.c                    |   24 +-
 src/testing/test_exchange_api_revocation.c         |   18 +-
 src/testing/test_exchange_api_twisted.c            |    2 +-
 src/testing/testing_api_cmd_refresh.c              |   92 +-
 32 files changed, 3632 insertions(+), 3451 deletions(-)

diff --git a/src/auditor/taler-auditor.c b/src/auditor/taler-auditor.c
index 1c6f82c8..d211bd94 100644
--- a/src/auditor/taler-auditor.c
+++ b/src/auditor/taler-auditor.c
@@ -3763,7 +3763,7 @@ refresh_session_cb (void *cls,
                               NULL);
   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
   {
-    report_row_inconsistency ("refresh_melt",
+    report_row_inconsistency ("melt",
                               rowid,
                               "denomination key not found");
     return GNUNET_OK;
diff --git a/src/benchmark/taler-exchange-benchmark.c 
b/src/benchmark/taler-exchange-benchmark.c
index 91ed5b04..7e1b8b72 100644
--- a/src/benchmark/taler-exchange-benchmark.c
+++ b/src/benchmark/taler-exchange-benchmark.c
@@ -374,8 +374,8 @@ run (void *cls,
                          i,
                          j);
         unit[2] =
-          TALER_TESTING_cmd_refresh_melt_with_retry
-            (TALER_TESTING_cmd_refresh_melt
+          TALER_TESTING_cmd_melt_with_retry
+            (TALER_TESTING_cmd_melt
               (add_label (melt_label),
               withdraw_label,
               MHD_HTTP_OK,
diff --git a/src/exchange/Makefile.am b/src/exchange/Makefile.am
index fe1fd397..61d3341c 100644
--- a/src/exchange/Makefile.am
+++ b/src/exchange/Makefile.am
@@ -51,11 +51,11 @@ taler_exchange_httpd_SOURCES = \
   taler-exchange-httpd_deposit.c taler-exchange-httpd_deposit.h \
   taler-exchange-httpd_deposits_get.c taler-exchange-httpd_deposits_get.h \
   taler-exchange-httpd_keystate.c taler-exchange-httpd_keystate.h \
+  taler-exchange-httpd_link.c taler-exchange-httpd_link.h \
   taler-exchange-httpd_mhd.c taler-exchange-httpd_mhd.h \
   taler-exchange-httpd_recoup.c taler-exchange-httpd_recoup.h \
-  taler-exchange-httpd_refresh_link.c taler-exchange-httpd_refresh_link.h \
-  taler-exchange-httpd_refresh_melt.c taler-exchange-httpd_refresh_melt.h \
-  taler-exchange-httpd_refresh_reveal.c taler-exchange-httpd_refresh_reveal.h \
+  taler-exchange-httpd_melt.c taler-exchange-httpd_melt.h \
+  taler-exchange-httpd_refreshes_reveal.c 
taler-exchange-httpd_refreshes_reveal.h \
   taler-exchange-httpd_refund.c taler-exchange-httpd_refund.h \
   taler-exchange-httpd_reserves_get.c taler-exchange-httpd_reserves_get.h \
   taler-exchange-httpd_responses.c taler-exchange-httpd_responses.h \
diff --git a/src/exchange/taler-exchange-httpd.c 
b/src/exchange/taler-exchange-httpd.c
index 8d569296..d353a796 100644
--- a/src/exchange/taler-exchange-httpd.c
+++ b/src/exchange/taler-exchange-httpd.c
@@ -33,9 +33,9 @@
 #include "taler-exchange-httpd_reserves_get.h"
 #include "taler-exchange-httpd_withdraw.h"
 #include "taler-exchange-httpd_recoup.h"
-#include "taler-exchange-httpd_refresh_link.h"
-#include "taler-exchange-httpd_refresh_melt.h"
-#include "taler-exchange-httpd_refresh_reveal.h"
+#include "taler-exchange-httpd_link.h"
+#include "taler-exchange-httpd_melt.h"
+#include "taler-exchange-httpd_refreshes_reveal.h"
 #include "taler-exchange-httpd_terms.h"
 #include "taler-exchange-httpd_transfers_get.h"
 #include "taler-exchange-httpd_deposits_get.h"
diff --git a/src/exchange/taler-exchange-httpd_refresh_link.c 
b/src/exchange/taler-exchange-httpd_link.c
similarity index 99%
rename from src/exchange/taler-exchange-httpd_refresh_link.c
rename to src/exchange/taler-exchange-httpd_link.c
index 6dbaed49..83d7f6a0 100644
--- a/src/exchange/taler-exchange-httpd_refresh_link.c
+++ b/src/exchange/taler-exchange-httpd_link.c
@@ -26,7 +26,7 @@
 #include <microhttpd.h>
 #include "taler_mhd_lib.h"
 #include "taler-exchange-httpd_mhd.h"
-#include "taler-exchange-httpd_refresh_link.h"
+#include "taler-exchange-httpd_link.h"
 #include "taler-exchange-httpd_responses.h"
 #include "taler-exchange-httpd_keystate.h"
 
diff --git a/src/exchange/taler-exchange-httpd_refresh_link.h 
b/src/exchange/taler-exchange-httpd_link.h
similarity index 100%
rename from src/exchange/taler-exchange-httpd_refresh_link.h
rename to src/exchange/taler-exchange-httpd_link.h
diff --git a/src/exchange/taler-exchange-httpd_refresh_melt.c 
b/src/exchange/taler-exchange-httpd_melt.c
similarity index 90%
rename from src/exchange/taler-exchange-httpd_refresh_melt.c
rename to src/exchange/taler-exchange-httpd_melt.c
index 9d92a4ce..035fc48f 100644
--- a/src/exchange/taler-exchange-httpd_refresh_melt.c
+++ b/src/exchange/taler-exchange-httpd_melt.c
@@ -14,7 +14,7 @@
   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
 */
 /**
- * @file taler-exchange-httpd_refresh_melt.c
+ * @file taler-exchange-httpd_melt.c
  * @brief Handle /refresh/melt requests
  * @author Florian Dold
  * @author Benedikt Mueller
@@ -27,7 +27,7 @@
 #include "taler_json_lib.h"
 #include "taler_mhd_lib.h"
 #include "taler-exchange-httpd_mhd.h"
-#include "taler-exchange-httpd_refresh_melt.h"
+#include "taler-exchange-httpd_melt.h"
 #include "taler-exchange-httpd_responses.h"
 #include "taler-exchange-httpd_keystate.h"
 
@@ -48,14 +48,14 @@
  * @return a MHD result code
  */
 static int
-reply_refresh_melt_insufficient_funds (struct MHD_Connection *connection,
-                                       const struct
-                                       TALER_CoinSpendPublicKeyP *coin_pub,
-                                       const struct TALER_Amount *coin_value,
-                                       struct TALER_EXCHANGEDB_TransactionList 
*
-                                       tl,
-                                       const struct TALER_Amount *requested,
-                                       const struct TALER_Amount *residual)
+reply_melt_insufficient_funds (struct MHD_Connection *connection,
+                               const struct
+                               TALER_CoinSpendPublicKeyP *coin_pub,
+                               const struct TALER_Amount *coin_value,
+                               struct TALER_EXCHANGEDB_TransactionList *
+                               tl,
+                               const struct TALER_Amount *requested,
+                               const struct TALER_Amount *residual)
 {
   json_t *history;
 
@@ -96,9 +96,9 @@ reply_refresh_melt_insufficient_funds (struct MHD_Connection 
*connection,
  * @return a MHD status code
  */
 static int
-reply_refresh_melt_success (struct MHD_Connection *connection,
-                            const struct TALER_RefreshCommitmentP *rc,
-                            uint32_t noreveal_index)
+reply_melt_success (struct MHD_Connection *connection,
+                    const struct TALER_RefreshCommitmentP *rc,
+                    uint32_t noreveal_index)
 {
   struct TALER_RefreshMeltConfirmationPS body;
   struct TALER_ExchangePublicKeyP pub;
@@ -139,7 +139,7 @@ struct RefreshMeltContext
 
   /**
    * noreveal_index is only initialized during
-   * #refresh_melt_transaction().
+   * #melt_transaction().
    */
   struct TALER_EXCHANGEDB_RefreshSession refresh_session;
 
@@ -253,14 +253,14 @@ refresh_check_melt (struct MHD_Connection *connection,
                    TALER_amount_subtract (&coin_residual,
                                           &spent,
                                           
&rmc->refresh_session.amount_with_fee));
-    *mhd_ret = reply_refresh_melt_insufficient_funds (connection,
-                                                      
&rmc->refresh_session.coin
-                                                      .coin_pub,
-                                                      &rmc->coin_value,
-                                                      tl,
-                                                      &rmc->refresh_session.
-                                                      amount_with_fee,
-                                                      &coin_residual);
+    *mhd_ret = reply_melt_insufficient_funds (connection,
+                                              &rmc->refresh_session.coin
+                                              .coin_pub,
+                                              &rmc->coin_value,
+                                              tl,
+                                              &rmc->refresh_session.
+                                              amount_with_fee,
+                                              &coin_residual);
     TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
                                             tl);
     return GNUNET_DB_STATUS_HARD_ERROR;
@@ -294,10 +294,10 @@ refresh_check_melt (struct MHD_Connection *connection,
  * @return transaction status
  */
 static enum GNUNET_DB_QueryStatus
-refresh_melt_transaction (void *cls,
-                          struct MHD_Connection *connection,
-                          struct TALER_EXCHANGEDB_Session *session,
-                          int *mhd_ret)
+melt_transaction (void *cls,
+                  struct MHD_Connection *connection,
+                  struct TALER_EXCHANGEDB_Session *session,
+                  int *mhd_ret)
 {
   struct RefreshMeltContext *rmc = cls;
   enum GNUNET_DB_QueryStatus qs;
@@ -311,9 +311,9 @@ refresh_melt_transaction (void *cls,
   if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
   {
     TALER_LOG_DEBUG ("Found already-melted coin\n");
-    *mhd_ret = reply_refresh_melt_success (connection,
-                                           &rmc->refresh_session.rc,
-                                           noreveal_index);
+    *mhd_ret = reply_melt_success (connection,
+                                   &rmc->refresh_session.rc,
+                                   noreveal_index);
     /* Note: we return "hard error" to ensure the wrapper
        does not retry the transaction, and to also not generate
        a "fresh" response (as we would on "success") */
@@ -366,15 +366,15 @@ refresh_melt_transaction (void *cls,
  * happened.  We now need to validate the coins being melted and the
  * session signature and then hand things of to execute the melt
  * operation.  This function parses the JSON arrays and then passes
- * processing on to #refresh_melt_transaction().
+ * processing on to #melt_transaction().
  *
  * @param connection the MHD connection to handle
  * @param[in,out] rmc details about the melt request
  * @return MHD result code
  */
 static int
-handle_refresh_melt (struct MHD_Connection *connection,
-                     struct RefreshMeltContext *rmc)
+handle_melt (struct MHD_Connection *connection,
+             struct RefreshMeltContext *rmc)
 {
 
   /* verify signature of coin for melt operation */
@@ -415,15 +415,15 @@ handle_refresh_melt (struct MHD_Connection *connection,
         TEH_DB_run_transaction (connection,
                                 "run melt",
                                 &mhd_ret,
-                                &refresh_melt_transaction,
+                                &melt_transaction,
                                 rmc))
       return mhd_ret;
   }
 
   /* generate ordinary response */
-  return reply_refresh_melt_success (connection,
-                                     &rmc->refresh_session.rc,
-                                     rmc->refresh_session.noreveal_index);
+  return reply_melt_success (connection,
+                             &rmc->refresh_session.rc,
+                             rmc->refresh_session.noreveal_index);
 }
 
 
@@ -571,8 +571,8 @@ check_for_denomination_key (struct MHD_Connection 
*connection,
                                        
TALER_EC_REFRESH_MELT_FEES_EXCEED_CONTRIBUTION,
                                        "melt amount smaller than melting fee");
   }
-  return handle_refresh_melt (connection,
-                              rmc);
+  return handle_melt (connection,
+                      rmc);
 }
 
 
@@ -580,7 +580,7 @@ check_for_denomination_key (struct MHD_Connection 
*connection,
  * Handle a "/coins/$COIN_PUB/melt" request.  Parses the request into the JSON
  * components and then hands things of to #check_for_denomination_key() to
  * validate the melted coins, the signature and execute the melt using
- * handle_refresh_melt().
+ * handle_melt().
 
  * @param connection the MHD connection to handle
  * @param coin_pub public key of the coin
@@ -625,4 +625,4 @@ TEH_REFRESH_handler_melt (struct MHD_Connection *connection,
 }
 
 
-/* end of taler-exchange-httpd_refresh_melt.c */
+/* end of taler-exchange-httpd_melt.c */
diff --git a/src/exchange/taler-exchange-httpd_refresh_melt.h 
b/src/exchange/taler-exchange-httpd_melt.h
similarity index 95%
rename from src/exchange/taler-exchange-httpd_refresh_melt.h
rename to src/exchange/taler-exchange-httpd_melt.h
index 41488c81..daf8ffc8 100644
--- a/src/exchange/taler-exchange-httpd_refresh_melt.h
+++ b/src/exchange/taler-exchange-httpd_melt.h
@@ -14,7 +14,7 @@
   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
 */
 /**
- * @file taler-exchange-httpd_refresh_melt.h
+ * @file taler-exchange-httpd_melt.h
  * @brief Handle /refresh/melt requests
  * @author Florian Dold
  * @author Benedikt Mueller
@@ -32,7 +32,7 @@
  * Handle a "/coins/$COIN_PUB/melt" request.  Parses the request into the JSON
  * components and then hands things of to #check_for_denomination_key() to
  * validate the melted coins, the signature and execute the melt using
- * handle_refresh_melt().
+ * handle_melt().
 
  * @param connection the MHD connection to handle
  * @param coin_pub public key of the coin
diff --git a/src/exchange/taler-exchange-httpd_refresh_reveal.c 
b/src/exchange/taler-exchange-httpd_refreshes_reveal.c
similarity index 97%
rename from src/exchange/taler-exchange-httpd_refresh_reveal.c
rename to src/exchange/taler-exchange-httpd_refreshes_reveal.c
index 802f0a8a..e7e5b97d 100644
--- a/src/exchange/taler-exchange-httpd_refresh_reveal.c
+++ b/src/exchange/taler-exchange-httpd_refreshes_reveal.c
@@ -14,8 +14,8 @@
   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
 */
 /**
- * @file taler-exchange-httpd_refresh_reveal.c
- * @brief Handle /refresh/reveal requests
+ * @file taler-exchange-httpd_refreshes_reveal.c
+ * @brief Handle /refreshes/$RCH/reveal requests
  * @author Florian Dold
  * @author Benedikt Mueller
  * @author Christian Grothoff
@@ -26,7 +26,7 @@
 #include <microhttpd.h>
 #include "taler_mhd_lib.h"
 #include "taler-exchange-httpd_mhd.h"
-#include "taler-exchange-httpd_refresh_reveal.h"
+#include "taler-exchange-httpd_refreshes_reveal.h"
 #include "taler-exchange-httpd_responses.h"
 #include "taler-exchange-httpd_keystate.h"
 
@@ -299,7 +299,7 @@ refresh_reveal_transaction (void *cls,
                             int *mhd_ret)
 {
   struct RevealContext *rctx = cls;
-  struct TALER_EXCHANGEDB_RefreshMelt refresh_melt;
+  struct TALER_EXCHANGEDB_RefreshMelt melt;
   enum GNUNET_DB_QueryStatus qs;
 
   /* Obtain basic information about the refresh operation and what
@@ -307,7 +307,7 @@ refresh_reveal_transaction (void *cls,
   qs = TEH_plugin->get_melt (TEH_plugin->cls,
                              session,
                              &rctx->rc,
-                             &refresh_melt);
+                             &melt);
   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
   {
     *mhd_ret = TALER_MHD_reply_with_error (connection,
@@ -319,7 +319,7 @@ refresh_reveal_transaction (void *cls,
   if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
     return qs;
   if ( (GNUNET_DB_STATUS_HARD_ERROR == qs) ||
-       (refresh_melt.session.noreveal_index >= TALER_CNC_KAPPA) )
+       (melt.session.noreveal_index >= TALER_CNC_KAPPA) )
   {
     GNUNET_break (0);
     *mhd_ret = TALER_MHD_reply_with_error (connection,
@@ -331,7 +331,7 @@ refresh_reveal_transaction (void *cls,
 
   /* Verify commitment */
   {
-    /* Note that the contents of rcs[refresh_melt.session.noreveal_index]
+    /* Note that the contents of rcs[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];
@@ -343,7 +343,7 @@ refresh_reveal_transaction (void *cls,
     {
       struct TALER_RefreshCommitmentEntry *rce = &rcs[i];
 
-      if (i == refresh_melt.session.noreveal_index)
+      if (i == melt.session.noreveal_index)
       {
         /* Take these coin envelopes from the client */
         rce->transfer_pub = rctx->gamma_tp;
@@ -360,7 +360,7 @@ refresh_reveal_transaction (void *cls,
         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,
+                                           &melt.session.coin.coin_pub,
                                            &ts);
         rce->new_coins = GNUNET_new_array (rctx->num_fresh_coins,
                                            struct TALER_RefreshCoinData);
@@ -387,15 +387,15 @@ refresh_reveal_transaction (void *cls,
                                   TALER_CNC_KAPPA,
                                   rctx->num_fresh_coins,
                                   rcs,
-                                  &refresh_melt.session.coin.coin_pub,
-                                  &refresh_melt.session.amount_with_fee);
+                                  &melt.session.coin.coin_pub,
+                                  &melt.session.amount_with_fee);
 
     /* Free resources allocated above */
     for (unsigned int i = 0; i<TALER_CNC_KAPPA; i++)
     {
       struct TALER_RefreshCommitmentEntry *rce = &rcs[i];
 
-      if (i == refresh_melt.session.noreveal_index)
+      if (i == melt.session.noreveal_index)
         continue; /* This offset is special... */
       for (unsigned int j = 0; j<rctx->num_fresh_coins; j++)
       {
@@ -421,7 +421,7 @@ refresh_reveal_transaction (void *cls,
   {
     struct TALER_Amount refresh_cost;
 
-    refresh_cost = refresh_melt.melt_fee;
+    refresh_cost = melt.melt_fee;
     for (unsigned int i = 0; i<rctx->num_fresh_coins; i++)
     {
       struct TALER_Amount fee_withdraw;
@@ -450,7 +450,7 @@ refresh_reveal_transaction (void *cls,
       }
     }
     if (0 < TALER_amount_cmp (&refresh_cost,
-                              &refresh_melt.session.amount_with_fee))
+                              &melt.session.amount_with_fee))
     {
       GNUNET_break_op (0);
       *mhd_ret = TALER_MHD_reply_with_error (connection,
@@ -542,7 +542,7 @@ resolve_refresh_reveal_denominations (struct 
TEH_KS_StateHandle *key_state,
   struct GNUNET_HashCode dki_h[num_fresh_coins];
   struct TALER_RefreshCoinData rcds[num_fresh_coins];
   struct TALER_CoinSpendSignatureP link_sigs[num_fresh_coins];
-  struct TALER_EXCHANGEDB_RefreshMelt refresh_melt;
+  struct TALER_EXCHANGEDB_RefreshMelt melt;
   int res;
 
   /* Parse denomination key hashes */
@@ -613,7 +613,7 @@ resolve_refresh_reveal_denominations (struct 
TEH_KS_StateHandle *key_state,
         (qs = TEH_plugin->get_melt (TEH_plugin->cls,
                                     NULL,
                                     &rctx->rc,
-                                    &refresh_melt)))
+                                    &melt)))
     {
       switch (qs)
       {
@@ -663,7 +663,7 @@ resolve_refresh_reveal_denominations (struct 
TEH_KS_StateHandle *key_state,
       ldp.purpose.size = htonl (sizeof (ldp));
       ldp.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_LINK);
       ldp.h_denom_pub = dki_h[i];
-      ldp.old_coin_pub = refresh_melt.session.coin.coin_pub;
+      ldp.old_coin_pub = melt.session.coin.coin_pub;
       ldp.transfer_pub = rctx->gamma_tp;
       GNUNET_CRYPTO_hash (rcds[i].coin_ev,
                           rcds[i].coin_ev_size,
@@ -672,7 +672,7 @@ resolve_refresh_reveal_denominations (struct 
TEH_KS_StateHandle *key_state,
           GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_LINK,
                                       &ldp.purpose,
                                       &link_sigs[i].eddsa_signature,
-                                      &refresh_melt.session.coin.coin_pub.
+                                      &melt.session.coin.coin_pub.
                                       eddsa_pub))
       {
         GNUNET_break_op (0);
diff --git a/src/exchange/taler-exchange-httpd_refresh_reveal.h 
b/src/exchange/taler-exchange-httpd_refreshes_reveal.h
similarity index 100%
rename from src/exchange/taler-exchange-httpd_refresh_reveal.h
rename to src/exchange/taler-exchange-httpd_refreshes_reveal.h
diff --git a/src/exchangedb/plugin_exchangedb_postgres.c 
b/src/exchangedb/plugin_exchangedb_postgres.c
index 9df2fe77..f8aea50c 100644
--- a/src/exchangedb/plugin_exchangedb_postgres.c
+++ b/src/exchangedb/plugin_exchangedb_postgres.c
@@ -3389,8 +3389,8 @@ postgres_select_refunds_by_coin (void *cls,
  * @param cls the `struct PostgresClosure` with the plugin-specific state
  * @param session database handle to use, NULL if not run in any transaction
  * @param rc commitment hash to use to locate the operation
- * @param[out] refresh_melt where to store the result; note that
- *             refresh_melt->session.coin.denom_sig will be set to NULL
+ * @param[out] melt where to store the result; note that
+ *             melt->session.coin.denom_sig will be set to NULL
  *             and is not fetched by this routine (as it is not needed by the 
client)
  * @return transaction status
  */
@@ -3398,7 +3398,7 @@ static enum GNUNET_DB_QueryStatus
 postgres_get_melt (void *cls,
                    struct TALER_EXCHANGEDB_Session *session,
                    const struct TALER_RefreshCommitmentP *rc,
-                   struct TALER_EXCHANGEDB_RefreshMelt *refresh_melt)
+                   struct TALER_EXCHANGEDB_RefreshMelt *melt)
 {
   struct PostgresClosure *pg = cls;
   struct GNUNET_PQ_QueryParam params[] = {
@@ -3407,30 +3407,30 @@ postgres_get_melt (void *cls,
   };
   struct GNUNET_PQ_ResultSpec rs[] = {
     GNUNET_PQ_result_spec_auto_from_type ("denom_pub_hash",
-                                          &refresh_melt->session.coin.
+                                          &melt->session.coin.
                                           denom_pub_hash),
     TALER_PQ_RESULT_SPEC_AMOUNT ("fee_refresh",
-                                 &refresh_melt->melt_fee),
+                                 &melt->melt_fee),
     GNUNET_PQ_result_spec_uint32 ("noreveal_index",
-                                  &refresh_melt->session.noreveal_index),
+                                  &melt->session.noreveal_index),
     GNUNET_PQ_result_spec_auto_from_type ("old_coin_pub",
-                                          
&refresh_melt->session.coin.coin_pub),
+                                          &melt->session.coin.coin_pub),
     GNUNET_PQ_result_spec_auto_from_type ("old_coin_sig",
-                                          &refresh_melt->session.coin_sig),
+                                          &melt->session.coin_sig),
     TALER_PQ_RESULT_SPEC_AMOUNT ("amount_with_fee",
-                                 &refresh_melt->session.amount_with_fee),
+                                 &melt->session.amount_with_fee),
     GNUNET_PQ_result_spec_end
   };
   enum GNUNET_DB_QueryStatus qs;
 
-  refresh_melt->session.coin.denom_sig.rsa_signature = NULL;
+  melt->session.coin.denom_sig.rsa_signature = NULL;
   if (NULL == session)
     session = postgres_get_session (pg);
   qs = GNUNET_PQ_eval_prepared_singleton_select (session->conn,
                                                  "get_melt",
                                                  params,
                                                  rs);
-  refresh_melt->session.rc = *rc;
+  melt->session.rc = *rc;
   return qs;
 }
 
diff --git a/src/include/taler_exchange_service.h 
b/src/include/taler_exchange_service.h
index 0696d96c..4e917df8 100644
--- a/src/include/taler_exchange_service.h
+++ b/src/include/taler_exchange_service.h
@@ -697,7 +697,7 @@ void
 TALER_EXCHANGE_wire_cancel (struct TALER_EXCHANGE_WireHandle *wh);
 
 
-/* *********************  /deposit *********************** */
+/* *********************  /coins/$COIN_PUB/deposit *********************** */
 
 
 /**
@@ -799,7 +799,7 @@ void
 TALER_EXCHANGE_deposit_cancel (struct TALER_EXCHANGE_DepositHandle *deposit);
 
 
-/* *********************  /refund *********************** */
+/* *********************  /coins/$COIN_PUB/refund *********************** */
 
 /**
  * @brief A Refund Handle
@@ -926,7 +926,7 @@ void
 TALER_EXCHANGE_refund_cancel (struct TALER_EXCHANGE_RefundHandle *refund);
 
 
-/* ********************* /reserve/status *********************** */
+/* ********************* GET /reserves/$RESERVE_PUB *********************** */
 
 
 /**
@@ -1147,14 +1147,14 @@ TALER_EXCHANGE_reserves_get (struct 
TALER_EXCHANGE_Handle *exchange,
  */
 void
 TALER_EXCHANGE_reserves_get_cancel (struct
-                                    TALER_EXCHANGE_ReservesGetHandle *rsh);
+                                    TALER_EXCHANGE_ReservesGetHandle *rhh);
 
 
-/* ********************* /reserve/withdraw *********************** */
+/* ********************* POST /reserves/$RESERVE_PUB/withdraw 
*********************** */
 
 
 /**
- * @brief A /reserve/withdraw Handle
+ * @brief A /reserves/$RESERVE_PUB/withdraw Handle
  */
 struct TALER_EXCHANGE_WithdrawHandle;
 
@@ -1175,13 +1175,12 @@ typedef void
                                     unsigned int http_status,
                                     enum TALER_ErrorCode ec,
                                     const struct
-                                    TALER_DenominationSignature *
-                                    sig,
+                                    TALER_DenominationSignature *sig,
                                     const json_t *full_response);
 
 
 /**
- * Withdraw a coin from the exchange using a /reserve/withdraw
+ * Withdraw a coin from the exchange using a /reserves/$RESERVE_PUB/withdraw
  * request.  This API is typically used by a wallet to withdraw from a
  * reserve.
  *
@@ -1213,7 +1212,7 @@ TALER_EXCHANGE_withdraw (struct TALER_EXCHANGE_Handle 
*exchange,
 
 
 /**
- * Withdraw a coin from the exchange using a /reserve/withdraw
+ * Withdraw a coin from the exchange using a /reserves/$RESERVE_PUB/withdraw
  * request.  This API is typically used by a wallet to withdraw a tip
  * where the reserve's signature was created by the merchant already.
  *
@@ -1252,12 +1251,10 @@ TALER_EXCHANGE_withdraw2 (struct TALER_EXCHANGE_Handle 
*exchange,
  * Cancel a withdraw status request.  This function cannot be used
  * on a request handle if a response is already served for it.
  *
- * @param sign the withdraw sign request handle
+ * @param wh the withdraw handle
  */
 void
-TALER_EXCHANGE_withdraw_cancel (struct
-                                TALER_EXCHANGE_WithdrawHandle *
-                                sign);
+TALER_EXCHANGE_withdraw_cancel (struct TALER_EXCHANGE_WithdrawHandle *wh);
 
 
 /* ********************* /refresh/melt+reveal ***************************** */
@@ -1273,7 +1270,7 @@ TALER_EXCHANGE_withdraw_cancel (struct
  * no money is lost in case of hardware failures, is operation does
  * not actually initiate the request. Instead, it generates a buffer
  * which the caller must store before proceeding with the actual call
- * to #TALER_EXCHANGE_refresh_melt() that will generate the request.
+ * to #TALER_EXCHANGE_melt() that will generate the request.
  *
  * This function does verify that the given request data is internally
  * consistent.  However, the @a melts_sigs are NOT verified.
@@ -1298,7 +1295,7 @@ TALER_EXCHANGE_withdraw_cancel (struct
  * @return NULL
  *         if the inputs are invalid (i.e. denomination key not with this 
exchange).
  *         Otherwise, pointer to a buffer of @a res_size to store persistently
- *         before proceeding to #TALER_EXCHANGE_refresh_melt().
+ *         before proceeding to #TALER_EXCHANGE_melt().
  *         Non-null results should be freed using GNUNET_free().
  */
 char *
@@ -1315,18 +1312,19 @@ TALER_EXCHANGE_refresh_prepare (const struct
                                 size_t *res_size);
 
 
-/* ********************* /refresh/melt ***************************** */
+/* ********************* /coins/$COIN_PUB/melt ***************************** */
 
 /**
- * @brief A /refresh/melt Handle
+ * @brief A /coins/$COIN_PUB/melt Handle
  */
-struct TALER_EXCHANGE_RefreshMeltHandle;
+struct TALER_EXCHANGE_MeltHandle;
 
 
 /**
- * Callbacks of this type are used to notify the application about the
- * result of the /refresh/melt stage.  If successful, the @a noreveal_index
- * should be committed to disk prior to proceeding 
#TALER_EXCHANGE_refresh_reveal().
+ * Callbacks of this type are used to notify the application about the result
+ * of the /coins/$COIN_PUB/melt stage.  If successful, the @a noreveal_index
+ * should be committed to disk prior to proceeding
+ * #TALER_EXCHANGE_refreshes_reveal().
  *
  * @param cls closure
  * @param http_status HTTP response code, never #MHD_HTTP_OK (200) as for 
successful intermediate response this callback is skipped.
@@ -1338,17 +1336,16 @@ struct TALER_EXCHANGE_RefreshMeltHandle;
  * @param full_response full response from the exchange (for logging, in case 
of errors)
  */
 typedef void
-(*TALER_EXCHANGE_RefreshMeltCallback) (void *cls,
-                                       unsigned int http_status,
-                                       enum TALER_ErrorCode ec,
-                                       uint32_t noreveal_index,
-                                       const struct
-                                       TALER_ExchangePublicKeyP *sign_key,
-                                       const json_t *full_response);
+(*TALER_EXCHANGE_MeltCallback) (void *cls,
+                                unsigned int http_status,
+                                enum TALER_ErrorCode ec,
+                                uint32_t noreveal_index,
+                                const struct TALER_ExchangePublicKeyP 
*sign_key,
+                                const json_t *full_response);
 
 
 /**
- * Submit a refresh melt request to the exchange and get the exchange's
+ * Submit a melt request to the exchange and get the exchange's
  * response.
  *
  * This API is typically used by a wallet.  Note that to ensure that
@@ -1367,26 +1364,25 @@ typedef void
  * @return a handle for this request; NULL if the argument was invalid.
  *         In this case, neither callback will be called.
  */
-struct TALER_EXCHANGE_RefreshMeltHandle *
-TALER_EXCHANGE_refresh_melt (struct TALER_EXCHANGE_Handle *exchange,
-                             size_t refresh_data_length,
-                             const char *refresh_data,
-                             TALER_EXCHANGE_RefreshMeltCallback melt_cb,
-                             void *melt_cb_cls);
+struct TALER_EXCHANGE_MeltHandle *
+TALER_EXCHANGE_melt (struct TALER_EXCHANGE_Handle *exchange,
+                     size_t refresh_data_length,
+                     const char *refresh_data,
+                     TALER_EXCHANGE_MeltCallback melt_cb,
+                     void *melt_cb_cls);
 
 
 /**
- * Cancel a refresh melt request.  This function cannot be used
+ * Cancel a melt request.  This function cannot be used
  * on a request handle if the callback was already invoked.
  *
- * @param rmh the refresh handle
+ * @param mh the melt handle
  */
 void
-TALER_EXCHANGE_refresh_melt_cancel (struct
-                                    TALER_EXCHANGE_RefreshMeltHandle *rmh);
+TALER_EXCHANGE_melt_cancel (struct TALER_EXCHANGE_MeltHandle *mh);
 
 
-/* ********************* /refresh/reveal ***************************** */
+/* ********************* /refreshes/$RCH/reveal ***************************** 
*/
 
 
 /**
@@ -1407,25 +1403,25 @@ TALER_EXCHANGE_refresh_melt_cancel (struct
  * @param full_response full response from the exchange (for logging, in case 
of errors)
  */
 typedef void
-(*TALER_EXCHANGE_RefreshRevealCallback) (void *cls,
-                                         unsigned int http_status,
-                                         enum TALER_ErrorCode ec,
-                                         unsigned int num_coins,
-                                         const struct
-                                         TALER_PlanchetSecretsP *coin_privs,
-                                         const struct
-                                         TALER_DenominationSignature *sigs,
-                                         const json_t *full_response);
+(*TALER_EXCHANGE_RefreshesRevealCallback)(void *cls,
+                                          unsigned int http_status,
+                                          enum TALER_ErrorCode ec,
+                                          unsigned int num_coins,
+                                          const struct
+                                          TALER_PlanchetSecretsP *coin_privs,
+                                          const struct
+                                          TALER_DenominationSignature *sigs,
+                                          const json_t *full_response);
 
 
 /**
- * @brief A /refresh/reveal Handle
+ * @brief A /refreshes/$RCH/reveal Handle
  */
-struct TALER_EXCHANGE_RefreshRevealHandle;
+struct TALER_EXCHANGE_RefreshesRevealHandle;
 
 
 /**
- * Submit a /refresh/reval request to the exchange and get the exchange's
+ * Submit a /refreshes/$RCH/reval request to the exchange and get the 
exchange's
  * response.
  *
  * This API is typically used by a wallet.  Note that to ensure that
@@ -1439,20 +1435,21 @@ struct TALER_EXCHANGE_RefreshRevealHandle;
  * @param refresh_data the refresh data as returned from
           #TALER_EXCHANGE_refresh_prepare())
  * @param noreveal_index response from the exchange to the
- *        #TALER_EXCHANGE_refresh_melt() invocation
+ *        #TALER_EXCHANGE_melt() invocation
  * @param reveal_cb the callback to call with the final result of the
  *        refresh operation
  * @param reveal_cb_cls closure for the above callback
  * @return a handle for this request; NULL if the argument was invalid.
  *         In this case, neither callback will be called.
  */
-struct TALER_EXCHANGE_RefreshRevealHandle *
-TALER_EXCHANGE_refresh_reveal (struct TALER_EXCHANGE_Handle *exchange,
-                               size_t refresh_data_length,
-                               const char *refresh_data,
-                               uint32_t noreveal_index,
-                               TALER_EXCHANGE_RefreshRevealCallback reveal_cb,
-                               void *reveal_cb_cls);
+struct TALER_EXCHANGE_RefreshesRevealHandle *
+TALER_EXCHANGE_refreshes_reveal (struct TALER_EXCHANGE_Handle *exchange,
+                                 size_t refresh_data_length,
+                                 const char *refresh_data,
+                                 uint32_t noreveal_index,
+                                 TALER_EXCHANGE_RefreshesRevealCallback
+                                 reveal_cb,
+                                 void *reveal_cb_cls);
 
 
 /**
@@ -1462,24 +1459,25 @@ TALER_EXCHANGE_refresh_reveal (struct 
TALER_EXCHANGE_Handle *exchange,
  * @param rrh the refresh reval handle
  */
 void
-TALER_EXCHANGE_refresh_reveal_cancel (struct
-                                      TALER_EXCHANGE_RefreshRevealHandle *rrh);
+TALER_EXCHANGE_refreshes_reveal_cancel (struct
+                                        TALER_EXCHANGE_RefreshesRevealHandle *
+                                        rrh);
 
 
-/* ********************* /refresh/link ***************************** */
+/* ********************* /coins/$COIN_PUB/link ***************************** */
 
 
 /**
- * @brief A /refresh/link Handle
+ * @brief A /coins/$COIN_PUB/link Handle
  */
-struct TALER_EXCHANGE_RefreshLinkHandle;
+struct TALER_EXCHANGE_LinkHandle;
 
 
 /**
- * Callbacks of this type are used to return the final result of
- * submitting a /refresh/link request to a exchange.  If the operation was
- * successful, this function returns the signatures over the coins
- * that were created when the original coin was melted.
+ * Callbacks of this type are used to return the final result of submitting a
+ * /coins/$COIN_PUB/link request to a exchange.  If the operation was
+ * successful, this function returns the signatures over the coins that were
+ * created when the original coin was melted.
  *
  * @param cls closure
  * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful 
status request
@@ -1492,26 +1490,24 @@ struct TALER_EXCHANGE_RefreshLinkHandle;
  * @param full_response full response from the exchange (for logging, in case 
of errors)
  */
 typedef void
-(*TALER_EXCHANGE_RefreshLinkCallback) (void *cls,
-                                       unsigned int http_status,
-                                       enum TALER_ErrorCode ec,
-                                       unsigned int num_coins,
-                                       const struct
-                                       TALER_CoinSpendPrivateKeyP *coin_privs,
-                                       const struct
-                                       TALER_DenominationSignature *sigs,
-                                       const struct
-                                       TALER_DenominationPublicKey *pubs,
-                                       const json_t *full_response);
+(*TALER_EXCHANGE_LinkCallback) (void *cls,
+                                unsigned int http_status,
+                                enum TALER_ErrorCode ec,
+                                unsigned int num_coins,
+                                const struct
+                                TALER_CoinSpendPrivateKeyP *coin_privs,
+                                const struct
+                                TALER_DenominationSignature *sigs,
+                                const struct
+                                TALER_DenominationPublicKey *pubs,
+                                const json_t *full_response);
 
 
 /**
- * Submit a refresh link request to the exchange and get the
- * exchange's response.
+ * Submit a link request to the exchange and get the exchange's response.
  *
- * This API is typically not used by anyone, it is more a threat
- * against those trying to receive a funds transfer by abusing the
- * /refresh protocol.
+ * This API is typically not used by anyone, it is more a threat against those
+ * trying to receive a funds transfer by abusing the refresh protocol.
  *
  * @param exchange the exchange handle; the exchange must be ready to operate
  * @param coin_priv private key to request link data for
@@ -1520,22 +1516,21 @@ typedef void
  * @param link_cb_cls closure for @a link_cb
  * @return a handle for this request
  */
-struct TALER_EXCHANGE_RefreshLinkHandle *
-TALER_EXCHANGE_refresh_link (struct TALER_EXCHANGE_Handle *exchange,
-                             const struct TALER_CoinSpendPrivateKeyP 
*coin_priv,
-                             TALER_EXCHANGE_RefreshLinkCallback link_cb,
-                             void *link_cb_cls);
+struct TALER_EXCHANGE_LinkHandle *
+TALER_EXCHANGE_link (struct TALER_EXCHANGE_Handle *exchange,
+                     const struct TALER_CoinSpendPrivateKeyP *coin_priv,
+                     TALER_EXCHANGE_LinkCallback link_cb,
+                     void *link_cb_cls);
 
 
 /**
- * Cancel a refresh link request.  This function cannot be used
+ * Cancel a link request.  This function cannot be used
  * on a request handle if the callback was already invoked.
  *
- * @param rlh the refresh link handle
+ * @param lh the link handle
  */
 void
-TALER_EXCHANGE_refresh_link_cancel (struct
-                                    TALER_EXCHANGE_RefreshLinkHandle *rlh);
+TALER_EXCHANGE_link_cancel (struct TALER_EXCHANGE_LinkHandle *lh);
 
 
 /* ********************* /transfers/$WTID *********************** */
@@ -1709,6 +1704,46 @@ TALER_EXCHANGE_verify_coin_history (const struct
                                     struct TALER_Amount *total);
 
 
+/**
+ * Parse history given in JSON format and return it in binary
+ * format.
+ *
+ * @param exchange connection to the exchange we can use
+ * @param history JSON array with the history
+ * @param reserve_pub public key of the reserve to inspect
+ * @param currency currency we expect the balance to be in
+ * @param[out] balance final balance
+ * @param history_length number of entries in @a history
+ * @param[out] rhistory array of length @a history_length, set to the
+ *             parsed history entries
+ * @return #GNUNET_OK if history was valid and @a rhistory and @a balance
+ *         were set,
+ *         #GNUNET_SYSERR if there was a protocol violation in @a history
+ */
+int
+TALER_EXCHANGE_parse_reserve_history (struct TALER_EXCHANGE_Handle *exchange,
+                                      const json_t *history,
+                                      const struct
+                                      TALER_ReservePublicKeyP *reserve_pub,
+                                      const char *currency,
+                                      struct TALER_Amount *balance,
+                                      unsigned int history_length,
+                                      struct TALER_EXCHANGE_ReserveHistory *
+                                      rhistory);
+
+
+/**
+ * Free memory (potentially) allocated by 
#TALER_EXCHANGE_parse_reserve_history().
+ *
+ * @param rhistory result to free
+ * @param len number of entries in @a rhistory
+ */
+void
+TALER_EXCHANGE_free_reserve_history (struct
+                                     TALER_EXCHANGE_ReserveHistory *rhistory,
+                                     unsigned int len);
+
+
 /* ********************* /recoup *********************** */
 
 
diff --git a/src/include/taler_exchangedb_plugin.h 
b/src/include/taler_exchangedb_plugin.h
index 4db76dd7..754ac1ef 100644
--- a/src/include/taler_exchangedb_plugin.h
+++ b/src/include/taler_exchangedb_plugin.h
@@ -2087,8 +2087,8 @@ struct TALER_EXCHANGEDB_Plugin
    * @param cls the @e cls of this struct with the plugin-specific state
    * @param session database handle to use
    * @param rc commitment to use for the lookup
-   * @param[out] refresh_melt where to store the result; note that
-   *             refresh_melt->session.coin.denom_sig will be set to NULL
+   * @param[out] melt where to store the result; note that
+   *             melt->session.coin.denom_sig will be set to NULL
    *             and is not fetched by this routine (as it is not needed by 
the client)
    * @return transaction status
    */
@@ -2096,7 +2096,7 @@ struct TALER_EXCHANGEDB_Plugin
   (*get_melt)(void *cls,
               struct TALER_EXCHANGEDB_Session *session,
               const struct TALER_RefreshCommitmentP *rc,
-              struct TALER_EXCHANGEDB_RefreshMelt *refresh_melt);
+              struct TALER_EXCHANGEDB_RefreshMelt *melt);
 
 
   /**
diff --git a/src/include/taler_testing_lib.h b/src/include/taler_testing_lib.h
index 5aa50155..7dadc1f4 100644
--- a/src/include/taler_testing_lib.h
+++ b/src/include/taler_testing_lib.h
@@ -1307,10 +1307,10 @@ TALER_TESTING_cmd_deposit_with_retry (struct 
TALER_TESTING_Command cmd);
  * @return the command.
  */
 struct TALER_TESTING_Command
-TALER_TESTING_cmd_refresh_melt (const char *label,
-                                const char *coin_reference,
-                                unsigned int expected_response_code,
-                                ...);
+TALER_TESTING_cmd_melt (const char *label,
+                        const char *coin_reference,
+                        unsigned int expected_response_code,
+                        ...);
 
 
 /**
@@ -1326,10 +1326,10 @@ TALER_TESTING_cmd_refresh_melt (const char *label,
  * @return the command.
  */
 struct TALER_TESTING_Command
-TALER_TESTING_cmd_refresh_melt_double (const char *label,
-                                       const char *coin_reference,
-                                       unsigned int expected_response_code,
-                                       ...);
+TALER_TESTING_cmd_melt_double (const char *label,
+                               const char *coin_reference,
+                               unsigned int expected_response_code,
+                               ...);
 
 
 /**
@@ -1339,7 +1339,7 @@ TALER_TESTING_cmd_refresh_melt_double (const char *label,
  * @return modified command.
  */
 struct TALER_TESTING_Command
-TALER_TESTING_cmd_refresh_melt_with_retry (struct TALER_TESTING_Command cmd);
+TALER_TESTING_cmd_melt_with_retry (struct TALER_TESTING_Command cmd);
 
 
 /**
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
index f03522a6..87f34309 100644
--- a/src/lib/Makefile.am
+++ b/src/lib/Makefile.am
@@ -25,13 +25,16 @@ libtalerexchange_la_SOURCES = \
   exchange_api_common.c \
   exchange_api_handle.c exchange_api_handle.h \
   exchange_api_deposit.c \
+  exchange_api_deposits_get.c \
+  exchange_api_link.c \
+  exchange_api_melt.c \
   exchange_api_recoup.c \
-  exchange_api_refresh.c \
-  exchange_api_refresh_link.c \
+  exchange_api_refresh_common.c exchange_api_refresh_common.h \
+  exchange_api_refreshes_reveal.c \
   exchange_api_refund.c \
-  exchange_api_reserve.c \
-  exchange_api_track_transaction.c \
-  exchange_api_track_transfer.c \
+  exchange_api_reserves_get.c \
+  exchange_api_transfers_get.c \
+  exchange_api_withdraw.c \
   exchange_api_wire.c
 libtalerexchange_la_LIBADD = \
   libtalerauditor.la \
diff --git a/src/lib/exchange_api_common.c b/src/lib/exchange_api_common.c
index d5f31b06..6b0d638e 100644
--- a/src/lib/exchange_api_common.c
+++ b/src/lib/exchange_api_common.c
@@ -26,6 +26,422 @@
 #include "taler_signatures.h"
 
 
+/**
+ * Parse history given in JSON format and return it in binary
+ * format.
+ *
+ * @param exchange connection to the exchange we can use
+ * @param history JSON array with the history
+ * @param reserve_pub public key of the reserve to inspect
+ * @param currency currency we expect the balance to be in
+ * @param[out] balance final balance
+ * @param history_length number of entries in @a history
+ * @param[out] rhistory array of length @a history_length, set to the
+ *             parsed history entries
+ * @return #GNUNET_OK if history was valid and @a rhistory and @a balance
+ *         were set,
+ *         #GNUNET_SYSERR if there was a protocol violation in @a history
+ */
+int
+TALER_EXCHANGE_parse_reserve_history (struct TALER_EXCHANGE_Handle *exchange,
+                                      const json_t *history,
+                                      const struct
+                                      TALER_ReservePublicKeyP *reserve_pub,
+                                      const char *currency,
+                                      struct TALER_Amount *balance,
+                                      unsigned int history_length,
+                                      struct TALER_EXCHANGE_ReserveHistory *
+                                      rhistory)
+{
+  struct GNUNET_HashCode uuid[history_length];
+  unsigned int uuid_off;
+  struct TALER_Amount total_in;
+  struct TALER_Amount total_out;
+  size_t off;
+
+  GNUNET_assert (GNUNET_OK ==
+                 TALER_amount_get_zero (currency,
+                                        &total_in));
+  GNUNET_assert (GNUNET_OK ==
+                 TALER_amount_get_zero (currency,
+                                        &total_out));
+  uuid_off = 0;
+  for (off = 0; off<history_length; off++)
+  {
+    json_t *transaction;
+    struct TALER_Amount amount;
+    const char *type;
+    struct GNUNET_JSON_Specification hist_spec[] = {
+      GNUNET_JSON_spec_string ("type", &type),
+      TALER_JSON_spec_amount ("amount",
+                              &amount),
+      /* 'wire' and 'signature' are optional depending on 'type'! */
+      GNUNET_JSON_spec_end ()
+    };
+
+    transaction = json_array_get (history,
+                                  off);
+    if (GNUNET_OK !=
+        GNUNET_JSON_parse (transaction,
+                           hist_spec,
+                           NULL, NULL))
+    {
+      GNUNET_break_op (0);
+      return GNUNET_SYSERR;
+    }
+    rhistory[off].amount = amount;
+
+    if (0 == strcasecmp (type,
+                         "DEPOSIT"))
+    {
+      const char *wire_url;
+      void *wire_reference;
+      size_t wire_reference_size;
+      struct GNUNET_TIME_Absolute timestamp;
+
+      struct GNUNET_JSON_Specification withdraw_spec[] = {
+        GNUNET_JSON_spec_varsize ("wire_reference",
+                                  &wire_reference,
+                                  &wire_reference_size),
+        GNUNET_JSON_spec_absolute_time ("timestamp",
+                                        &timestamp),
+        GNUNET_JSON_spec_string ("sender_account_url",
+                                 &wire_url),
+        GNUNET_JSON_spec_end ()
+      };
+
+      rhistory[off].type = TALER_EXCHANGE_RTT_DEPOSIT;
+      if (GNUNET_OK !=
+          TALER_amount_add (&total_in,
+                            &total_in,
+                            &amount))
+      {
+        /* overflow in history already!? inconceivable! Bad exchange! */
+        GNUNET_break_op (0);
+        return GNUNET_SYSERR;
+      }
+      if (GNUNET_OK !=
+          GNUNET_JSON_parse (transaction,
+                             withdraw_spec,
+                             NULL, NULL))
+      {
+        GNUNET_break_op (0);
+        return GNUNET_SYSERR;
+      }
+      rhistory[off].details.in_details.sender_url = GNUNET_strdup (wire_url);
+      rhistory[off].details.in_details.wire_reference = wire_reference;
+      rhistory[off].details.in_details.wire_reference_size =
+        wire_reference_size;
+      rhistory[off].details.in_details.timestamp = timestamp;
+      /* end type==DEPOSIT */
+    }
+    else if (0 == strcasecmp (type,
+                              "WITHDRAW"))
+    {
+      struct TALER_ReserveSignatureP sig;
+      struct TALER_WithdrawRequestPS withdraw_purpose;
+      struct GNUNET_JSON_Specification withdraw_spec[] = {
+        GNUNET_JSON_spec_fixed_auto ("reserve_sig",
+                                     &sig),
+        TALER_JSON_spec_amount_nbo ("withdraw_fee",
+                                    &withdraw_purpose.withdraw_fee),
+        GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
+                                     &withdraw_purpose.h_denomination_pub),
+        GNUNET_JSON_spec_fixed_auto ("h_coin_envelope",
+                                     &withdraw_purpose.h_coin_envelope),
+        GNUNET_JSON_spec_end ()
+      };
+
+      rhistory[off].type = TALER_EXCHANGE_RTT_WITHDRAWAL;
+      if (GNUNET_OK !=
+          GNUNET_JSON_parse (transaction,
+                             withdraw_spec,
+                             NULL, NULL))
+      {
+        GNUNET_break_op (0);
+        return GNUNET_SYSERR;
+      }
+      withdraw_purpose.purpose.size
+        = htonl (sizeof (withdraw_purpose));
+      withdraw_purpose.purpose.purpose
+        = htonl (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW);
+      withdraw_purpose.reserve_pub = *reserve_pub;
+      TALER_amount_hton (&withdraw_purpose.amount_with_fee,
+                         &amount);
+      /* Check that the signature is a valid withdraw request */
+      if (GNUNET_OK !=
+          GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW,
+                                      &withdraw_purpose.purpose,
+                                      &sig.eddsa_signature,
+                                      &reserve_pub->eddsa_pub))
+      {
+        GNUNET_break_op (0);
+        GNUNET_JSON_parse_free (withdraw_spec);
+        return GNUNET_SYSERR;
+      }
+      /* check that withdraw fee matches expectations! */
+      {
+        const struct TALER_EXCHANGE_Keys *key_state;
+        const struct TALER_EXCHANGE_DenomPublicKey *dki;
+        struct TALER_Amount fee;
+
+        key_state = TALER_EXCHANGE_get_keys (exchange);
+        dki = TALER_EXCHANGE_get_denomination_key_by_hash (key_state,
+                                                           &withdraw_purpose.
+                                                           h_denomination_pub);
+        TALER_amount_ntoh (&fee,
+                           &withdraw_purpose.withdraw_fee);
+        if ( (GNUNET_YES !=
+              TALER_amount_cmp_currency (&fee,
+                                         &dki->fee_withdraw)) ||
+             (0 !=
+              TALER_amount_cmp (&fee,
+                                &dki->fee_withdraw)) )
+        {
+          GNUNET_break_op (0);
+          GNUNET_JSON_parse_free (withdraw_spec);
+          return GNUNET_SYSERR;
+        }
+      }
+      rhistory[off].details.out_authorization_sig
+        = json_object_get (transaction,
+                           "signature");
+      /* Check check that the same withdraw transaction
+         isn't listed twice by the exchange. We use the
+         "uuid" array to remember the hashes of all
+         purposes, and compare the hashes to find
+         duplicates. *///
+      GNUNET_CRYPTO_hash (&withdraw_purpose,
+                          ntohl (withdraw_purpose.purpose.size),
+                          &uuid[uuid_off]);
+      for (unsigned int i = 0; i<uuid_off; i++)
+      {
+        if (0 == GNUNET_memcmp (&uuid[uuid_off],
+                                &uuid[i]))
+        {
+          GNUNET_break_op (0);
+          GNUNET_JSON_parse_free (withdraw_spec);
+          return GNUNET_SYSERR;
+        }
+      }
+      uuid_off++;
+
+      if (GNUNET_OK !=
+          TALER_amount_add (&total_out,
+                            &total_out,
+                            &amount))
+      {
+        /* overflow in history already!? inconceivable! Bad exchange! */
+        GNUNET_break_op (0);
+        GNUNET_JSON_parse_free (withdraw_spec);
+        return GNUNET_SYSERR;
+      }
+      /* end type==WITHDRAW */
+    }
+    else if (0 == strcasecmp (type,
+                              "RECOUP"))
+    {
+      struct TALER_RecoupConfirmationPS pc;
+      struct GNUNET_TIME_Absolute timestamp;
+      const struct TALER_EXCHANGE_Keys *key_state;
+      struct GNUNET_JSON_Specification recoup_spec[] = {
+        GNUNET_JSON_spec_fixed_auto ("coin_pub",
+                                     &pc.coin_pub),
+        GNUNET_JSON_spec_fixed_auto ("exchange_sig",
+                                     &rhistory[off].details.recoup_details.
+                                     exchange_sig),
+        GNUNET_JSON_spec_fixed_auto ("exchange_pub",
+                                     &rhistory[off].details.recoup_details.
+                                     exchange_pub),
+        GNUNET_JSON_spec_absolute_time_nbo ("timestamp",
+                                            &pc.timestamp),
+        GNUNET_JSON_spec_end ()
+      };
+
+      rhistory[off].type = TALER_EXCHANGE_RTT_RECOUP;
+      rhistory[off].amount = amount;
+      if (GNUNET_OK !=
+          GNUNET_JSON_parse (transaction,
+                             recoup_spec,
+                             NULL, NULL))
+      {
+        GNUNET_break_op (0);
+        return GNUNET_SYSERR;
+      }
+      rhistory[off].details.recoup_details.coin_pub = pc.coin_pub;
+      TALER_amount_hton (&pc.recoup_amount,
+                         &amount);
+      pc.purpose.size = htonl (sizeof (pc));
+      pc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP);
+      pc.reserve_pub = *reserve_pub;
+      timestamp = GNUNET_TIME_absolute_ntoh (pc.timestamp);
+      rhistory[off].details.recoup_details.timestamp = timestamp;
+
+      key_state = TALER_EXCHANGE_get_keys (exchange);
+      if (GNUNET_OK !=
+          TALER_EXCHANGE_test_signing_key (key_state,
+                                           &rhistory[off].details.
+                                           recoup_details.exchange_pub))
+      {
+        GNUNET_break_op (0);
+        return GNUNET_SYSERR;
+      }
+      if (GNUNET_OK !=
+          GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP,
+                                      &pc.purpose,
+                                      &rhistory[off].details.recoup_details.
+                                      exchange_sig.eddsa_signature,
+                                      &rhistory[off].details.recoup_details.
+                                      exchange_pub.eddsa_pub))
+      {
+        GNUNET_break_op (0);
+        return GNUNET_SYSERR;
+      }
+      if (GNUNET_OK !=
+          TALER_amount_add (&total_in,
+                            &total_in,
+                            &rhistory[off].amount))
+      {
+        /* overflow in history already!? inconceivable! Bad exchange! */
+        GNUNET_break_op (0);
+        return GNUNET_SYSERR;
+      }
+      /* end type==RECOUP */
+    }
+    else if (0 == strcasecmp (type,
+                              "CLOSING"))
+    {
+      const struct TALER_EXCHANGE_Keys *key_state;
+      struct TALER_ReserveCloseConfirmationPS rcc;
+      struct GNUNET_TIME_Absolute timestamp;
+      struct GNUNET_JSON_Specification closing_spec[] = {
+        GNUNET_JSON_spec_string ("receiver_account_details",
+                                 &rhistory[off].details.close_details.
+                                 receiver_account_details),
+        GNUNET_JSON_spec_fixed_auto ("wtid",
+                                     
&rhistory[off].details.close_details.wtid),
+        GNUNET_JSON_spec_fixed_auto ("exchange_sig",
+                                     &rhistory[off].details.close_details.
+                                     exchange_sig),
+        GNUNET_JSON_spec_fixed_auto ("exchange_pub",
+                                     &rhistory[off].details.close_details.
+                                     exchange_pub),
+        TALER_JSON_spec_amount_nbo ("closing_fee",
+                                    &rcc.closing_fee),
+        GNUNET_JSON_spec_absolute_time_nbo ("timestamp",
+                                            &rcc.timestamp),
+        GNUNET_JSON_spec_end ()
+      };
+
+      rhistory[off].type = TALER_EXCHANGE_RTT_CLOSE;
+      rhistory[off].amount = amount;
+      if (GNUNET_OK !=
+          GNUNET_JSON_parse (transaction,
+                             closing_spec,
+                             NULL, NULL))
+      {
+        GNUNET_break_op (0);
+        return GNUNET_SYSERR;
+      }
+      TALER_amount_hton (&rcc.closing_amount,
+                         &amount);
+      GNUNET_CRYPTO_hash (
+        rhistory[off].details.close_details.receiver_account_details,
+        strlen (
+          rhistory[off].details.close_details.receiver_account_details) + 1,
+        &rcc.h_wire);
+      rcc.wtid = rhistory[off].details.close_details.wtid;
+      rcc.purpose.size = htonl (sizeof (rcc));
+      rcc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_RESERVE_CLOSED);
+      rcc.reserve_pub = *reserve_pub;
+      timestamp = GNUNET_TIME_absolute_ntoh (rcc.timestamp);
+      rhistory[off].details.close_details.timestamp = timestamp;
+
+      key_state = TALER_EXCHANGE_get_keys (exchange);
+      if (GNUNET_OK !=
+          TALER_EXCHANGE_test_signing_key (key_state,
+                                           
&rhistory[off].details.close_details.
+                                           exchange_pub))
+      {
+        GNUNET_break_op (0);
+        return GNUNET_SYSERR;
+      }
+      if (GNUNET_OK !=
+          GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_EXCHANGE_RESERVE_CLOSED,
+                                      &rcc.purpose,
+                                      &rhistory[off].details.close_details.
+                                      exchange_sig.eddsa_signature,
+                                      &rhistory[off].details.close_details.
+                                      exchange_pub.eddsa_pub))
+      {
+        GNUNET_break_op (0);
+        return GNUNET_SYSERR;
+      }
+      if (GNUNET_OK !=
+          TALER_amount_add (&total_out,
+                            &total_out,
+                            &rhistory[off].amount))
+      {
+        /* overflow in history already!? inconceivable! Bad exchange! */
+        GNUNET_break_op (0);
+        return GNUNET_SYSERR;
+      }
+      /* end type==CLOSING */
+    }
+    else
+    {
+      /* unexpected 'type', protocol incompatibility, complain! */
+      GNUNET_break_op (0);
+      return GNUNET_SYSERR;
+    }
+  }
+
+  /* check balance = total_in - total_out < withdraw-amount */
+  if (GNUNET_SYSERR ==
+      TALER_amount_subtract (balance,
+                             &total_in,
+                             &total_out))
+  {
+    /* total_in < total_out, why did the exchange ever allow this!? */
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  return GNUNET_OK;
+}
+
+
+/**
+ * Free memory (potentially) allocated by 
#TALER_EXCHANGE_parse_reserve_history().
+ *
+ * @param rhistory result to free
+ * @param len number of entries in @a rhistory
+ */
+void
+TALER_EXCHANGE_free_reserve_history (struct
+                                     TALER_EXCHANGE_ReserveHistory *rhistory,
+                                     unsigned int len)
+{
+  for (unsigned int i = 0; i<len; i++)
+  {
+    switch (rhistory[i].type)
+    {
+    case TALER_EXCHANGE_RTT_DEPOSIT:
+      GNUNET_free_non_null (rhistory[i].details.in_details.wire_reference);
+      GNUNET_free_non_null (rhistory[i].details.in_details.sender_url);
+      break;
+    case TALER_EXCHANGE_RTT_WITHDRAWAL:
+      break;
+    case TALER_EXCHANGE_RTT_RECOUP:
+      break;
+    case TALER_EXCHANGE_RTT_CLOSE:
+      /* FIXME: should we free "receiver_account_details" ? */
+      break;
+    }
+  }
+  GNUNET_free (rhistory);
+}
+
+
 /**
  * Verify a coins transaction history as returned by the exchange.
  *
diff --git a/src/lib/exchange_api_track_transaction.c 
b/src/lib/exchange_api_deposits_get.c
similarity index 96%
rename from src/lib/exchange_api_track_transaction.c
rename to src/lib/exchange_api_deposits_get.c
index 636b986f..1ffd738c 100644
--- a/src/lib/exchange_api_track_transaction.c
+++ b/src/lib/exchange_api_deposits_get.c
@@ -15,8 +15,8 @@
   <http://www.gnu.org/licenses/>
 */
 /**
- * @file lib/exchange_api_track_transaction.c
- * @brief Implementation of the /track/transaction request of the exchange's 
HTTP API
+ * @file lib/exchange_api_deposits_get.c
+ * @brief Implementation of the /deposits/ GET request
  * @author Christian Grothoff
  */
 #include "platform.h"
@@ -378,15 +378,13 @@ TALER_EXCHANGE_deposits_get (struct TALER_EXCHANGE_Handle 
*exchange,
 
 
 /**
- * Cancel deposit wtid request.  This function cannot be used on a request
+ * Cancel /deposits/$WTID request.  This function cannot be used on a request
  * handle if a response is already served for it.
  *
  * @param dwh the wire deposits request handle
  */
 void
-TALER_EXCHANGE_deposits_get_cancel (struct
-                                    TALER_EXCHANGE_DepositGetHandle *
-                                    dwh)
+TALER_EXCHANGE_deposits_get_cancel (struct TALER_EXCHANGE_DepositGetHandle 
*dwh)
 {
   if (NULL != dwh->job)
   {
@@ -399,4 +397,4 @@ TALER_EXCHANGE_deposits_get_cancel (struct
 }
 
 
-/* end of exchange_api_track_transaction.c */
+/* end of exchange_api_deposits_get.c */
diff --git a/src/lib/exchange_api_refresh_link.c b/src/lib/exchange_api_link.c
similarity index 74%
rename from src/lib/exchange_api_refresh_link.c
rename to src/lib/exchange_api_link.c
index 4b4b38ba..3204ca84 100644
--- a/src/lib/exchange_api_refresh_link.c
+++ b/src/lib/exchange_api_link.c
@@ -15,8 +15,8 @@
   <http://www.gnu.org/licenses/>
 */
 /**
- * @file lib/exchange_api_refresh_link.c
- * @brief Implementation of the /refresh/link request of the exchange's HTTP 
API
+ * @file lib/exchange_api_link.c
+ * @brief Implementation of the /coins/$COIN_PUB/link request
  * @author Christian Grothoff
  */
 #include "platform.h"
@@ -31,9 +31,9 @@
 
 
 /**
- * @brief A /refresh/link Handle
+ * @brief A /coins/$COIN_PUB/link Handle
  */
-struct TALER_EXCHANGE_RefreshLinkHandle
+struct TALER_EXCHANGE_LinkHandle
 {
 
   /**
@@ -54,7 +54,7 @@ struct TALER_EXCHANGE_RefreshLinkHandle
   /**
    * Function to call with the result.
    */
-  TALER_EXCHANGE_RefreshLinkCallback link_cb;
+  TALER_EXCHANGE_LinkCallback link_cb;
 
   /**
    * Closure for @e cb.
@@ -73,7 +73,7 @@ struct TALER_EXCHANGE_RefreshLinkHandle
  * Parse the provided linkage data from the "200 OK" response
  * for one of the coins.
  *
- * @param rlh refresh link handle
+ * @param lh link handle
  * @param json json reply with the data for one coin
  * @param coin_num number of the coin to decode
  * @param trans_pub our transfer public key
@@ -83,13 +83,13 @@ struct TALER_EXCHANGE_RefreshLinkHandle
  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
  */
 static int
-parse_refresh_link_coin (const struct TALER_EXCHANGE_RefreshLinkHandle *rlh,
-                         const json_t *json,
-                         unsigned int coin_num,
-                         const struct TALER_TransferPublicKeyP *trans_pub,
-                         struct TALER_CoinSpendPrivateKeyP *coin_priv,
-                         struct TALER_DenominationSignature *sig,
-                         struct TALER_DenominationPublicKey *pub)
+parse_link_coin (const struct TALER_EXCHANGE_LinkHandle *lh,
+                 const json_t *json,
+                 unsigned int coin_num,
+                 const struct TALER_TransferPublicKeyP *trans_pub,
+                 struct TALER_CoinSpendPrivateKeyP *coin_priv,
+                 struct TALER_DenominationSignature *sig,
+                 struct TALER_DenominationPublicKey *pub)
 {
   struct GNUNET_CRYPTO_RsaSignature *bsig;
   struct GNUNET_CRYPTO_RsaPublicKey *rpub;
@@ -114,7 +114,7 @@ parse_refresh_link_coin (const struct 
TALER_EXCHANGE_RefreshLinkHandle *rlh,
   }
 
   TALER_link_recover_transfer_secret (trans_pub,
-                                      &rlh->coin_priv,
+                                      &lh->coin_priv,
                                       &secret);
   TALER_planchet_setup_refresh (&secret,
                                 coin_num,
@@ -133,7 +133,7 @@ parse_refresh_link_coin (const struct 
TALER_EXCHANGE_RefreshLinkHandle *rlh,
 
     ldp.purpose.size = htonl (sizeof (ldp));
     ldp.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_LINK);
-    GNUNET_CRYPTO_eddsa_key_get_public (&rlh->coin_priv.eddsa_priv,
+    GNUNET_CRYPTO_eddsa_key_get_public (&lh->coin_priv.eddsa_priv,
                                         &ldp.old_coin_pub.eddsa_pub);
     ldp.transfer_pub = *trans_pub;
     pub->rsa_public_key = rpub;
@@ -175,13 +175,13 @@ parse_refresh_link_coin (const struct 
TALER_EXCHANGE_RefreshLinkHandle *rlh,
  * Parse the provided linkage data from the "200 OK" response
  * for one of the coins.
  *
- * @param[in,out] rlh refresh link handle (callback may be zero'ed out)
+ * @param[in,out] lh link handle (callback may be zero'ed out)
  * @param json json reply with the data for one coin
  * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
  */
 static int
-parse_refresh_link_ok (struct TALER_EXCHANGE_RefreshLinkHandle *rlh,
-                       const json_t *json)
+parse_link_ok (struct TALER_EXCHANGE_LinkHandle *lh,
+               const json_t *json)
 {
   unsigned int session;
   unsigned int num_coins;
@@ -277,14 +277,14 @@ parse_refresh_link_ok (struct 
TALER_EXCHANGE_RefreshLinkHandle *rlh,
       {
         GNUNET_assert (i + off_coin < num_coins);
         if (GNUNET_OK !=
-            parse_refresh_link_coin (rlh,
-                                     json_array_get (jsona,
-                                                     i),
-                                     i,
-                                     &trans_pub,
-                                     &coin_privs[i + off_coin],
-                                     &sigs[i + off_coin],
-                                     &pubs[i + off_coin]))
+            parse_link_coin (lh,
+                             json_array_get (jsona,
+                                             i),
+                             i,
+                             &trans_pub,
+                             &coin_privs[i + off_coin],
+                             &sigs[i + off_coin],
+                             &pubs[i + off_coin]))
         {
           GNUNET_break_op (0);
           break;
@@ -304,15 +304,15 @@ parse_refresh_link_ok (struct 
TALER_EXCHANGE_RefreshLinkHandle *rlh,
 
     if (off_coin == num_coins)
     {
-      rlh->link_cb (rlh->link_cb_cls,
-                    MHD_HTTP_OK,
-                    TALER_EC_NONE,
-                    num_coins,
-                    coin_privs,
-                    sigs,
-                    pubs,
-                    json);
-      rlh->link_cb = NULL;
+      lh->link_cb (lh->link_cb_cls,
+                   MHD_HTTP_OK,
+                   TALER_EC_NONE,
+                   num_coins,
+                   coin_privs,
+                   sigs,
+                   pubs,
+                   json);
+      lh->link_cb = NULL;
       ret = GNUNET_OK;
     }
     else
@@ -337,29 +337,29 @@ parse_refresh_link_ok (struct 
TALER_EXCHANGE_RefreshLinkHandle *rlh,
 
 /**
  * Function called when we're done processing the
- * HTTP /refresh/link request.
+ * HTTP /coins/$COIN_PUB/link request.
  *
- * @param cls the `struct TALER_EXCHANGE_RefreshLinkHandle`
+ * @param cls the `struct TALER_EXCHANGE_LinkHandle`
  * @param response_code HTTP response code, 0 on error
  * @param response parsed JSON result, NULL on error
  */
 static void
-handle_refresh_link_finished (void *cls,
-                              long response_code,
-                              const void *response)
+handle_link_finished (void *cls,
+                      long response_code,
+                      const void *response)
 {
-  struct TALER_EXCHANGE_RefreshLinkHandle *rlh = cls;
+  struct TALER_EXCHANGE_LinkHandle *lh = cls;
   const json_t *j = response;
 
-  rlh->job = NULL;
+  lh->job = NULL;
   switch (response_code)
   {
   case 0:
     break;
   case MHD_HTTP_OK:
     if (GNUNET_OK !=
-        parse_refresh_link_ok (rlh,
-                               j))
+        parse_link_ok (lh,
+                       j))
     {
       GNUNET_break_op (0);
       response_code = 0;
@@ -386,25 +386,24 @@ handle_refresh_link_finished (void *cls,
     response_code = 0;
     break;
   }
-  if (NULL != rlh->link_cb)
-    rlh->link_cb (rlh->link_cb_cls,
-                  response_code,
-                  TALER_JSON_get_error_code (j),
-                  0,
-                  NULL,
-                  NULL,
-                  NULL,
-                  j);
-  TALER_EXCHANGE_refresh_link_cancel (rlh);
+  if (NULL != lh->link_cb)
+    lh->link_cb (lh->link_cb_cls,
+                 response_code,
+                 TALER_JSON_get_error_code (j),
+                 0,
+                 NULL,
+                 NULL,
+                 NULL,
+                 j);
+  TALER_EXCHANGE_link_cancel (lh);
 }
 
 
 /**
  * Submit a link request to the exchange and get the exchange's response.
  *
- * This API is typically not used by anyone, it is more a threat
- * against those trying to receive a funds transfer by abusing the
- * /refresh protocol.
+ * This API is typically not used by anyone, it is more a threat against those
+ * trying to receive a funds transfer by abusing the refresh protocol.
  *
  * @param exchange the exchange handle; the exchange must be ready to operate
  * @param coin_priv private key to request link data for
@@ -413,13 +412,13 @@ handle_refresh_link_finished (void *cls,
  * @param link_cb_cls closure for @a link_cb
  * @return a handle for this request
  */
-struct TALER_EXCHANGE_RefreshLinkHandle *
-TALER_EXCHANGE_refresh_link (struct TALER_EXCHANGE_Handle *exchange,
-                             const struct TALER_CoinSpendPrivateKeyP 
*coin_priv,
-                             TALER_EXCHANGE_RefreshLinkCallback link_cb,
-                             void *link_cb_cls)
+struct TALER_EXCHANGE_LinkHandle *
+TALER_EXCHANGE_link (struct TALER_EXCHANGE_Handle *exchange,
+                     const struct TALER_CoinSpendPrivateKeyP *coin_priv,
+                     TALER_EXCHANGE_LinkCallback link_cb,
+                     void *link_cb_cls)
 {
-  struct TALER_EXCHANGE_RefreshLinkHandle *rlh;
+  struct TALER_EXCHANGE_LinkHandle *lh;
   CURL *eh;
   struct GNUNET_CURL_Context *ctx;
   struct TALER_CoinSpendPublicKeyP coin_pub;
@@ -449,42 +448,41 @@ TALER_EXCHANGE_refresh_link (struct TALER_EXCHANGE_Handle 
*exchange,
                      "/coins/%s/link",
                      pub_str);
   }
-  rlh = GNUNET_new (struct TALER_EXCHANGE_RefreshLinkHandle);
-  rlh->exchange = exchange;
-  rlh->link_cb = link_cb;
-  rlh->link_cb_cls = link_cb_cls;
-  rlh->coin_priv = *coin_priv;
-  rlh->url = TEAH_path_to_url (exchange,
-                               arg_str);
-  eh = TEL_curl_easy_get (rlh->url);
+  lh = GNUNET_new (struct TALER_EXCHANGE_LinkHandle);
+  lh->exchange = exchange;
+  lh->link_cb = link_cb;
+  lh->link_cb_cls = link_cb_cls;
+  lh->coin_priv = *coin_priv;
+  lh->url = TEAH_path_to_url (exchange,
+                              arg_str);
+  eh = TEL_curl_easy_get (lh->url);
   ctx = TEAH_handle_to_context (exchange);
-  rlh->job = GNUNET_CURL_job_add (ctx,
-                                  eh,
-                                  GNUNET_YES,
-                                  &handle_refresh_link_finished,
-                                  rlh);
-  return rlh;
+  lh->job = GNUNET_CURL_job_add (ctx,
+                                 eh,
+                                 GNUNET_YES,
+                                 &handle_link_finished,
+                                 lh);
+  return lh;
 }
 
 
 /**
- * Cancel a refresh link request.  This function cannot be used
+ * Cancel a link request.  This function cannot be used
  * on a request handle if the callback was already invoked.
  *
- * @param rlh the refresh link handle
+ * @param lh the link handle
  */
 void
-TALER_EXCHANGE_refresh_link_cancel (struct
-                                    TALER_EXCHANGE_RefreshLinkHandle *rlh)
+TALER_EXCHANGE_link_cancel (struct TALER_EXCHANGE_LinkHandle *lh)
 {
-  if (NULL != rlh->job)
+  if (NULL != lh->job)
   {
-    GNUNET_CURL_job_cancel (rlh->job);
-    rlh->job = NULL;
+    GNUNET_CURL_job_cancel (lh->job);
+    lh->job = NULL;
   }
-  GNUNET_free (rlh->url);
-  GNUNET_free (rlh);
+  GNUNET_free (lh->url);
+  GNUNET_free (lh);
 }
 
 
-/* end of exchange_api_refresh_link.c */
+/* end of exchange_api_link.c */
diff --git a/src/lib/exchange_api_melt.c b/src/lib/exchange_api_melt.c
new file mode 100644
index 00000000..9c85fa18
--- /dev/null
+++ b/src/lib/exchange_api_melt.c
@@ -0,0 +1,505 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2015-2020 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 lib/exchange_api_melt.c
+ * @brief Implementation of the /coins/$COIN_PUB/melt request
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <jansson.h>
+#include <microhttpd.h> /* just for HTTP status codes */
+#include <gnunet/gnunet_util_lib.h>
+#include <gnunet/gnunet_json_lib.h>
+#include <gnunet/gnunet_curl_lib.h>
+#include "taler_json_lib.h"
+#include "taler_exchange_service.h"
+#include "exchange_api_handle.h"
+#include "taler_signatures.h"
+#include "exchange_api_curl_defaults.h"
+#include "exchange_api_refresh_common.h"
+
+
+/**
+ * @brief A /coins/$COIN_PUB/melt Handle
+ */
+struct TALER_EXCHANGE_MeltHandle
+{
+
+  /**
+   * The connection to exchange this request handle will use
+   */
+  struct TALER_EXCHANGE_Handle *exchange;
+
+  /**
+   * The url for this request.
+   */
+  char *url;
+
+  /**
+   * Context for #TEH_curl_easy_post(). Keeps the data that must
+   * persist for Curl to make the upload.
+   */
+  struct TALER_CURL_PostContext ctx;
+
+  /**
+   * Handle for the request.
+   */
+  struct GNUNET_CURL_Job *job;
+
+  /**
+   * Function to call with refresh melt failure results.
+   */
+  TALER_EXCHANGE_MeltCallback melt_cb;
+
+  /**
+   * Closure for @e result_cb and @e melt_failure_cb.
+   */
+  void *melt_cb_cls;
+
+  /**
+   * Actual information about the melt operation.
+   */
+  struct MeltData *md;
+
+  /**
+   * @brief Public information about the coin's denomination key
+   */
+  struct TALER_EXCHANGE_DenomPublicKey dki;
+};
+
+
+/**
+ * Verify that the signature on the "200 OK" response
+ * from the exchange is valid.
+ *
+ * @param mh melt handle
+ * @param json json reply with the signature
+ * @param[out] exchange_pub public key of the exchange used for the signature
+ * @param[out] noreveal_index set to the noreveal index selected by the 
exchange
+ * @return #GNUNET_OK if the signature is valid, #GNUNET_SYSERR if not
+ */
+static int
+verify_melt_signature_ok (struct TALER_EXCHANGE_MeltHandle *mh,
+                          const json_t *json,
+                          struct TALER_ExchangePublicKeyP *exchange_pub,
+                          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_uint32 ("noreveal_index", noreveal_index),
+    GNUNET_JSON_spec_end ()
+  };
+  struct TALER_RefreshMeltConfirmationPS confirm;
+
+  if (GNUNET_OK !=
+      GNUNET_JSON_parse (json,
+                         spec,
+                         NULL, NULL))
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+
+  /* check that exchange signing key is permitted */
+  key_state = TALER_EXCHANGE_get_keys (mh->exchange);
+  if (GNUNET_OK !=
+      TALER_EXCHANGE_test_signing_key (key_state,
+                                       exchange_pub))
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+
+  /* check that noreveal index is in permitted range */
+  if (TALER_CNC_KAPPA <= *noreveal_index)
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+
+  /* verify signature by exchange */
+  confirm.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_MELT);
+  confirm.purpose.size = htonl (sizeof (struct
+                                        TALER_RefreshMeltConfirmationPS));
+  confirm.rc = mh->md->rc;
+  confirm.noreveal_index = htonl (*noreveal_index);
+  if (GNUNET_OK !=
+      GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_EXCHANGE_CONFIRM_MELT,
+                                  &confirm.purpose,
+                                  &exchange_sig.eddsa_signature,
+                                  &exchange_pub->eddsa_pub))
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  return GNUNET_OK;
+}
+
+
+/**
+ * Verify that the signatures on the "409 CONFLICT" response from the
+ * exchange demonstrating customer double-spending are valid.
+ *
+ * @param mh melt handle
+ * @param json json reply with the signature(s) and transaction history
+ * @return #GNUNET_OK if the signature(s) is valid, #GNUNET_SYSERR if not
+ */
+static int
+verify_melt_signature_conflict (struct TALER_EXCHANGE_MeltHandle *mh,
+                                const json_t *json)
+{
+  json_t *history;
+  struct TALER_Amount original_value;
+  struct TALER_Amount melt_value_with_fee;
+  struct TALER_Amount total;
+  struct TALER_CoinSpendPublicKeyP coin_pub;
+  struct GNUNET_JSON_Specification spec[] = {
+    GNUNET_JSON_spec_json ("history", &history),
+    GNUNET_JSON_spec_fixed_auto ("coin_pub", &coin_pub),
+    TALER_JSON_spec_amount ("original_value", &original_value),
+    TALER_JSON_spec_amount ("requested_value", &melt_value_with_fee),
+    GNUNET_JSON_spec_end ()
+  };
+  const struct MeltedCoin *mc;
+
+  /* parse JSON reply */
+  if (GNUNET_OK !=
+      GNUNET_JSON_parse (json,
+                         spec,
+                         NULL, NULL))
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+
+  /* Find out which coin was deemed problematic by the exchange */
+  mc = &mh->md->melted_coin;
+
+  /* check basic coin properties */
+  if (0 != TALER_amount_cmp (&original_value,
+                             &mc->original_value))
+  {
+    /* We disagree on the value of the coin */
+    GNUNET_break_op (0);
+    json_decref (history);
+    return GNUNET_SYSERR;
+  }
+  if (0 != TALER_amount_cmp (&melt_value_with_fee,
+                             &mc->melt_amount_with_fee))
+  {
+    /* We disagree on the value of the coin */
+    GNUNET_break_op (0);
+    json_decref (history);
+    return GNUNET_SYSERR;
+  }
+
+  /* verify coin history */
+  history = json_object_get (json,
+                             "history");
+  if (GNUNET_OK !=
+      TALER_EXCHANGE_verify_coin_history (&mh->dki,
+                                          original_value.currency,
+                                          &coin_pub,
+                                          history,
+                                          &total))
+  {
+    GNUNET_break_op (0);
+    json_decref (history);
+    return GNUNET_SYSERR;
+  }
+  json_decref (history);
+
+  /* check if melt operation was really too expensive given history */
+  if (GNUNET_OK !=
+      TALER_amount_add (&total,
+                        &total,
+                        &melt_value_with_fee))
+  {
+    /* clearly not OK if our transaction would have caused
+       the overflow... */
+    return GNUNET_OK;
+  }
+
+  if (0 >= TALER_amount_cmp (&total,
+                             &original_value))
+  {
+    /* transaction should have still fit */
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+
+  /* everything OK, valid proof of double-spending was provided */
+  return GNUNET_OK;
+}
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP /coins/$COIN_PUB/melt request.
+ *
+ * @param cls the `struct TALER_EXCHANGE_MeltHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response parsed JSON result, NULL on error
+ */
+static void
+handle_melt_finished (void *cls,
+                      long response_code,
+                      const void *response)
+{
+  struct TALER_EXCHANGE_MeltHandle *mh = cls;
+  uint32_t noreveal_index = TALER_CNC_KAPPA; /* invalid value */
+  struct TALER_ExchangePublicKeyP exchange_pub;
+  const json_t *j = response;
+
+  mh->job = NULL;
+  switch (response_code)
+  {
+  case 0:
+    break;
+  case MHD_HTTP_OK:
+    if (GNUNET_OK !=
+        verify_melt_signature_ok (mh,
+                                  j,
+                                  &exchange_pub,
+                                  &noreveal_index))
+    {
+      GNUNET_break_op (0);
+      response_code = 0;
+    }
+    if (NULL != mh->melt_cb)
+    {
+      mh->melt_cb (mh->melt_cb_cls,
+                   response_code,
+                   TALER_JSON_get_error_code (j),
+                   noreveal_index,
+                   (0 == response_code) ? NULL : &exchange_pub,
+                   j);
+      mh->melt_cb = NULL;
+    }
+    break;
+  case MHD_HTTP_BAD_REQUEST:
+    /* This should never happen, either us or the exchange is buggy
+       (or API version conflict); just pass JSON reply to the application */
+    break;
+  case MHD_HTTP_CONFLICT:
+    /* Double spending; check signatures on transaction history */
+    if (GNUNET_OK !=
+        verify_melt_signature_conflict (mh,
+                                        j))
+    {
+      GNUNET_break_op (0);
+      response_code = 0;
+    }
+    break;
+  case MHD_HTTP_FORBIDDEN:
+    /* Nothing really to verify, exchange says one of the signatures is
+       invalid; assuming we checked them, this should never happen, we
+       should pass the JSON reply to the application */
+    break;
+  case MHD_HTTP_NOT_FOUND:
+    /* Nothing really to verify, this should never
+       happen, we should pass the JSON reply to the application */
+    break;
+  case MHD_HTTP_INTERNAL_SERVER_ERROR:
+    /* Server had an internal issue; we should retry, but this API
+       leaves this to the application */
+    break;
+  default:
+    /* unexpected response code */
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Unexpected response code %u\n",
+                (unsigned int) response_code);
+    GNUNET_break (0);
+    response_code = 0;
+    break;
+  }
+  if (NULL != mh->melt_cb)
+    mh->melt_cb (mh->melt_cb_cls,
+                 response_code,
+                 TALER_JSON_get_error_code (j),
+                 UINT32_MAX,
+                 NULL,
+                 j);
+  TALER_EXCHANGE_melt_cancel (mh);
+}
+
+
+/**
+ * Submit a melt request to the exchange and get the exchange's
+ * response.
+ *
+ * This API is typically used by a wallet.  Note that to ensure that
+ * no money is lost in case of hardware failures, the provided
+ * argument should have been constructed using
+ * #TALER_EXCHANGE_refresh_prepare and committed to persistent storage
+ * prior to calling this function.
+ *
+ * @param exchange the exchange handle; the exchange must be ready to operate
+ * @param refresh_data_length size of the @a refresh_data (returned
+ *        in the `res_size` argument from #TALER_EXCHANGE_refresh_prepare())
+ * @param refresh_data the refresh data as returned from
+          #TALER_EXCHANGE_refresh_prepare())
+ * @param melt_cb the callback to call with the result
+ * @param melt_cb_cls closure for @a melt_cb
+ * @return a handle for this request; NULL if the argument was invalid.
+ *         In this case, neither callback will be called.
+ */
+struct TALER_EXCHANGE_MeltHandle *
+TALER_EXCHANGE_melt (struct TALER_EXCHANGE_Handle *exchange,
+                     size_t refresh_data_length,
+                     const char *refresh_data,
+                     TALER_EXCHANGE_MeltCallback melt_cb,
+                     void *melt_cb_cls)
+{
+  const struct TALER_EXCHANGE_Keys *key_state;
+  const struct TALER_EXCHANGE_DenomPublicKey *dki;
+  json_t *melt_obj;
+  struct TALER_EXCHANGE_MeltHandle *mh;
+  CURL *eh;
+  struct GNUNET_CURL_Context *ctx;
+  struct MeltData *md;
+  struct TALER_CoinSpendSignatureP confirm_sig;
+  struct TALER_RefreshMeltCoinAffirmationPS melt;
+  struct GNUNET_HashCode h_denom_pub;
+  char arg_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2 + 32];
+
+  GNUNET_assert (GNUNET_YES ==
+                 TEAH_handle_is_ready (exchange));
+  md = TALER_EXCHANGE_deserialize_melt_data_ (refresh_data,
+                                              refresh_data_length);
+  if (NULL == md)
+  {
+    GNUNET_break (0);
+    return NULL;
+  }
+  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);
+  GNUNET_CRYPTO_rsa_public_key_hash (md->melted_coin.pub_key.rsa_public_key,
+                                     &h_denom_pub);
+  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_hash",
+                        GNUNET_JSON_from_data_auto (&h_denom_pub),
+                        "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);
+    TALER_EXCHANGE_free_melt_data_ (md);
+    return NULL;
+  }
+  {
+    char pub_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2];
+    char *end;
+
+    end = GNUNET_STRINGS_data_to_string (&melt.coin_pub,
+                                         sizeof (struct
+                                                 TALER_CoinSpendPublicKeyP),
+                                         pub_str,
+                                         sizeof (pub_str));
+    *end = '\0';
+    GNUNET_snprintf (arg_str,
+                     sizeof (arg_str),
+                     "/coins/%s/melt",
+                     pub_str);
+  }
+
+  key_state = TALER_EXCHANGE_get_keys (exchange);
+  dki = TALER_EXCHANGE_get_denomination_key (key_state,
+                                             &md->melted_coin.pub_key);
+
+  /* and now we can at last begin the actual request handling */
+  mh = GNUNET_new (struct TALER_EXCHANGE_MeltHandle);
+  mh->exchange = exchange;
+  mh->dki = *dki;
+  mh->dki.key.rsa_public_key = NULL; /* lifetime not warranted, so better
+                                         not copy the pointer */
+  mh->melt_cb = melt_cb;
+  mh->melt_cb_cls = melt_cb_cls;
+  mh->md = md;
+  mh->url = TEAH_path_to_url (exchange,
+                              arg_str);
+  eh = TEL_curl_easy_get (mh->url);
+  if (GNUNET_OK !=
+      TALER_curl_easy_post (&mh->ctx,
+                            eh,
+                            melt_obj))
+  {
+    GNUNET_break (0);
+    curl_easy_cleanup (eh);
+    json_decref (melt_obj);
+    GNUNET_free (mh->url);
+    GNUNET_free (mh);
+    return NULL;
+  }
+  json_decref (melt_obj);
+  ctx = TEAH_handle_to_context (exchange);
+  mh->job = GNUNET_CURL_job_add2 (ctx,
+                                  eh,
+                                  mh->ctx.headers,
+                                  &handle_melt_finished,
+                                  mh);
+  return mh;
+}
+
+
+/**
+ * Cancel a melt request.  This function cannot be used
+ * on a request handle if either callback was already invoked.
+ *
+ * @param mh the refresh melt handle
+ */
+void
+TALER_EXCHANGE_melt_cancel (struct TALER_EXCHANGE_MeltHandle *mh)
+{
+  if (NULL != mh->job)
+  {
+    GNUNET_CURL_job_cancel (mh->job);
+    mh->job = NULL;
+  }
+  TALER_EXCHANGE_free_melt_data_ (mh->md); /* does not free 'md' itself */
+  GNUNET_free (mh->md);
+  GNUNET_free (mh->url);
+  TALER_curl_easy_post_finished (&mh->ctx);
+  GNUNET_free (mh);
+}
+
+
+/* end of exchange_api_melt.c */
diff --git a/src/lib/exchange_api_refresh.c b/src/lib/exchange_api_refresh.c
deleted file mode 100644
index eb26b7c1..00000000
--- a/src/lib/exchange_api_refresh.c
+++ /dev/null
@@ -1,1770 +0,0 @@
-/*
-  This file is part of TALER
-  Copyright (C) 2015-2020 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 lib/exchange_api_refresh.c
- * @brief Implementation of the /refresh/melt+reveal requests of the 
exchange's HTTP API
- * @author Christian Grothoff
- */
-#include "platform.h"
-#include <jansson.h>
-#include <microhttpd.h> /* just for HTTP status codes */
-#include <gnunet/gnunet_util_lib.h>
-#include <gnunet/gnunet_json_lib.h>
-#include <gnunet/gnunet_curl_lib.h>
-#include "taler_json_lib.h"
-#include "taler_exchange_service.h"
-#include "exchange_api_handle.h"
-#include "taler_signatures.h"
-#include "exchange_api_curl_defaults.h"
-
-
-/* ********************* /refresh/ common ***************************** */
-
-/* structures for committing refresh data to disk before doing the
-   network interaction(s) */
-
-GNUNET_NETWORK_STRUCT_BEGIN
-
-/**
- * Header of serialized information about a coin we are melting.
- */
-struct MeltedCoinP
-{
-  /**
-   * Private key of the coin.
-   */
-  struct TALER_CoinSpendPrivateKeyP coin_priv;
-
-  /**
-   * Amount this coin contributes to the melt, including fee.
-   */
-  struct TALER_AmountNBO melt_amount_with_fee;
-
-  /**
-   * The applicable fee for withdrawing a coin of this denomination
-   */
-  struct TALER_AmountNBO fee_melt;
-
-  /**
-   * The original value of the coin.
-   */
-  struct TALER_AmountNBO original_value;
-
-  /**
-   * Transfer private keys for each cut-and-choose dimension.
-   */
-  struct TALER_TransferPrivateKeyP transfer_priv[TALER_CNC_KAPPA];
-
-  /**
-   * Timestamp indicating when coins of this denomination become invalid.
-   */
-  struct GNUNET_TIME_AbsoluteNBO expire_deposit;
-
-  /**
-   * Size of the encoded public key that follows.
-   */
-  uint16_t pbuf_size;
-
-  /**
-   * Size of the encoded signature that follows.
-   */
-  uint16_t sbuf_size;
-
-  /* Followed by serializations of:
-     1) struct TALER_DenominationPublicKey pub_key;
-     2) struct TALER_DenominationSignature sig;
-  */
-};
-
-
-/**
- * Header of serialized data about a melt operation, suitable for
- * persisting it on disk.
- */
-struct MeltDataP
-{
-
-  /**
-   * Hash over the melting session.
-   */
-  struct TALER_RefreshCommitmentP rc;
-
-  /**
-   * Number of coins we are melting, in NBO
-   */
-  uint16_t num_melted_coins GNUNET_PACKED;
-
-  /**
-   * Number of coins we are creating, in NBO
-   */
-  uint16_t num_fresh_coins GNUNET_PACKED;
-
-  /* Followed by serializations of:
-     1) struct MeltedCoinP melted_coins[num_melted_coins];
-     2) struct TALER_EXCHANGE_DenomPublicKey fresh_pks[num_fresh_coins];
-     3) TALER_CNC_KAPPA times:
-        3a) struct TALER_PlanchetSecretsP fresh_coins[num_fresh_coins];
-  */
-};
-
-
-GNUNET_NETWORK_STRUCT_END
-
-
-/**
- * Information about a coin we are melting.
- */
-struct MeltedCoin
-{
-  /**
-   * Private key of the coin.
-   */
-  struct TALER_CoinSpendPrivateKeyP coin_priv;
-
-  /**
-   * Amount this coin contributes to the melt, including fee.
-   */
-  struct TALER_Amount melt_amount_with_fee;
-
-  /**
-   * The applicable fee for melting a coin of this denomination
-   */
-  struct TALER_Amount fee_melt;
-
-  /**
-   * The original value of the coin.
-   */
-  struct TALER_Amount original_value;
-
-  /**
-   * Transfer private keys for each cut-and-choose dimension.
-   */
-  struct TALER_TransferPrivateKeyP transfer_priv[TALER_CNC_KAPPA];
-
-  /**
-   * Timestamp indicating when coins of this denomination become invalid.
-   */
-  struct GNUNET_TIME_Absolute expire_deposit;
-
-  /**
-   * Denomination key of the original coin.
-   */
-  struct TALER_DenominationPublicKey pub_key;
-
-  /**
-   * Exchange's signature over the coin.
-   */
-  struct TALER_DenominationSignature sig;
-
-};
-
-
-/**
- * Melt data in non-serialized format for convenient processing.
- */
-struct MeltData
-{
-
-  /**
-   * Hash over the committed data during refresh operation.
-   */
-  struct TALER_RefreshCommitmentP rc;
-
-  /**
-   * Number of coins we are creating
-   */
-  uint16_t num_fresh_coins;
-
-  /**
-   * Information about the melted coin.
-   */
-  struct MeltedCoin melted_coin;
-
-  /**
-   * Array of @e num_fresh_coins denomination keys for the coins to be
-   * freshly exchangeed.
-   */
-  struct TALER_DenominationPublicKey *fresh_pks;
-
-  /**
-   * Arrays of @e num_fresh_coins with information about the fresh
-   * coins to be created, for each cut-and-choose dimension.
-   */
-  struct TALER_PlanchetSecretsP *fresh_coins[TALER_CNC_KAPPA];
-};
-
-
-/**
- * Free all information associated with a melted coin session.
- *
- * @param mc melted coin to release, the pointer itself is NOT
- *           freed (as it is typically not allocated by itself)
- */
-static void
-free_melted_coin (struct MeltedCoin *mc)
-{
-  if (NULL != mc->pub_key.rsa_public_key)
-    GNUNET_CRYPTO_rsa_public_key_free (mc->pub_key.rsa_public_key);
-  if (NULL != mc->sig.rsa_signature)
-    GNUNET_CRYPTO_rsa_signature_free (mc->sig.rsa_signature);
-}
-
-
-/**
- * Free all information associated with a melting session.  Note
- * that we allow the melting session to be only partially initialized,
- * as we use this function also when freeing melt data that was not
- * fully initialized (i.e. due to failures in #deserialize_melt_data()).
- *
- * @param md melting data to release, the pointer itself is NOT
- *           freed (as it is typically not allocated by itself)
- */
-static void
-free_melt_data (struct MeltData *md)
-{
-  free_melted_coin (&md->melted_coin);
-  if (NULL != md->fresh_pks)
-  {
-    for (unsigned int i = 0; i<md->num_fresh_coins; i++)
-      if (NULL != md->fresh_pks[i].rsa_public_key)
-        GNUNET_CRYPTO_rsa_public_key_free (md->fresh_pks[i].rsa_public_key);
-    GNUNET_free (md->fresh_pks);
-  }
-
-  for (unsigned int i = 0; i<TALER_CNC_KAPPA; i++)
-    GNUNET_free_non_null (md->fresh_coins[i]);
-  /* Finally, clean up a bit...
-     (NOTE: compilers might optimize this away, so this is
-     not providing any strong assurances that the key material
-     is purged.) */
-  memset (md,
-          0,
-          sizeof (struct MeltData));
-}
-
-
-/**
- * Serialize information about a coin we are melting.
- *
- * @param mc information to serialize
- * @param buf buffer to write data in, NULL to just compute
- *            required size
- * @param off offeset at @a buf to use
- * @return number of bytes written to @a buf at @a off, or if
- *        @a buf is NULL, number of bytes required; 0 on error
- */
-static size_t
-serialize_melted_coin (const struct MeltedCoin *mc,
-                       char *buf,
-                       size_t off)
-{
-  struct MeltedCoinP mcp;
-  unsigned int i;
-  char *pbuf;
-  size_t pbuf_size;
-  char *sbuf;
-  size_t sbuf_size;
-
-  sbuf_size = GNUNET_CRYPTO_rsa_signature_encode (mc->sig.rsa_signature,
-                                                  &sbuf);
-  pbuf_size = GNUNET_CRYPTO_rsa_public_key_encode (mc->pub_key.rsa_public_key,
-                                                   &pbuf);
-  if (NULL == buf)
-  {
-    GNUNET_free (sbuf);
-    GNUNET_free (pbuf);
-    return sizeof (struct MeltedCoinP) + sbuf_size + pbuf_size;
-  }
-  if ( (sbuf_size > UINT16_MAX) ||
-       (pbuf_size > UINT16_MAX) )
-  {
-    GNUNET_break (0);
-    return 0;
-  }
-  mcp.coin_priv = mc->coin_priv;
-  TALER_amount_hton (&mcp.melt_amount_with_fee,
-                     &mc->melt_amount_with_fee);
-  TALER_amount_hton (&mcp.fee_melt,
-                     &mc->fee_melt);
-  TALER_amount_hton (&mcp.original_value,
-                     &mc->original_value);
-  for (i = 0; i<TALER_CNC_KAPPA; i++)
-    mcp.transfer_priv[i] = mc->transfer_priv[i];
-  mcp.expire_deposit = GNUNET_TIME_absolute_hton (mc->expire_deposit);
-  mcp.pbuf_size = htons ((uint16_t) pbuf_size);
-  mcp.sbuf_size = htons ((uint16_t) sbuf_size);
-  memcpy (&buf[off],
-          &mcp,
-          sizeof (struct MeltedCoinP));
-  memcpy (&buf[off + sizeof (struct MeltedCoinP)],
-          pbuf,
-          pbuf_size);
-  memcpy (&buf[off + sizeof (struct MeltedCoinP) + pbuf_size],
-          sbuf,
-          sbuf_size);
-  GNUNET_free (sbuf);
-  GNUNET_free (pbuf);
-  return sizeof (struct MeltedCoinP) + sbuf_size + pbuf_size;
-}
-
-
-/**
- * Deserialize information about a coin we are melting.
- *
- * @param[out] mc information to deserialize
- * @param buf buffer to read data from
- * @param size number of bytes available at @a buf to use
- * @param[out] ok set to #GNUNET_NO to report errors
- * @return number of bytes read from @a buf, 0 on error
- */
-static size_t
-deserialize_melted_coin (struct MeltedCoin *mc,
-                         const char *buf,
-                         size_t size,
-                         int *ok)
-{
-  struct MeltedCoinP mcp;
-  unsigned int i;
-  size_t pbuf_size;
-  size_t sbuf_size;
-  size_t off;
-
-  if (size < sizeof (struct MeltedCoinP))
-  {
-    GNUNET_break (0);
-    *ok = GNUNET_NO;
-    return 0;
-  }
-  memcpy (&mcp,
-          buf,
-          sizeof (struct MeltedCoinP));
-  pbuf_size = ntohs (mcp.pbuf_size);
-  sbuf_size = ntohs (mcp.sbuf_size);
-  if (size < sizeof (struct MeltedCoinP) + pbuf_size + sbuf_size)
-  {
-    GNUNET_break (0);
-    *ok = GNUNET_NO;
-    return 0;
-  }
-  off = sizeof (struct MeltedCoinP);
-  mc->pub_key.rsa_public_key
-    = GNUNET_CRYPTO_rsa_public_key_decode (&buf[off],
-                                           pbuf_size);
-  off += pbuf_size;
-  mc->sig.rsa_signature
-    = GNUNET_CRYPTO_rsa_signature_decode (&buf[off],
-                                          sbuf_size);
-  off += sbuf_size;
-  if ( (NULL == mc->pub_key.rsa_public_key) ||
-       (NULL == mc->sig.rsa_signature) )
-  {
-    GNUNET_break (0);
-    *ok = GNUNET_NO;
-    return 0;
-  }
-
-  mc->coin_priv = mcp.coin_priv;
-  TALER_amount_ntoh (&mc->melt_amount_with_fee,
-                     &mcp.melt_amount_with_fee);
-  TALER_amount_ntoh (&mc->fee_melt,
-                     &mcp.fee_melt);
-  TALER_amount_ntoh (&mc->original_value,
-                     &mcp.original_value);
-  for (i = 0; i<TALER_CNC_KAPPA; i++)
-    mc->transfer_priv[i] = mcp.transfer_priv[i];
-  mc->expire_deposit = GNUNET_TIME_absolute_ntoh (mcp.expire_deposit);
-  return off;
-}
-
-
-/**
- * Serialize information about a denomination key.
- *
- * @param dk information to serialize
- * @param buf buffer to write data in, NULL to just compute
- *            required size
- * @param off offeset at @a buf to use
- * @return number of bytes written to @a buf at @a off, or if
- *        @a buf is NULL, number of bytes required
- */
-static size_t
-serialize_denomination_key (const struct TALER_DenominationPublicKey *dk,
-                            char *buf,
-                            size_t off)
-{
-  char *pbuf;
-  size_t pbuf_size;
-  uint32_t be;
-
-  pbuf_size = GNUNET_CRYPTO_rsa_public_key_encode (dk->rsa_public_key,
-                                                   &pbuf);
-  if (NULL == buf)
-  {
-    GNUNET_free (pbuf);
-    return pbuf_size + sizeof (uint32_t);
-  }
-  be = htonl ((uint32_t) pbuf_size);
-  memcpy (&buf[off],
-          &be,
-          sizeof (uint32_t));
-  memcpy (&buf[off + sizeof (uint32_t)],
-          pbuf,
-          pbuf_size);
-  GNUNET_free (pbuf);
-  return pbuf_size + sizeof (uint32_t);
-}
-
-
-/**
- * Deserialize information about a denomination key.
- *
- * @param[out] dk information to deserialize
- * @param buf buffer to read data from
- * @param size number of bytes available at @a buf to use
- * @param[out] ok set to #GNUNET_NO to report errors
- * @return number of bytes read from @a buf, 0 on error
- */
-static size_t
-deserialize_denomination_key (struct TALER_DenominationPublicKey *dk,
-                              const char *buf,
-                              size_t size,
-                              int *ok)
-{
-  size_t pbuf_size;
-  uint32_t be;
-
-  if (size < sizeof (uint32_t))
-  {
-    GNUNET_break (0);
-    *ok = GNUNET_NO;
-    return 0;
-  }
-  memcpy (&be,
-          buf,
-          sizeof (uint32_t));
-  pbuf_size = ntohl (be);
-  if (size < sizeof (uint32_t) + pbuf_size)
-  {
-    GNUNET_break (0);
-    *ok = GNUNET_NO;
-    return 0;
-  }
-  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);
-    *ok = GNUNET_NO;
-    return 0;
-  }
-  return sizeof (uint32_t) + pbuf_size;
-}
-
-
-/**
- * Serialize information about a fresh coin we are generating.
- *
- * @param fc information to serialize
- * @param buf buffer to write data in, NULL to just compute
- *            required size
- * @param off offeset at @a buf to use
- * @return number of bytes written to @a buf at @a off, or if
- *        @a buf is NULL, number of bytes required
- */
-static size_t
-serialize_fresh_coin (const struct TALER_PlanchetSecretsP *fc,
-                      char *buf,
-                      size_t off)
-{
-  if (NULL != buf)
-    memcpy (&buf[off],
-            fc,
-            sizeof (struct TALER_PlanchetSecretsP));
-  return sizeof (struct TALER_PlanchetSecretsP);
-}
-
-
-/**
- * Deserialize information about a fresh coin we are generating.
- *
- * @param[out] fc information to deserialize
- * @param buf buffer to read data from
- * @param size number of bytes available at @a buf to use
- * @param[out] ok set to #GNUNET_NO to report errors
- * @return number of bytes read from @a buf, 0 on error
- */
-static size_t
-deserialize_fresh_coin (struct TALER_PlanchetSecretsP *fc,
-                        const char *buf,
-                        size_t size,
-                        int *ok)
-{
-  if (size < sizeof (struct TALER_PlanchetSecretsP))
-  {
-    GNUNET_break (0);
-    *ok = GNUNET_NO;
-    return 0;
-  }
-  memcpy (fc,
-          buf,
-          sizeof (struct TALER_PlanchetSecretsP));
-  return sizeof (struct TALER_PlanchetSecretsP);
-}
-
-
-/**
- * Serialize melt data.
- *
- * @param md data to serialize
- * @param[out] res_size size of buffer returned
- * @return serialized melt data
- */
-static char *
-serialize_melt_data (const struct MeltData *md,
-                     size_t *res_size)
-{
-  size_t size;
-  size_t asize;
-  char *buf;
-
-  size = 0;
-  asize = (size_t) -1; /* make the compiler happy */
-  buf = NULL;
-  /* we do 2 iterations, #1 to determine total size, #2 to
-     actually construct the buffer */
-  do {
-    if (0 == size)
-    {
-      size = sizeof (struct MeltDataP);
-    }
-    else
-    {
-      struct MeltDataP *mdp;
-
-      buf = GNUNET_malloc (size);
-      asize = size; /* just for invariant check later */
-      size = sizeof (struct MeltDataP);
-      mdp = (struct MeltDataP *) buf;
-      mdp->rc = md->rc;
-      mdp->num_fresh_coins = htons (md->num_fresh_coins);
-    }
-    size += serialize_melted_coin (&md->melted_coin,
-                                   buf,
-                                   size);
-    for (unsigned int i = 0; i<md->num_fresh_coins; i++)
-      size += serialize_denomination_key (&md->fresh_pks[i],
-                                          buf,
-                                          size);
-    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);
-  } while (NULL == buf);
-  GNUNET_assert (size == asize);
-  *res_size = size;
-  return buf;
-}
-
-
-/**
- * Deserialize melt data.
- *
- * @param buf serialized data
- * @param buf_size size of @a buf
- * @return deserialized melt data, NULL on error
- */
-static struct MeltData *
-deserialize_melt_data (const char *buf,
-                       size_t buf_size)
-{
-  struct MeltData *md;
-  struct MeltDataP mdp;
-  size_t off;
-  int ok;
-
-  if (buf_size < sizeof (struct MeltDataP))
-    return NULL;
-  memcpy (&mdp,
-          buf,
-          sizeof (struct MeltDataP));
-  md = GNUNET_new (struct MeltData);
-  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);
-  for (unsigned int i = 0; i<TALER_CNC_KAPPA; i++)
-    md->fresh_coins[i] = GNUNET_new_array (md->num_fresh_coins,
-                                           struct TALER_PlanchetSecretsP);
-  off = sizeof (struct MeltDataP);
-  ok = GNUNET_YES;
-  off += deserialize_melted_coin (&md->melted_coin,
-                                  &buf[off],
-                                  buf_size - off,
-                                  &ok);
-  for (unsigned int i = 0; (i<md->num_fresh_coins) && (GNUNET_YES == ok); i++)
-    off += deserialize_denomination_key (&md->fresh_pks[i],
-                                         &buf[off],
-                                         buf_size - off,
-                                         &ok);
-
-  for (unsigned int i = 0; i<TALER_CNC_KAPPA; i++)
-    for (unsigned int j = 0; (j<md->num_fresh_coins) && (GNUNET_YES == ok); 
j++)
-      off += deserialize_fresh_coin (&md->fresh_coins[i][j],
-                                     &buf[off],
-                                     buf_size - off,
-                                     &ok);
-  if (off != buf_size)
-  {
-    GNUNET_break (0);
-    ok = GNUNET_NO;
-  }
-  if (GNUNET_YES != ok)
-  {
-    free_melt_data (md);
-    GNUNET_free (md);
-    return NULL;
-  }
-  return md;
-}
-
-
-/**
- * Melt (partially spent) coins to obtain fresh coins that are
- * unlinkable to the original coin(s).  Note that melting more
- * than one coin in a single request will make those coins linkable,
- * so the safest operation only melts one coin at a time.
- *
- * This API is typically used by a wallet.  Note that to ensure that
- * no money is lost in case of hardware failures, this operation does
- * not actually initiate the request. Instead, it generates a buffer
- * which the caller must store before proceeding with the actual call
- * to #TALER_EXCHANGE_refresh_melt() that will generate the request.
- *
- * This function does verify that the given request data is internally
- * consistent.  However, the @a melts_sigs are NOT verified.
- *
- * Aside from some non-trivial cryptographic operations that might
- * take a bit of CPU time to complete, this function returns
- * its result immediately and does not start any asynchronous
- * processing.  This function is also thread-safe.
- *
- * @param melt_priv private key of the coin to melt
- * @param melt_amount amount specifying how much
- *                     the coin will contribute to the melt (including fee)
- * @param melt_sig signature affirming the
- *                   validity of the public keys corresponding to the
- *                   @a melt_priv private key
- * @param melt_pk denomination key information
- *                   record corresponding to the @a melt_sig
- *                   validity of the keys
- * @param fresh_pks_len length of the @a pks array
- * @param fresh_pks array of @a pks_len denominations of fresh coins to create
- * @param[out] res_size set to the size of the return value, or 0 on error
- * @return NULL
- *         if the inputs are invalid (i.e. denomination key not with this 
exchange).
- *         Otherwise, pointer to a buffer of @a res_size to store persistently
- *         before proceeding to #TALER_EXCHANGE_refresh_melt().
- *         Non-null results should be freed using GNUNET_free().
- */
-char *
-TALER_EXCHANGE_refresh_prepare (const struct
-                                TALER_CoinSpendPrivateKeyP *melt_priv,
-                                const struct TALER_Amount *melt_amount,
-                                const struct
-                                TALER_DenominationSignature *melt_sig,
-                                const struct
-                                TALER_EXCHANGE_DenomPublicKey *melt_pk,
-                                unsigned int fresh_pks_len,
-                                const struct
-                                TALER_EXCHANGE_DenomPublicKey *fresh_pks,
-                                size_t *res_size)
-{
-  struct MeltData md;
-  char *buf;
-  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);
-  /* build up melt data structure */
-  memset (&md, 0, sizeof (md));
-  md.num_fresh_coins = fresh_pks_len;
-  md.melted_coin.coin_priv = *melt_priv;
-  md.melted_coin.melt_amount_with_fee = *melt_amount;
-  md.melted_coin.fee_melt = melt_pk->fee_refresh;
-  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
-    = GNUNET_CRYPTO_rsa_signature_dup (melt_sig->rsa_signature);
-  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);
-    if ( (GNUNET_OK !=
-          TALER_amount_add (&total,
-                            &total,
-                            &fresh_pks[i].value)) ||
-         (GNUNET_OK !=
-          TALER_amount_add (&total,
-                            &total,
-                            &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) )
-  {
-    /* Eh, this operation is more expensive than the
-       @a melt_amount. This is not OK. */
-    GNUNET_break (0);
-    free_melt_data (&md);
-    return NULL;
-  }
-
-  /* build up coins */
-  for (unsigned int i = 0; i<TALER_CNC_KAPPA; i++)
-  {
-    struct GNUNET_CRYPTO_EcdhePrivateKey *tpk;
-
-    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,
-      &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++)
-    {
-      struct TALER_PlanchetSecretsP *fc = &md.fresh_coins[i][j];
-      struct TALER_RefreshCoinData *rcd = &rce[i].new_coins[j];
-      struct TALER_PlanchetDetail pd;
-
-      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);
-        free_melt_data (&md);
-        return NULL;
-      }
-      rcd->dk = &md.fresh_pks[j];
-      rcd->coin_ev = pd.coin_ev;
-      rcd->coin_ev_size = pd.coin_ev_size;
-    }
-  }
-
-  /* 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;
-}
-
-
-/* ********************* /refresh/melt ***************************** */
-
-
-/**
- * @brief A /refresh/melt Handle
- */
-struct TALER_EXCHANGE_RefreshMeltHandle
-{
-
-  /**
-   * The connection to exchange this request handle will use
-   */
-  struct TALER_EXCHANGE_Handle *exchange;
-
-  /**
-   * The url for this request.
-   */
-  char *url;
-
-  /**
-   * Context for #TEH_curl_easy_post(). Keeps the data that must
-   * persist for Curl to make the upload.
-   */
-  struct TALER_CURL_PostContext ctx;
-
-  /**
-   * Handle for the request.
-   */
-  struct GNUNET_CURL_Job *job;
-
-  /**
-   * Function to call with refresh melt failure results.
-   */
-  TALER_EXCHANGE_RefreshMeltCallback melt_cb;
-
-  /**
-   * Closure for @e result_cb and @e melt_failure_cb.
-   */
-  void *melt_cb_cls;
-
-  /**
-   * Actual information about the melt operation.
-   */
-  struct MeltData *md;
-
-  /**
-   * @brief Public information about the coin's denomination key
-   */
-  struct TALER_EXCHANGE_DenomPublicKey dki;
-};
-
-
-/**
- * Verify that the signature on the "200 OK" response
- * from the exchange is valid.
- *
- * @param rmh melt handle
- * @param json json reply with the signature
- * @param[out] exchange_pub public key of the exchange used for the signature
- * @param[out] noreveal_index set to the noreveal index selected by the 
exchange
- * @return #GNUNET_OK if the signature is valid, #GNUNET_SYSERR if not
- */
-static int
-verify_refresh_melt_signature_ok (struct TALER_EXCHANGE_RefreshMeltHandle *rmh,
-                                  const json_t *json,
-                                  struct TALER_ExchangePublicKeyP 
*exchange_pub,
-                                  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_uint32 ("noreveal_index", noreveal_index),
-    GNUNET_JSON_spec_end ()
-  };
-  struct TALER_RefreshMeltConfirmationPS confirm;
-
-  if (GNUNET_OK !=
-      GNUNET_JSON_parse (json,
-                         spec,
-                         NULL, NULL))
-  {
-    GNUNET_break_op (0);
-    return GNUNET_SYSERR;
-  }
-
-  /* check that exchange signing key is permitted */
-  key_state = TALER_EXCHANGE_get_keys (rmh->exchange);
-  if (GNUNET_OK !=
-      TALER_EXCHANGE_test_signing_key (key_state,
-                                       exchange_pub))
-  {
-    GNUNET_break_op (0);
-    return GNUNET_SYSERR;
-  }
-
-  /* check that noreveal index is in permitted range */
-  if (TALER_CNC_KAPPA <= *noreveal_index)
-  {
-    GNUNET_break_op (0);
-    return GNUNET_SYSERR;
-  }
-
-  /* verify signature by exchange */
-  confirm.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_MELT);
-  confirm.purpose.size = htonl (sizeof (struct
-                                        TALER_RefreshMeltConfirmationPS));
-  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,
-                                  &exchange_sig.eddsa_signature,
-                                  &exchange_pub->eddsa_pub))
-  {
-    GNUNET_break_op (0);
-    return GNUNET_SYSERR;
-  }
-  return GNUNET_OK;
-}
-
-
-/**
- * Verify that the signatures on the "409 CONFLICT" response from the
- * exchange demonstrating customer double-spending are valid.
- *
- * @param rmh melt handle
- * @param json json reply with the signature(s) and transaction history
- * @return #GNUNET_OK if the signature(s) is valid, #GNUNET_SYSERR if not
- */
-static int
-verify_refresh_melt_signature_conflict (struct
-                                        TALER_EXCHANGE_RefreshMeltHandle *rmh,
-                                        const json_t *json)
-{
-  json_t *history;
-  struct TALER_Amount original_value;
-  struct TALER_Amount melt_value_with_fee;
-  struct TALER_Amount total;
-  struct TALER_CoinSpendPublicKeyP coin_pub;
-  struct GNUNET_JSON_Specification spec[] = {
-    GNUNET_JSON_spec_json ("history", &history),
-    GNUNET_JSON_spec_fixed_auto ("coin_pub", &coin_pub),
-    TALER_JSON_spec_amount ("original_value", &original_value),
-    TALER_JSON_spec_amount ("requested_value", &melt_value_with_fee),
-    GNUNET_JSON_spec_end ()
-  };
-  const struct MeltedCoin *mc;
-
-  /* parse JSON reply */
-  if (GNUNET_OK !=
-      GNUNET_JSON_parse (json,
-                         spec,
-                         NULL, NULL))
-  {
-    GNUNET_break_op (0);
-    return GNUNET_SYSERR;
-  }
-
-  /* Find out which coin was deemed problematic by the exchange */
-  mc = &rmh->md->melted_coin;
-
-  /* check basic coin properties */
-  if (0 != TALER_amount_cmp (&original_value,
-                             &mc->original_value))
-  {
-    /* We disagree on the value of the coin */
-    GNUNET_break_op (0);
-    json_decref (history);
-    return GNUNET_SYSERR;
-  }
-  if (0 != TALER_amount_cmp (&melt_value_with_fee,
-                             &mc->melt_amount_with_fee))
-  {
-    /* We disagree on the value of the coin */
-    GNUNET_break_op (0);
-    json_decref (history);
-    return GNUNET_SYSERR;
-  }
-
-  /* verify coin history */
-  history = json_object_get (json,
-                             "history");
-  if (GNUNET_OK !=
-      TALER_EXCHANGE_verify_coin_history (&rmh->dki,
-                                          original_value.currency,
-                                          &coin_pub,
-                                          history,
-                                          &total))
-  {
-    GNUNET_break_op (0);
-    json_decref (history);
-    return GNUNET_SYSERR;
-  }
-  json_decref (history);
-
-  /* check if melt operation was really too expensive given history */
-  if (GNUNET_OK !=
-      TALER_amount_add (&total,
-                        &total,
-                        &melt_value_with_fee))
-  {
-    /* clearly not OK if our transaction would have caused
-       the overflow... */
-    return GNUNET_OK;
-  }
-
-  if (0 >= TALER_amount_cmp (&total,
-                             &original_value))
-  {
-    /* transaction should have still fit */
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
-  }
-
-  /* everything OK, valid proof of double-spending was provided */
-  return GNUNET_OK;
-}
-
-
-/**
- * Function called when we're done processing the
- * HTTP /refresh/melt request.
- *
- * @param cls the `struct TALER_EXCHANGE_RefreshMeltHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response parsed JSON result, NULL on error
- */
-static void
-handle_refresh_melt_finished (void *cls,
-                              long response_code,
-                              const void *response)
-{
-  struct TALER_EXCHANGE_RefreshMeltHandle *rmh = cls;
-  uint32_t noreveal_index = TALER_CNC_KAPPA; /* invalid value */
-  struct TALER_ExchangePublicKeyP exchange_pub;
-  const json_t *j = response;
-
-  rmh->job = NULL;
-  switch (response_code)
-  {
-  case 0:
-    break;
-  case MHD_HTTP_OK:
-    if (GNUNET_OK !=
-        verify_refresh_melt_signature_ok (rmh,
-                                          j,
-                                          &exchange_pub,
-                                          &noreveal_index))
-    {
-      GNUNET_break_op (0);
-      response_code = 0;
-    }
-    if (NULL != rmh->melt_cb)
-    {
-      rmh->melt_cb (rmh->melt_cb_cls,
-                    response_code,
-                    TALER_JSON_get_error_code (j),
-                    noreveal_index,
-                    (0 == response_code) ? NULL : &exchange_pub,
-                    j);
-      rmh->melt_cb = NULL;
-    }
-    break;
-  case MHD_HTTP_BAD_REQUEST:
-    /* This should never happen, either us or the exchange is buggy
-       (or API version conflict); just pass JSON reply to the application */
-    break;
-  case MHD_HTTP_CONFLICT:
-    /* Double spending; check signatures on transaction history */
-    if (GNUNET_OK !=
-        verify_refresh_melt_signature_conflict (rmh,
-                                                j))
-    {
-      GNUNET_break_op (0);
-      response_code = 0;
-    }
-    break;
-  case MHD_HTTP_FORBIDDEN:
-    /* Nothing really to verify, exchange says one of the signatures is
-       invalid; assuming we checked them, this should never happen, we
-       should pass the JSON reply to the application */
-    break;
-  case MHD_HTTP_NOT_FOUND:
-    /* Nothing really to verify, this should never
-       happen, we should pass the JSON reply to the application */
-    break;
-  case MHD_HTTP_INTERNAL_SERVER_ERROR:
-    /* Server had an internal issue; we should retry, but this API
-       leaves this to the application */
-    break;
-  default:
-    /* unexpected response code */
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unexpected response code %u\n",
-                (unsigned int) response_code);
-    GNUNET_break (0);
-    response_code = 0;
-    break;
-  }
-  if (NULL != rmh->melt_cb)
-    rmh->melt_cb (rmh->melt_cb_cls,
-                  response_code,
-                  TALER_JSON_get_error_code (j),
-                  UINT32_MAX,
-                  NULL,
-                  j);
-  TALER_EXCHANGE_refresh_melt_cancel (rmh);
-}
-
-
-/**
- * Submit a melt request to the exchange and get the exchange's
- * response.
- *
- * This API is typically used by a wallet.  Note that to ensure that
- * no money is lost in case of hardware failures, the provided
- * argument should have been constructed using
- * #TALER_EXCHANGE_refresh_prepare and committed to persistent storage
- * prior to calling this function.
- *
- * @param exchange the exchange handle; the exchange must be ready to operate
- * @param refresh_data_length size of the @a refresh_data (returned
- *        in the `res_size` argument from #TALER_EXCHANGE_refresh_prepare())
- * @param refresh_data the refresh data as returned from
-          #TALER_EXCHANGE_refresh_prepare())
- * @param melt_cb the callback to call with the result
- * @param melt_cb_cls closure for @a melt_cb
- * @return a handle for this request; NULL if the argument was invalid.
- *         In this case, neither callback will be called.
- */
-struct TALER_EXCHANGE_RefreshMeltHandle *
-TALER_EXCHANGE_refresh_melt (struct TALER_EXCHANGE_Handle *exchange,
-                             size_t refresh_data_length,
-                             const char *refresh_data,
-                             TALER_EXCHANGE_RefreshMeltCallback melt_cb,
-                             void *melt_cb_cls)
-{
-  const struct TALER_EXCHANGE_Keys *key_state;
-  const struct TALER_EXCHANGE_DenomPublicKey *dki;
-  json_t *melt_obj;
-  struct TALER_EXCHANGE_RefreshMeltHandle *rmh;
-  CURL *eh;
-  struct GNUNET_CURL_Context *ctx;
-  struct MeltData *md;
-  struct TALER_CoinSpendSignatureP confirm_sig;
-  struct TALER_RefreshMeltCoinAffirmationPS melt;
-  struct GNUNET_HashCode h_denom_pub;
-  char arg_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2 + 32];
-
-  GNUNET_assert (GNUNET_YES ==
-                 TEAH_handle_is_ready (exchange));
-  md = deserialize_melt_data (refresh_data,
-                              refresh_data_length);
-  if (NULL == md)
-  {
-    GNUNET_break (0);
-    return NULL;
-  }
-  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);
-  GNUNET_CRYPTO_rsa_public_key_hash (md->melted_coin.pub_key.rsa_public_key,
-                                     &h_denom_pub);
-  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_hash",
-                        GNUNET_JSON_from_data_auto (&h_denom_pub),
-                        "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;
-  }
-  {
-    char pub_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2];
-    char *end;
-
-    end = GNUNET_STRINGS_data_to_string (&melt.coin_pub,
-                                         sizeof (struct
-                                                 TALER_CoinSpendPublicKeyP),
-                                         pub_str,
-                                         sizeof (pub_str));
-    *end = '\0';
-    GNUNET_snprintf (arg_str,
-                     sizeof (arg_str),
-                     "/coins/%s/melt",
-                     pub_str);
-  }
-
-  key_state = TALER_EXCHANGE_get_keys (exchange);
-  dki = TALER_EXCHANGE_get_denomination_key (key_state,
-                                             &md->melted_coin.pub_key);
-
-  /* and now we can at last begin the actual request handling */
-  rmh = GNUNET_new (struct TALER_EXCHANGE_RefreshMeltHandle);
-  rmh->exchange = exchange;
-  rmh->dki = *dki;
-  rmh->dki.key.rsa_public_key = NULL; /* lifetime not warranted, so better
-                                         not copy the pointer */
-  rmh->melt_cb = melt_cb;
-  rmh->melt_cb_cls = melt_cb_cls;
-  rmh->md = md;
-  rmh->url = TEAH_path_to_url (exchange,
-                               arg_str);
-  eh = TEL_curl_easy_get (rmh->url);
-  if (GNUNET_OK !=
-      TALER_curl_easy_post (&rmh->ctx,
-                            eh,
-                            melt_obj))
-  {
-    GNUNET_break (0);
-    curl_easy_cleanup (eh);
-    json_decref (melt_obj);
-    GNUNET_free (rmh->url);
-    GNUNET_free (rmh);
-    return NULL;
-  }
-  json_decref (melt_obj);
-  ctx = TEAH_handle_to_context (exchange);
-  rmh->job = GNUNET_CURL_job_add2 (ctx,
-                                   eh,
-                                   rmh->ctx.headers,
-                                   &handle_refresh_melt_finished,
-                                   rmh);
-  return rmh;
-}
-
-
-/**
- * Cancel a refresh execute request.  This function cannot be used
- * on a request handle if either callback was already invoked.
- *
- * @param rmh the refresh melt handle
- */
-void
-TALER_EXCHANGE_refresh_melt_cancel (struct
-                                    TALER_EXCHANGE_RefreshMeltHandle *rmh)
-{
-  if (NULL != rmh->job)
-  {
-    GNUNET_CURL_job_cancel (rmh->job);
-    rmh->job = NULL;
-  }
-  free_melt_data (rmh->md); /* does not free 'md' itself */
-  GNUNET_free (rmh->md);
-  GNUNET_free (rmh->url);
-  TALER_curl_easy_post_finished (&rmh->ctx);
-  GNUNET_free (rmh);
-}
-
-
-/* ********************* /refreshes/$RCH/reveal ***************************** 
*/
-
-
-/**
- * @brief A /refreshes/$RCH/reveal Handle
- */
-struct TALER_EXCHANGE_RefreshRevealHandle
-{
-
-  /**
-   * The connection to exchange this request handle will use
-   */
-  struct TALER_EXCHANGE_Handle *exchange;
-
-  /**
-   * The url for this request.
-   */
-  char *url;
-
-  /**
-   * Context for #TEH_curl_easy_post(). Keeps the data that must
-   * persist for Curl to make the upload.
-   */
-  struct TALER_CURL_PostContext ctx;
-
-  /**
-   * Handle for the request.
-   */
-  struct GNUNET_CURL_Job *job;
-
-  /**
-   * Function to call with the result.
-   */
-  TALER_EXCHANGE_RefreshRevealCallback reveal_cb;
-
-  /**
-   * Closure for @e reveal_cb.
-   */
-  void *reveal_cb_cls;
-
-  /**
-   * Actual information about the melt operation.
-   */
-  struct MeltData *md;
-
-  /**
-   * The index selected by the exchange in cut-and-choose to not be revealed.
-   */
-  uint16_t noreveal_index;
-
-};
-
-
-/**
- * We got a 200 OK response for the /refreshes/$RCH/reveal operation.
- * Extract the coin signatures and return them to the caller.
- * The signatures we get from the exchange is for the blinded value.
- * Thus, we first must unblind them and then should verify their
- * validity.
- *
- * If everything checks out, we return the unblinded signatures
- * to the application via the callback.
- *
- * @param rrh operation handle
- * @param json reply from the exchange
- * @param[out] sigs array of length `num_fresh_coins`, initialized to cointain 
RSA signatures
- * @return #GNUNET_OK on success, #GNUNET_SYSERR on errors
- */
-static int
-refresh_reveal_ok (struct TALER_EXCHANGE_RefreshRevealHandle *rrh,
-                   const json_t *json,
-                   struct TALER_DenominationSignature *sigs)
-{
-  json_t *jsona;
-  struct GNUNET_JSON_Specification outer_spec[] = {
-    GNUNET_JSON_spec_json ("ev_sigs", &jsona),
-    GNUNET_JSON_spec_end ()
-  };
-
-  if (GNUNET_OK !=
-      GNUNET_JSON_parse (json,
-                         outer_spec,
-                         NULL, NULL))
-  {
-    GNUNET_break_op (0);
-    return GNUNET_SYSERR;
-  }
-  if (! json_is_array (jsona))
-  {
-    /* We expected an array of coins */
-    GNUNET_break_op (0);
-    GNUNET_JSON_parse_free (outer_spec);
-    return GNUNET_SYSERR;
-  }
-  if (rrh->md->num_fresh_coins != json_array_size (jsona))
-  {
-    /* Number of coins generated does not match our expectation */
-    GNUNET_break_op (0);
-    GNUNET_JSON_parse_free (outer_spec);
-    return GNUNET_SYSERR;
-  }
-  for (unsigned int i = 0; i<rrh->md->num_fresh_coins; i++)
-  {
-    const struct TALER_PlanchetSecretsP *fc;
-    struct TALER_DenominationPublicKey *pk;
-    json_t *jsonai;
-    struct GNUNET_CRYPTO_RsaSignature *blind_sig;
-    struct TALER_CoinSpendPublicKeyP coin_pub;
-    struct GNUNET_HashCode coin_hash;
-    struct GNUNET_JSON_Specification spec[] = {
-      GNUNET_JSON_spec_rsa_signature ("ev_sig", &blind_sig),
-      GNUNET_JSON_spec_end ()
-    };
-    struct TALER_FreshCoin coin;
-
-    fc = &rrh->md->fresh_coins[rrh->noreveal_index][i];
-    pk = &rrh->md->fresh_pks[i];
-    jsonai = json_array_get (jsona, i);
-    GNUNET_assert (NULL != jsonai);
-
-    if (GNUNET_OK !=
-        GNUNET_JSON_parse (jsonai,
-                           spec,
-                           NULL, NULL))
-    {
-      GNUNET_break_op (0);
-      GNUNET_JSON_parse_free (outer_spec);
-      return GNUNET_SYSERR;
-    }
-
-    /* needed to verify the signature, and we didn't store it earlier,
-       hence recomputing it here... */
-    GNUNET_CRYPTO_eddsa_key_get_public (&fc->coin_priv.eddsa_priv,
-                                        &coin_pub.eddsa_pub);
-    GNUNET_CRYPTO_hash (&coin_pub.eddsa_pub,
-                        sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey),
-                        &coin_hash);
-    if (GNUNET_OK !=
-        TALER_planchet_to_coin (pk,
-                                blind_sig,
-                                fc,
-                                &coin_hash,
-                                &coin))
-    {
-      GNUNET_break_op (0);
-      GNUNET_CRYPTO_rsa_signature_free (blind_sig);
-      GNUNET_JSON_parse_free (outer_spec);
-      return GNUNET_SYSERR;
-    }
-    GNUNET_CRYPTO_rsa_signature_free (blind_sig);
-    sigs[i] = coin.sig;
-  }
-  GNUNET_JSON_parse_free (outer_spec);
-  return GNUNET_OK;
-}
-
-
-/**
- * Function called when we're done processing the
- * HTTP /refreshes/$RCH/reveal request.
- *
- * @param cls the `struct TALER_EXCHANGE_RefreshHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response parsed JSON result, NULL on error
- */
-static void
-handle_refresh_reveal_finished (void *cls,
-                                long response_code,
-                                const void *response)
-{
-  struct TALER_EXCHANGE_RefreshRevealHandle *rrh = cls;
-  const json_t *j = response;
-
-  rrh->job = NULL;
-  switch (response_code)
-  {
-  case 0:
-    break;
-  case MHD_HTTP_OK:
-    {
-      struct TALER_DenominationSignature sigs[rrh->md->num_fresh_coins];
-      int ret;
-
-      memset (sigs, 0, sizeof (sigs));
-      ret = refresh_reveal_ok (rrh,
-                               j,
-                               sigs);
-      if (GNUNET_OK != ret)
-      {
-        response_code = 0;
-      }
-      else
-      {
-        rrh->reveal_cb (rrh->reveal_cb_cls,
-                        MHD_HTTP_OK,
-                        TALER_EC_NONE,
-                        rrh->md->num_fresh_coins,
-                        rrh->md->fresh_coins[rrh->noreveal_index],
-                        sigs,
-                        j);
-        rrh->reveal_cb = NULL;
-      }
-      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);
-    }
-    break;
-  case MHD_HTTP_BAD_REQUEST:
-    /* This should never happen, either us or the exchange is buggy
-       (or API version conflict); just pass JSON reply to the application */
-    break;
-  case MHD_HTTP_CONFLICT:
-    /* Nothing really to verify, exchange says our reveal is inconsitent
-       with our commitment, so either side is buggy; we
-       should pass the JSON reply to the application */
-    break;
-  case MHD_HTTP_INTERNAL_SERVER_ERROR:
-    /* Server had an internal issue; we should retry, but this API
-       leaves this to the application */
-    break;
-  default:
-    /* unexpected response code */
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unexpected response code %u\n",
-                (unsigned int) response_code);
-    GNUNET_break (0);
-    response_code = 0;
-    break;
-  }
-  if (NULL != rrh->reveal_cb)
-    rrh->reveal_cb (rrh->reveal_cb_cls,
-                    response_code,
-                    TALER_JSON_get_error_code (j),
-                    0,
-                    NULL,
-                    NULL,
-                    j);
-  TALER_EXCHANGE_refresh_reveal_cancel (rrh);
-}
-
-
-/**
- * Submit a /refresh/reval request to the exchange and get the exchange's
- * response.
- *
- * This API is typically used by a wallet.  Note that to ensure that
- * no money is lost in case of hardware failures, the provided
- * arguments should have been committed to persistent storage
- * prior to calling this function.
- *
- * @param exchange the exchange handle; the exchange must be ready to operate
- * @param refresh_data_length size of the @a refresh_data (returned
- *        in the `res_size` argument from #TALER_EXCHANGE_refresh_prepare())
- * @param refresh_data the refresh data as returned from
-          #TALER_EXCHANGE_refresh_prepare())
- * @param noreveal_index response from the exchange to the
- *        #TALER_EXCHANGE_refresh_melt() invocation
- * @param reveal_cb the callback to call with the final result of the
- *        refresh operation
- * @param reveal_cb_cls closure for the above callback
- * @return a handle for this request; NULL if the argument was invalid.
- *         In this case, neither callback will be called.
- */
-struct TALER_EXCHANGE_RefreshRevealHandle *
-TALER_EXCHANGE_refresh_reveal (struct TALER_EXCHANGE_Handle *exchange,
-                               size_t refresh_data_length,
-                               const char *refresh_data,
-                               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;
-  json_t *link_sigs;
-  CURL *eh;
-  struct GNUNET_CURL_Context *ctx;
-  struct MeltData *md;
-  struct TALER_TransferPublicKeyP transfer_pub;
-  char arg_str[sizeof (struct TALER_RefreshCommitmentP) * 2 + 32];
-
-  if (noreveal_index >= TALER_CNC_KAPPA)
-  {
-    /* We check this here, as it would be really bad to below just
-       disclose all the transfer keys. Note that this error should
-       have been caught way earlier when the exchange replied, but maybe
-       we had some internal corruption that changed the value... */
-    GNUNET_break (0);
-    return NULL;
-  }
-  if (GNUNET_YES !=
-      TEAH_handle_is_ready (exchange))
-  {
-    GNUNET_break (0);
-    return NULL;
-  }
-  md = deserialize_melt_data (refresh_data,
-                              refresh_data_length);
-  if (NULL == md)
-  {
-    GNUNET_break (0);
-    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 ()));
-  GNUNET_assert (NULL != (link_sigs = 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)));
-
-    /* compute link signature */
-    {
-      struct TALER_CoinSpendSignatureP link_sig;
-      struct TALER_LinkDataPS ldp;
-
-      ldp.purpose.size = htonl (sizeof (ldp));
-      ldp.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_LINK);
-      ldp.h_denom_pub = denom_hash;
-      GNUNET_CRYPTO_eddsa_key_get_public 
(&md->melted_coin.coin_priv.eddsa_priv,
-                                          &ldp.old_coin_pub.eddsa_pub);
-      ldp.transfer_pub = transfer_pub;
-      GNUNET_CRYPTO_hash (pd.coin_ev,
-                          pd.coin_ev_size,
-                          &ldp.coin_envelope_hash);
-      GNUNET_assert (GNUNET_OK ==
-                     GNUNET_CRYPTO_eddsa_sign (
-                       &md->melted_coin.coin_priv.eddsa_priv,
-                       &ldp.purpose,
-                       &link_sig.eddsa_signature));
-      GNUNET_assert (0 ==
-                     json_array_append_new (link_sigs,
-                                            GNUNET_JSON_from_data_auto (
-                                              &link_sig)));
-    }
-
-    GNUNET_free (pd.coin_ev);
-  }
-
-  /* build array of transfer private keys */
-  GNUNET_assert (NULL != (transfer_privs = json_array ()));
-  for (unsigned int j = 0; j<TALER_CNC_KAPPA; j++)
-  {
-    if (j == noreveal_index)
-    {
-      /* This is crucial: exclude the transfer key for the
-   noreval index! */
-      continue;
-    }
-    GNUNET_assert (0 ==
-                   json_array_append_new (transfer_privs,
-                                          GNUNET_JSON_from_data_auto (
-                                            
&md->melted_coin.transfer_priv[j])));
-  }
-
-  /* build main JSON request */
-  reveal_obj = json_pack ("{s:o, s:o, s:o, s:o, s:o}",
-                          "transfer_pub",
-                          GNUNET_JSON_from_data_auto (&transfer_pub),
-                          "transfer_privs",
-                          transfer_privs,
-                          "link_sigs",
-                          link_sigs,
-                          "new_denoms_h",
-                          new_denoms_h,
-                          "coin_evs",
-                          coin_evs);
-  if (NULL == reveal_obj)
-  {
-    GNUNET_break (0);
-    return NULL;
-  }
-
-  {
-    char pub_str[sizeof (struct TALER_RefreshCommitmentP) * 2];
-    char *end;
-
-    end = GNUNET_STRINGS_data_to_string (&md->rc,
-                                         sizeof (struct
-                                                 TALER_RefreshCommitmentP),
-                                         pub_str,
-                                         sizeof (pub_str));
-    *end = '\0';
-    GNUNET_snprintf (arg_str,
-                     sizeof (arg_str),
-                     "/refreshes/%s/reveal",
-                     pub_str);
-  }
-  /* finally, we can actually issue the request */
-  rrh = GNUNET_new (struct TALER_EXCHANGE_RefreshRevealHandle);
-  rrh->exchange = exchange;
-  rrh->noreveal_index = noreveal_index;
-  rrh->reveal_cb = reveal_cb;
-  rrh->reveal_cb_cls = reveal_cb_cls;
-  rrh->md = md;
-  rrh->url = TEAH_path_to_url (rrh->exchange,
-                               arg_str);
-
-  eh = TEL_curl_easy_get (rrh->url);
-  if (GNUNET_OK !=
-      TALER_curl_easy_post (&rrh->ctx,
-                            eh,
-                            reveal_obj))
-  {
-    GNUNET_break (0);
-    curl_easy_cleanup (eh);
-    json_decref (reveal_obj);
-    GNUNET_free (rrh->url);
-    GNUNET_free (rrh);
-    return NULL;
-  }
-  json_decref (reveal_obj);
-  ctx = TEAH_handle_to_context (rrh->exchange);
-  rrh->job = GNUNET_CURL_job_add2 (ctx,
-                                   eh,
-                                   rrh->ctx.headers,
-                                   &handle_refresh_reveal_finished,
-                                   rrh);
-  return rrh;
-}
-
-
-/**
- * Cancel a refresh reveal request.  This function cannot be used
- * on a request handle if the callback was already invoked.
- *
- * @param rrh the refresh reval handle
- */
-void
-TALER_EXCHANGE_refresh_reveal_cancel (struct
-                                      TALER_EXCHANGE_RefreshRevealHandle *rrh)
-{
-  if (NULL != rrh->job)
-  {
-    GNUNET_CURL_job_cancel (rrh->job);
-    rrh->job = NULL;
-  }
-  GNUNET_free (rrh->url);
-  TALER_curl_easy_post_finished (&rrh->ctx);
-  free_melt_data (rrh->md); /* does not free 'md' itself */
-  GNUNET_free (rrh->md);
-  GNUNET_free (rrh);
-}
-
-
-/* end of exchange_api_refresh.c */
diff --git a/src/lib/exchange_api_refresh_common.c 
b/src/lib/exchange_api_refresh_common.c
new file mode 100644
index 00000000..b2eec291
--- /dev/null
+++ b/src/lib/exchange_api_refresh_common.c
@@ -0,0 +1,631 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2015-2020 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 lib/exchange_api_refresh_common.c
+ * @brief Serialization logic shared between melt and reveal steps during 
refreshing
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "exchange_api_refresh_common.h"
+
+
+/**
+ * Free all information associated with a melted coin session.
+ *
+ * @param mc melted coin to release, the pointer itself is NOT
+ *           freed (as it is typically not allocated by itself)
+ */
+static void
+free_melted_coin (struct MeltedCoin *mc)
+{
+  if (NULL != mc->pub_key.rsa_public_key)
+    GNUNET_CRYPTO_rsa_public_key_free (mc->pub_key.rsa_public_key);
+  if (NULL != mc->sig.rsa_signature)
+    GNUNET_CRYPTO_rsa_signature_free (mc->sig.rsa_signature);
+}
+
+
+/**
+ * Free all information associated with a melting session.  Note
+ * that we allow the melting session to be only partially initialized,
+ * as we use this function also when freeing melt data that was not
+ * fully initialized (i.e. due to failures in 
#TALER_EXCHANGE_deserialize_melt_data_()).
+ *
+ * @param md melting data to release, the pointer itself is NOT
+ *           freed (as it is typically not allocated by itself)
+ */
+void
+TALER_EXCHANGE_free_melt_data_ (struct MeltData *md)
+{
+  free_melted_coin (&md->melted_coin);
+  if (NULL != md->fresh_pks)
+  {
+    for (unsigned int i = 0; i<md->num_fresh_coins; i++)
+      if (NULL != md->fresh_pks[i].rsa_public_key)
+        GNUNET_CRYPTO_rsa_public_key_free (md->fresh_pks[i].rsa_public_key);
+    GNUNET_free (md->fresh_pks);
+  }
+
+  for (unsigned int i = 0; i<TALER_CNC_KAPPA; i++)
+    GNUNET_free_non_null (md->fresh_coins[i]);
+  /* Finally, clean up a bit...
+     (NOTE: compilers might optimize this away, so this is
+     not providing any strong assurances that the key material
+     is purged.) */
+  memset (md,
+          0,
+          sizeof (struct MeltData));
+}
+
+
+/**
+ * Serialize information about a coin we are melting.
+ *
+ * @param mc information to serialize
+ * @param buf buffer to write data in, NULL to just compute
+ *            required size
+ * @param off offeset at @a buf to use
+ * @return number of bytes written to @a buf at @a off, or if
+ *        @a buf is NULL, number of bytes required; 0 on error
+ */
+static size_t
+serialize_melted_coin (const struct MeltedCoin *mc,
+                       char *buf,
+                       size_t off)
+{
+  struct MeltedCoinP mcp;
+  unsigned int i;
+  char *pbuf;
+  size_t pbuf_size;
+  char *sbuf;
+  size_t sbuf_size;
+
+  sbuf_size = GNUNET_CRYPTO_rsa_signature_encode (mc->sig.rsa_signature,
+                                                  &sbuf);
+  pbuf_size = GNUNET_CRYPTO_rsa_public_key_encode (mc->pub_key.rsa_public_key,
+                                                   &pbuf);
+  if (NULL == buf)
+  {
+    GNUNET_free (sbuf);
+    GNUNET_free (pbuf);
+    return sizeof (struct MeltedCoinP) + sbuf_size + pbuf_size;
+  }
+  if ( (sbuf_size > UINT16_MAX) ||
+       (pbuf_size > UINT16_MAX) )
+  {
+    GNUNET_break (0);
+    return 0;
+  }
+  mcp.coin_priv = mc->coin_priv;
+  TALER_amount_hton (&mcp.melt_amount_with_fee,
+                     &mc->melt_amount_with_fee);
+  TALER_amount_hton (&mcp.fee_melt,
+                     &mc->fee_melt);
+  TALER_amount_hton (&mcp.original_value,
+                     &mc->original_value);
+  for (i = 0; i<TALER_CNC_KAPPA; i++)
+    mcp.transfer_priv[i] = mc->transfer_priv[i];
+  mcp.expire_deposit = GNUNET_TIME_absolute_hton (mc->expire_deposit);
+  mcp.pbuf_size = htons ((uint16_t) pbuf_size);
+  mcp.sbuf_size = htons ((uint16_t) sbuf_size);
+  memcpy (&buf[off],
+          &mcp,
+          sizeof (struct MeltedCoinP));
+  memcpy (&buf[off + sizeof (struct MeltedCoinP)],
+          pbuf,
+          pbuf_size);
+  memcpy (&buf[off + sizeof (struct MeltedCoinP) + pbuf_size],
+          sbuf,
+          sbuf_size);
+  GNUNET_free (sbuf);
+  GNUNET_free (pbuf);
+  return sizeof (struct MeltedCoinP) + sbuf_size + pbuf_size;
+}
+
+
+/**
+ * Deserialize information about a coin we are melting.
+ *
+ * @param[out] mc information to deserialize
+ * @param buf buffer to read data from
+ * @param size number of bytes available at @a buf to use
+ * @param[out] ok set to #GNUNET_NO to report errors
+ * @return number of bytes read from @a buf, 0 on error
+ */
+static size_t
+deserialize_melted_coin (struct MeltedCoin *mc,
+                         const char *buf,
+                         size_t size,
+                         int *ok)
+{
+  struct MeltedCoinP mcp;
+  unsigned int i;
+  size_t pbuf_size;
+  size_t sbuf_size;
+  size_t off;
+
+  if (size < sizeof (struct MeltedCoinP))
+  {
+    GNUNET_break (0);
+    *ok = GNUNET_NO;
+    return 0;
+  }
+  memcpy (&mcp,
+          buf,
+          sizeof (struct MeltedCoinP));
+  pbuf_size = ntohs (mcp.pbuf_size);
+  sbuf_size = ntohs (mcp.sbuf_size);
+  if (size < sizeof (struct MeltedCoinP) + pbuf_size + sbuf_size)
+  {
+    GNUNET_break (0);
+    *ok = GNUNET_NO;
+    return 0;
+  }
+  off = sizeof (struct MeltedCoinP);
+  mc->pub_key.rsa_public_key
+    = GNUNET_CRYPTO_rsa_public_key_decode (&buf[off],
+                                           pbuf_size);
+  off += pbuf_size;
+  mc->sig.rsa_signature
+    = GNUNET_CRYPTO_rsa_signature_decode (&buf[off],
+                                          sbuf_size);
+  off += sbuf_size;
+  if ( (NULL == mc->pub_key.rsa_public_key) ||
+       (NULL == mc->sig.rsa_signature) )
+  {
+    GNUNET_break (0);
+    *ok = GNUNET_NO;
+    return 0;
+  }
+
+  mc->coin_priv = mcp.coin_priv;
+  TALER_amount_ntoh (&mc->melt_amount_with_fee,
+                     &mcp.melt_amount_with_fee);
+  TALER_amount_ntoh (&mc->fee_melt,
+                     &mcp.fee_melt);
+  TALER_amount_ntoh (&mc->original_value,
+                     &mcp.original_value);
+  for (i = 0; i<TALER_CNC_KAPPA; i++)
+    mc->transfer_priv[i] = mcp.transfer_priv[i];
+  mc->expire_deposit = GNUNET_TIME_absolute_ntoh (mcp.expire_deposit);
+  return off;
+}
+
+
+/**
+ * Serialize information about a denomination key.
+ *
+ * @param dk information to serialize
+ * @param buf buffer to write data in, NULL to just compute
+ *            required size
+ * @param off offeset at @a buf to use
+ * @return number of bytes written to @a buf at @a off, or if
+ *        @a buf is NULL, number of bytes required
+ */
+static size_t
+serialize_denomination_key (const struct TALER_DenominationPublicKey *dk,
+                            char *buf,
+                            size_t off)
+{
+  char *pbuf;
+  size_t pbuf_size;
+  uint32_t be;
+
+  pbuf_size = GNUNET_CRYPTO_rsa_public_key_encode (dk->rsa_public_key,
+                                                   &pbuf);
+  if (NULL == buf)
+  {
+    GNUNET_free (pbuf);
+    return pbuf_size + sizeof (uint32_t);
+  }
+  be = htonl ((uint32_t) pbuf_size);
+  memcpy (&buf[off],
+          &be,
+          sizeof (uint32_t));
+  memcpy (&buf[off + sizeof (uint32_t)],
+          pbuf,
+          pbuf_size);
+  GNUNET_free (pbuf);
+  return pbuf_size + sizeof (uint32_t);
+}
+
+
+/**
+ * Deserialize information about a denomination key.
+ *
+ * @param[out] dk information to deserialize
+ * @param buf buffer to read data from
+ * @param size number of bytes available at @a buf to use
+ * @param[out] ok set to #GNUNET_NO to report errors
+ * @return number of bytes read from @a buf, 0 on error
+ */
+static size_t
+deserialize_denomination_key (struct TALER_DenominationPublicKey *dk,
+                              const char *buf,
+                              size_t size,
+                              int *ok)
+{
+  size_t pbuf_size;
+  uint32_t be;
+
+  if (size < sizeof (uint32_t))
+  {
+    GNUNET_break (0);
+    *ok = GNUNET_NO;
+    return 0;
+  }
+  memcpy (&be,
+          buf,
+          sizeof (uint32_t));
+  pbuf_size = ntohl (be);
+  if (size < sizeof (uint32_t) + pbuf_size)
+  {
+    GNUNET_break (0);
+    *ok = GNUNET_NO;
+    return 0;
+  }
+  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);
+    *ok = GNUNET_NO;
+    return 0;
+  }
+  return sizeof (uint32_t) + pbuf_size;
+}
+
+
+/**
+ * Serialize information about a fresh coin we are generating.
+ *
+ * @param fc information to serialize
+ * @param buf buffer to write data in, NULL to just compute
+ *            required size
+ * @param off offeset at @a buf to use
+ * @return number of bytes written to @a buf at @a off, or if
+ *        @a buf is NULL, number of bytes required
+ */
+static size_t
+serialize_fresh_coin (const struct TALER_PlanchetSecretsP *fc,
+                      char *buf,
+                      size_t off)
+{
+  if (NULL != buf)
+    memcpy (&buf[off],
+            fc,
+            sizeof (struct TALER_PlanchetSecretsP));
+  return sizeof (struct TALER_PlanchetSecretsP);
+}
+
+
+/**
+ * Deserialize information about a fresh coin we are generating.
+ *
+ * @param[out] fc information to deserialize
+ * @param buf buffer to read data from
+ * @param size number of bytes available at @a buf to use
+ * @param[out] ok set to #GNUNET_NO to report errors
+ * @return number of bytes read from @a buf, 0 on error
+ */
+static size_t
+deserialize_fresh_coin (struct TALER_PlanchetSecretsP *fc,
+                        const char *buf,
+                        size_t size,
+                        int *ok)
+{
+  if (size < sizeof (struct TALER_PlanchetSecretsP))
+  {
+    GNUNET_break (0);
+    *ok = GNUNET_NO;
+    return 0;
+  }
+  memcpy (fc,
+          buf,
+          sizeof (struct TALER_PlanchetSecretsP));
+  return sizeof (struct TALER_PlanchetSecretsP);
+}
+
+
+/**
+ * Serialize melt data.
+ *
+ * @param md data to serialize
+ * @param[out] res_size size of buffer returned
+ * @return serialized melt data
+ */
+static char *
+serialize_melt_data (const struct MeltData *md,
+                     size_t *res_size)
+{
+  size_t size;
+  size_t asize;
+  char *buf;
+
+  size = 0;
+  asize = (size_t) -1; /* make the compiler happy */
+  buf = NULL;
+  /* we do 2 iterations, #1 to determine total size, #2 to
+     actually construct the buffer */
+  do {
+    if (0 == size)
+    {
+      size = sizeof (struct MeltDataP);
+    }
+    else
+    {
+      struct MeltDataP *mdp;
+
+      buf = GNUNET_malloc (size);
+      asize = size; /* just for invariant check later */
+      size = sizeof (struct MeltDataP);
+      mdp = (struct MeltDataP *) buf;
+      mdp->rc = md->rc;
+      mdp->num_fresh_coins = htons (md->num_fresh_coins);
+    }
+    size += serialize_melted_coin (&md->melted_coin,
+                                   buf,
+                                   size);
+    for (unsigned int i = 0; i<md->num_fresh_coins; i++)
+      size += serialize_denomination_key (&md->fresh_pks[i],
+                                          buf,
+                                          size);
+    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);
+  } while (NULL == buf);
+  GNUNET_assert (size == asize);
+  *res_size = size;
+  return buf;
+}
+
+
+/**
+ * Deserialize melt data.
+ *
+ * @param buf serialized data
+ * @param buf_size size of @a buf
+ * @return deserialized melt data, NULL on error
+ */
+struct MeltData *
+TALER_EXCHANGE_deserialize_melt_data_ (const char *buf,
+                                       size_t buf_size)
+{
+  struct MeltData *md;
+  struct MeltDataP mdp;
+  size_t off;
+  int ok;
+
+  if (buf_size < sizeof (struct MeltDataP))
+    return NULL;
+  memcpy (&mdp,
+          buf,
+          sizeof (struct MeltDataP));
+  md = GNUNET_new (struct MeltData);
+  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);
+  for (unsigned int i = 0; i<TALER_CNC_KAPPA; i++)
+    md->fresh_coins[i] = GNUNET_new_array (md->num_fresh_coins,
+                                           struct TALER_PlanchetSecretsP);
+  off = sizeof (struct MeltDataP);
+  ok = GNUNET_YES;
+  off += deserialize_melted_coin (&md->melted_coin,
+                                  &buf[off],
+                                  buf_size - off,
+                                  &ok);
+  for (unsigned int i = 0; (i<md->num_fresh_coins) && (GNUNET_YES == ok); i++)
+    off += deserialize_denomination_key (&md->fresh_pks[i],
+                                         &buf[off],
+                                         buf_size - off,
+                                         &ok);
+
+  for (unsigned int i = 0; i<TALER_CNC_KAPPA; i++)
+    for (unsigned int j = 0; (j<md->num_fresh_coins) && (GNUNET_YES == ok); 
j++)
+      off += deserialize_fresh_coin (&md->fresh_coins[i][j],
+                                     &buf[off],
+                                     buf_size - off,
+                                     &ok);
+  if (off != buf_size)
+  {
+    GNUNET_break (0);
+    ok = GNUNET_NO;
+  }
+  if (GNUNET_YES != ok)
+  {
+    TALER_EXCHANGE_free_melt_data_ (md);
+    GNUNET_free (md);
+    return NULL;
+  }
+  return md;
+}
+
+
+/**
+ * Melt (partially spent) coins to obtain fresh coins that are
+ * unlinkable to the original coin(s).  Note that melting more
+ * than one coin in a single request will make those coins linkable,
+ * so the safest operation only melts one coin at a time.
+ *
+ * This API is typically used by a wallet.  Note that to ensure that
+ * no money is lost in case of hardware failures, this operation does
+ * not actually initiate the request. Instead, it generates a buffer
+ * which the caller must store before proceeding with the actual call
+ * to #TALER_EXCHANGE_melt() that will generate the request.
+ *
+ * This function does verify that the given request data is internally
+ * consistent.  However, the @a melts_sigs are NOT verified.
+ *
+ * Aside from some non-trivial cryptographic operations that might
+ * take a bit of CPU time to complete, this function returns
+ * its result immediately and does not start any asynchronous
+ * processing.  This function is also thread-safe.
+ *
+ * @param melt_priv private key of the coin to melt
+ * @param melt_amount amount specifying how much
+ *                     the coin will contribute to the melt (including fee)
+ * @param melt_sig signature affirming the
+ *                   validity of the public keys corresponding to the
+ *                   @a melt_priv private key
+ * @param melt_pk denomination key information
+ *                   record corresponding to the @a melt_sig
+ *                   validity of the keys
+ * @param fresh_pks_len length of the @a pks array
+ * @param fresh_pks array of @a pks_len denominations of fresh coins to create
+ * @param[out] res_size set to the size of the return value, or 0 on error
+ * @return NULL
+ *         if the inputs are invalid (i.e. denomination key not with this 
exchange).
+ *         Otherwise, pointer to a buffer of @a res_size to store persistently
+ *         before proceeding to #TALER_EXCHANGE_melt().
+ *         Non-null results should be freed using GNUNET_free().
+ */
+char *
+TALER_EXCHANGE_refresh_prepare (const struct
+                                TALER_CoinSpendPrivateKeyP *melt_priv,
+                                const struct TALER_Amount *melt_amount,
+                                const struct
+                                TALER_DenominationSignature *melt_sig,
+                                const struct
+                                TALER_EXCHANGE_DenomPublicKey *melt_pk,
+                                unsigned int fresh_pks_len,
+                                const struct
+                                TALER_EXCHANGE_DenomPublicKey *fresh_pks,
+                                size_t *res_size)
+{
+  struct MeltData md;
+  char *buf;
+  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);
+  /* build up melt data structure */
+  memset (&md, 0, sizeof (md));
+  md.num_fresh_coins = fresh_pks_len;
+  md.melted_coin.coin_priv = *melt_priv;
+  md.melted_coin.melt_amount_with_fee = *melt_amount;
+  md.melted_coin.fee_melt = melt_pk->fee_refresh;
+  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
+    = GNUNET_CRYPTO_rsa_signature_dup (melt_sig->rsa_signature);
+  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);
+    if ( (GNUNET_OK !=
+          TALER_amount_add (&total,
+                            &total,
+                            &fresh_pks[i].value)) ||
+         (GNUNET_OK !=
+          TALER_amount_add (&total,
+                            &total,
+                            &fresh_pks[i].fee_withdraw)) )
+    {
+      GNUNET_break (0);
+      TALER_EXCHANGE_free_melt_data_ (&md);
+      return NULL;
+    }
+  }
+  /* verify that melt_amount is above total cost */
+  if (1 ==
+      TALER_amount_cmp (&total,
+                        melt_amount) )
+  {
+    /* Eh, this operation is more expensive than the
+       @a melt_amount. This is not OK. */
+    GNUNET_break (0);
+    TALER_EXCHANGE_free_melt_data_ (&md);
+    return NULL;
+  }
+
+  /* build up coins */
+  for (unsigned int i = 0; i<TALER_CNC_KAPPA; i++)
+  {
+    struct GNUNET_CRYPTO_EcdhePrivateKey *tpk;
+
+    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,
+      &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++)
+    {
+      struct TALER_PlanchetSecretsP *fc = &md.fresh_coins[i][j];
+      struct TALER_RefreshCoinData *rcd = &rce[i].new_coins[j];
+      struct TALER_PlanchetDetail pd;
+
+      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);
+        TALER_EXCHANGE_free_melt_data_ (&md);
+        return NULL;
+      }
+      rcd->dk = &md.fresh_pks[j];
+      rcd->coin_ev = pd.coin_ev;
+      rcd->coin_ev_size = pd.coin_ev_size;
+    }
+  }
+
+  /* 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);
+  }
+  TALER_EXCHANGE_free_melt_data_ (&md);
+  return buf;
+}
diff --git a/src/lib/exchange_api_refresh_common.h 
b/src/lib/exchange_api_refresh_common.h
new file mode 100644
index 00000000..05988e65
--- /dev/null
+++ b/src/lib/exchange_api_refresh_common.h
@@ -0,0 +1,240 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2015-2020 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 lib/exchange_api_refresh.c
+ * @brief Implementation of the /refresh/melt+reveal requests of the 
exchange's HTTP API
+ * @author Christian Grothoff
+ */
+#ifndef REFRESH_COMMON_H
+#define REFRESH_COMMON_H
+#include <jansson.h>
+#include "taler_json_lib.h"
+#include "taler_exchange_service.h"
+#include "taler_signatures.h"
+
+
+/* structures for committing refresh data to disk before doing the
+   network interaction(s) */
+
+GNUNET_NETWORK_STRUCT_BEGIN
+
+/**
+ * Header of serialized information about a coin we are melting.
+ */
+struct MeltedCoinP
+{
+  /**
+   * Private key of the coin.
+   */
+  struct TALER_CoinSpendPrivateKeyP coin_priv;
+
+  /**
+   * Amount this coin contributes to the melt, including fee.
+   */
+  struct TALER_AmountNBO melt_amount_with_fee;
+
+  /**
+   * The applicable fee for withdrawing a coin of this denomination
+   */
+  struct TALER_AmountNBO fee_melt;
+
+  /**
+   * The original value of the coin.
+   */
+  struct TALER_AmountNBO original_value;
+
+  /**
+   * Transfer private keys for each cut-and-choose dimension.
+   */
+  struct TALER_TransferPrivateKeyP transfer_priv[TALER_CNC_KAPPA];
+
+  /**
+   * Timestamp indicating when coins of this denomination become invalid.
+   */
+  struct GNUNET_TIME_AbsoluteNBO expire_deposit;
+
+  /**
+   * Size of the encoded public key that follows.
+   */
+  uint16_t pbuf_size;
+
+  /**
+   * Size of the encoded signature that follows.
+   */
+  uint16_t sbuf_size;
+
+  /* Followed by serializations of:
+     1) struct TALER_DenominationPublicKey pub_key;
+     2) struct TALER_DenominationSignature sig;
+  */
+};
+
+
+/**
+ * Header of serialized data about a melt operation, suitable for
+ * persisting it on disk.
+ */
+struct MeltDataP
+{
+
+  /**
+   * Hash over the melting session.
+   */
+  struct TALER_RefreshCommitmentP rc;
+
+  /**
+   * Number of coins we are melting, in NBO
+   */
+  uint16_t num_melted_coins GNUNET_PACKED;
+
+  /**
+   * Number of coins we are creating, in NBO
+   */
+  uint16_t num_fresh_coins GNUNET_PACKED;
+
+  /* Followed by serializations of:
+     1) struct MeltedCoinP melted_coins[num_melted_coins];
+     2) struct TALER_EXCHANGE_DenomPublicKey fresh_pks[num_fresh_coins];
+     3) TALER_CNC_KAPPA times:
+        3a) struct TALER_PlanchetSecretsP fresh_coins[num_fresh_coins];
+  */
+};
+
+
+GNUNET_NETWORK_STRUCT_END
+
+
+/**
+ * Information about a coin we are melting.
+ */
+struct MeltedCoin
+{
+  /**
+   * Private key of the coin.
+   */
+  struct TALER_CoinSpendPrivateKeyP coin_priv;
+
+  /**
+   * Amount this coin contributes to the melt, including fee.
+   */
+  struct TALER_Amount melt_amount_with_fee;
+
+  /**
+   * The applicable fee for melting a coin of this denomination
+   */
+  struct TALER_Amount fee_melt;
+
+  /**
+   * The original value of the coin.
+   */
+  struct TALER_Amount original_value;
+
+  /**
+   * Transfer private keys for each cut-and-choose dimension.
+   */
+  struct TALER_TransferPrivateKeyP transfer_priv[TALER_CNC_KAPPA];
+
+  /**
+   * Timestamp indicating when coins of this denomination become invalid.
+   */
+  struct GNUNET_TIME_Absolute expire_deposit;
+
+  /**
+   * Denomination key of the original coin.
+   */
+  struct TALER_DenominationPublicKey pub_key;
+
+  /**
+   * Exchange's signature over the coin.
+   */
+  struct TALER_DenominationSignature sig;
+
+};
+
+
+/**
+ * Melt data in non-serialized format for convenient processing.
+ */
+struct MeltData
+{
+
+  /**
+   * Hash over the committed data during refresh operation.
+   */
+  struct TALER_RefreshCommitmentP rc;
+
+  /**
+   * Number of coins we are creating
+   */
+  uint16_t num_fresh_coins;
+
+  /**
+   * Information about the melted coin.
+   */
+  struct MeltedCoin melted_coin;
+
+  /**
+   * Array of @e num_fresh_coins denomination keys for the coins to be
+   * freshly exchangeed.
+   */
+  struct TALER_DenominationPublicKey *fresh_pks;
+
+  /**
+   * Arrays of @e num_fresh_coins with information about the fresh
+   * coins to be created, for each cut-and-choose dimension.
+   */
+  struct TALER_PlanchetSecretsP *fresh_coins[TALER_CNC_KAPPA];
+};
+
+
+/**
+ * Serialize melt data.
+ *
+ * @param md data to serialize
+ * @param[out] res_size size of buffer returned
+ * @return serialized melt data
+ */
+char *
+TALER_EXCHANGE_serialize_melt_data_ (const struct MeltData *md,
+                                     size_t *res_size);
+
+
+/**
+ * Deserialize melt data.
+ *
+ * @param buf serialized data
+ * @param buf_size size of @a buf
+ * @return deserialized melt data, NULL on error
+ */
+struct MeltData *
+TALER_EXCHANGE_deserialize_melt_data_ (const char *buf,
+                                       size_t buf_size);
+
+
+/**
+ * Free all information associated with a melting session.  Note
+ * that we allow the melting session to be only partially initialized,
+ * as we use this function also when freeing melt data that was not
+ * fully initialized (i.e. due to failures in #deserialize_melt_data()).
+ *
+ * @param md melting data to release, the pointer itself is NOT
+ *           freed (as it is typically not allocated by itself)
+ */
+void
+TALER_EXCHANGE_free_melt_data_ (struct MeltData *md);
+
+#endif
diff --git a/src/lib/exchange_api_refreshes_reveal.c 
b/src/lib/exchange_api_refreshes_reveal.c
new file mode 100644
index 00000000..96aafbda
--- /dev/null
+++ b/src/lib/exchange_api_refreshes_reveal.c
@@ -0,0 +1,512 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2015-2020 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 lib/exchange_api_refreshes_reveal.c
+ * @brief Implementation of the /refreshes/$RCH/reveal requests
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <jansson.h>
+#include <microhttpd.h> /* just for HTTP status codes */
+#include <gnunet/gnunet_util_lib.h>
+#include <gnunet/gnunet_json_lib.h>
+#include <gnunet/gnunet_curl_lib.h>
+#include "taler_json_lib.h"
+#include "taler_exchange_service.h"
+#include "exchange_api_handle.h"
+#include "taler_signatures.h"
+#include "exchange_api_curl_defaults.h"
+#include "exchange_api_refresh_common.h"
+
+
+/**
+ * @brief A /refreshes/$RCH/reveal Handle
+ */
+struct TALER_EXCHANGE_RefreshesRevealHandle
+{
+
+  /**
+   * The connection to exchange this request handle will use
+   */
+  struct TALER_EXCHANGE_Handle *exchange;
+
+  /**
+   * The url for this request.
+   */
+  char *url;
+
+  /**
+   * Context for #TEH_curl_easy_post(). Keeps the data that must
+   * persist for Curl to make the upload.
+   */
+  struct TALER_CURL_PostContext ctx;
+
+  /**
+   * Handle for the request.
+   */
+  struct GNUNET_CURL_Job *job;
+
+  /**
+   * Function to call with the result.
+   */
+  TALER_EXCHANGE_RefreshesRevealCallback reveal_cb;
+
+  /**
+   * Closure for @e reveal_cb.
+   */
+  void *reveal_cb_cls;
+
+  /**
+   * Actual information about the melt operation.
+   */
+  struct MeltData *md;
+
+  /**
+   * The index selected by the exchange in cut-and-choose to not be revealed.
+   */
+  uint16_t noreveal_index;
+
+};
+
+
+/**
+ * We got a 200 OK response for the /refreshes/$RCH/reveal operation.
+ * Extract the coin signatures and return them to the caller.
+ * The signatures we get from the exchange is for the blinded value.
+ * Thus, we first must unblind them and then should verify their
+ * validity.
+ *
+ * If everything checks out, we return the unblinded signatures
+ * to the application via the callback.
+ *
+ * @param rrh operation handle
+ * @param json reply from the exchange
+ * @param[out] sigs array of length `num_fresh_coins`, initialized to cointain 
RSA signatures
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on errors
+ */
+static int
+refresh_reveal_ok (struct TALER_EXCHANGE_RefreshesRevealHandle *rrh,
+                   const json_t *json,
+                   struct TALER_DenominationSignature *sigs)
+{
+  json_t *jsona;
+  struct GNUNET_JSON_Specification outer_spec[] = {
+    GNUNET_JSON_spec_json ("ev_sigs", &jsona),
+    GNUNET_JSON_spec_end ()
+  };
+
+  if (GNUNET_OK !=
+      GNUNET_JSON_parse (json,
+                         outer_spec,
+                         NULL, NULL))
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  if (! json_is_array (jsona))
+  {
+    /* We expected an array of coins */
+    GNUNET_break_op (0);
+    GNUNET_JSON_parse_free (outer_spec);
+    return GNUNET_SYSERR;
+  }
+  if (rrh->md->num_fresh_coins != json_array_size (jsona))
+  {
+    /* Number of coins generated does not match our expectation */
+    GNUNET_break_op (0);
+    GNUNET_JSON_parse_free (outer_spec);
+    return GNUNET_SYSERR;
+  }
+  for (unsigned int i = 0; i<rrh->md->num_fresh_coins; i++)
+  {
+    const struct TALER_PlanchetSecretsP *fc;
+    struct TALER_DenominationPublicKey *pk;
+    json_t *jsonai;
+    struct GNUNET_CRYPTO_RsaSignature *blind_sig;
+    struct TALER_CoinSpendPublicKeyP coin_pub;
+    struct GNUNET_HashCode coin_hash;
+    struct GNUNET_JSON_Specification spec[] = {
+      GNUNET_JSON_spec_rsa_signature ("ev_sig", &blind_sig),
+      GNUNET_JSON_spec_end ()
+    };
+    struct TALER_FreshCoin coin;
+
+    fc = &rrh->md->fresh_coins[rrh->noreveal_index][i];
+    pk = &rrh->md->fresh_pks[i];
+    jsonai = json_array_get (jsona, i);
+    GNUNET_assert (NULL != jsonai);
+
+    if (GNUNET_OK !=
+        GNUNET_JSON_parse (jsonai,
+                           spec,
+                           NULL, NULL))
+    {
+      GNUNET_break_op (0);
+      GNUNET_JSON_parse_free (outer_spec);
+      return GNUNET_SYSERR;
+    }
+
+    /* needed to verify the signature, and we didn't store it earlier,
+       hence recomputing it here... */
+    GNUNET_CRYPTO_eddsa_key_get_public (&fc->coin_priv.eddsa_priv,
+                                        &coin_pub.eddsa_pub);
+    GNUNET_CRYPTO_hash (&coin_pub.eddsa_pub,
+                        sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey),
+                        &coin_hash);
+    if (GNUNET_OK !=
+        TALER_planchet_to_coin (pk,
+                                blind_sig,
+                                fc,
+                                &coin_hash,
+                                &coin))
+    {
+      GNUNET_break_op (0);
+      GNUNET_CRYPTO_rsa_signature_free (blind_sig);
+      GNUNET_JSON_parse_free (outer_spec);
+      return GNUNET_SYSERR;
+    }
+    GNUNET_CRYPTO_rsa_signature_free (blind_sig);
+    sigs[i] = coin.sig;
+  }
+  GNUNET_JSON_parse_free (outer_spec);
+  return GNUNET_OK;
+}
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP /refreshes/$RCH/reveal request.
+ *
+ * @param cls the `struct TALER_EXCHANGE_RefreshHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response parsed JSON result, NULL on error
+ */
+static void
+handle_refresh_reveal_finished (void *cls,
+                                long response_code,
+                                const void *response)
+{
+  struct TALER_EXCHANGE_RefreshesRevealHandle *rrh = cls;
+  const json_t *j = response;
+
+  rrh->job = NULL;
+  switch (response_code)
+  {
+  case 0:
+    break;
+  case MHD_HTTP_OK:
+    {
+      struct TALER_DenominationSignature sigs[rrh->md->num_fresh_coins];
+      int ret;
+
+      memset (sigs, 0, sizeof (sigs));
+      ret = refresh_reveal_ok (rrh,
+                               j,
+                               sigs);
+      if (GNUNET_OK != ret)
+      {
+        response_code = 0;
+      }
+      else
+      {
+        rrh->reveal_cb (rrh->reveal_cb_cls,
+                        MHD_HTTP_OK,
+                        TALER_EC_NONE,
+                        rrh->md->num_fresh_coins,
+                        rrh->md->fresh_coins[rrh->noreveal_index],
+                        sigs,
+                        j);
+        rrh->reveal_cb = NULL;
+      }
+      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);
+    }
+    break;
+  case MHD_HTTP_BAD_REQUEST:
+    /* This should never happen, either us or the exchange is buggy
+       (or API version conflict); just pass JSON reply to the application */
+    break;
+  case MHD_HTTP_CONFLICT:
+    /* Nothing really to verify, exchange says our reveal is inconsitent
+       with our commitment, so either side is buggy; we
+       should pass the JSON reply to the application */
+    break;
+  case MHD_HTTP_INTERNAL_SERVER_ERROR:
+    /* Server had an internal issue; we should retry, but this API
+       leaves this to the application */
+    break;
+  default:
+    /* unexpected response code */
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Unexpected response code %u\n",
+                (unsigned int) response_code);
+    GNUNET_break (0);
+    response_code = 0;
+    break;
+  }
+  if (NULL != rrh->reveal_cb)
+    rrh->reveal_cb (rrh->reveal_cb_cls,
+                    response_code,
+                    TALER_JSON_get_error_code (j),
+                    0,
+                    NULL,
+                    NULL,
+                    j);
+  TALER_EXCHANGE_refreshes_reveal_cancel (rrh);
+}
+
+
+/**
+ * Submit a /refresh/reval request to the exchange and get the exchange's
+ * response.
+ *
+ * This API is typically used by a wallet.  Note that to ensure that
+ * no money is lost in case of hardware failures, the provided
+ * arguments should have been committed to persistent storage
+ * prior to calling this function.
+ *
+ * @param exchange the exchange handle; the exchange must be ready to operate
+ * @param refresh_data_length size of the @a refresh_data (returned
+ *        in the `res_size` argument from #TALER_EXCHANGE_refresh_prepare())
+ * @param refresh_data the refresh data as returned from
+          #TALER_EXCHANGE_refresh_prepare())
+ * @param noreveal_index response from the exchange to the
+ *        #TALER_EXCHANGE_melt() invocation
+ * @param reveal_cb the callback to call with the final result of the
+ *        refresh operation
+ * @param reveal_cb_cls closure for the above callback
+ * @return a handle for this request; NULL if the argument was invalid.
+ *         In this case, neither callback will be called.
+ */
+struct TALER_EXCHANGE_RefreshesRevealHandle *
+TALER_EXCHANGE_refreshes_reveal (struct TALER_EXCHANGE_Handle *exchange,
+                                 size_t refresh_data_length,
+                                 const char *refresh_data,
+                                 uint32_t noreveal_index,
+                                 TALER_EXCHANGE_RefreshesRevealCallback
+                                 reveal_cb,
+                                 void *reveal_cb_cls)
+{
+  struct TALER_EXCHANGE_RefreshesRevealHandle *rrh;
+  json_t *transfer_privs;
+  json_t *new_denoms_h;
+  json_t *coin_evs;
+  json_t *reveal_obj;
+  json_t *link_sigs;
+  CURL *eh;
+  struct GNUNET_CURL_Context *ctx;
+  struct MeltData *md;
+  struct TALER_TransferPublicKeyP transfer_pub;
+  char arg_str[sizeof (struct TALER_RefreshCommitmentP) * 2 + 32];
+
+  if (noreveal_index >= TALER_CNC_KAPPA)
+  {
+    /* We check this here, as it would be really bad to below just
+       disclose all the transfer keys. Note that this error should
+       have been caught way earlier when the exchange replied, but maybe
+       we had some internal corruption that changed the value... */
+    GNUNET_break (0);
+    return NULL;
+  }
+  if (GNUNET_YES !=
+      TEAH_handle_is_ready (exchange))
+  {
+    GNUNET_break (0);
+    return NULL;
+  }
+  md = TALER_EXCHANGE_deserialize_melt_data_ (refresh_data,
+                                              refresh_data_length);
+  if (NULL == md)
+  {
+    GNUNET_break (0);
+    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 ()));
+  GNUNET_assert (NULL != (link_sigs = 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)));
+
+    /* compute link signature */
+    {
+      struct TALER_CoinSpendSignatureP link_sig;
+      struct TALER_LinkDataPS ldp;
+
+      ldp.purpose.size = htonl (sizeof (ldp));
+      ldp.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_LINK);
+      ldp.h_denom_pub = denom_hash;
+      GNUNET_CRYPTO_eddsa_key_get_public 
(&md->melted_coin.coin_priv.eddsa_priv,
+                                          &ldp.old_coin_pub.eddsa_pub);
+      ldp.transfer_pub = transfer_pub;
+      GNUNET_CRYPTO_hash (pd.coin_ev,
+                          pd.coin_ev_size,
+                          &ldp.coin_envelope_hash);
+      GNUNET_assert (GNUNET_OK ==
+                     GNUNET_CRYPTO_eddsa_sign (
+                       &md->melted_coin.coin_priv.eddsa_priv,
+                       &ldp.purpose,
+                       &link_sig.eddsa_signature));
+      GNUNET_assert (0 ==
+                     json_array_append_new (link_sigs,
+                                            GNUNET_JSON_from_data_auto (
+                                              &link_sig)));
+    }
+
+    GNUNET_free (pd.coin_ev);
+  }
+
+  /* build array of transfer private keys */
+  GNUNET_assert (NULL != (transfer_privs = json_array ()));
+  for (unsigned int j = 0; j<TALER_CNC_KAPPA; j++)
+  {
+    if (j == noreveal_index)
+    {
+      /* This is crucial: exclude the transfer key for the
+   noreval index! */
+      continue;
+    }
+    GNUNET_assert (0 ==
+                   json_array_append_new (transfer_privs,
+                                          GNUNET_JSON_from_data_auto (
+                                            
&md->melted_coin.transfer_priv[j])));
+  }
+
+  /* build main JSON request */
+  reveal_obj = json_pack ("{s:o, s:o, s:o, s:o, s:o}",
+                          "transfer_pub",
+                          GNUNET_JSON_from_data_auto (&transfer_pub),
+                          "transfer_privs",
+                          transfer_privs,
+                          "link_sigs",
+                          link_sigs,
+                          "new_denoms_h",
+                          new_denoms_h,
+                          "coin_evs",
+                          coin_evs);
+  if (NULL == reveal_obj)
+  {
+    GNUNET_break (0);
+    return NULL;
+  }
+
+  {
+    char pub_str[sizeof (struct TALER_RefreshCommitmentP) * 2];
+    char *end;
+
+    end = GNUNET_STRINGS_data_to_string (&md->rc,
+                                         sizeof (struct
+                                                 TALER_RefreshCommitmentP),
+                                         pub_str,
+                                         sizeof (pub_str));
+    *end = '\0';
+    GNUNET_snprintf (arg_str,
+                     sizeof (arg_str),
+                     "/refreshes/%s/reveal",
+                     pub_str);
+  }
+  /* finally, we can actually issue the request */
+  rrh = GNUNET_new (struct TALER_EXCHANGE_RefreshesRevealHandle);
+  rrh->exchange = exchange;
+  rrh->noreveal_index = noreveal_index;
+  rrh->reveal_cb = reveal_cb;
+  rrh->reveal_cb_cls = reveal_cb_cls;
+  rrh->md = md;
+  rrh->url = TEAH_path_to_url (rrh->exchange,
+                               arg_str);
+
+  eh = TEL_curl_easy_get (rrh->url);
+  if (GNUNET_OK !=
+      TALER_curl_easy_post (&rrh->ctx,
+                            eh,
+                            reveal_obj))
+  {
+    GNUNET_break (0);
+    curl_easy_cleanup (eh);
+    json_decref (reveal_obj);
+    GNUNET_free (rrh->url);
+    GNUNET_free (rrh);
+    return NULL;
+  }
+  json_decref (reveal_obj);
+  ctx = TEAH_handle_to_context (rrh->exchange);
+  rrh->job = GNUNET_CURL_job_add2 (ctx,
+                                   eh,
+                                   rrh->ctx.headers,
+                                   &handle_refresh_reveal_finished,
+                                   rrh);
+  return rrh;
+}
+
+
+/**
+ * Cancel a refresh reveal request.  This function cannot be used
+ * on a request handle if the callback was already invoked.
+ *
+ * @param rrh the refresh reval handle
+ */
+void
+TALER_EXCHANGE_refreshes_reveal_cancel (struct
+                                        TALER_EXCHANGE_RefreshesRevealHandle *
+                                        rrh)
+{
+  if (NULL != rrh->job)
+  {
+    GNUNET_CURL_job_cancel (rrh->job);
+    rrh->job = NULL;
+  }
+  GNUNET_free (rrh->url);
+  TALER_curl_easy_post_finished (&rrh->ctx);
+  TALER_EXCHANGE_free_melt_data_ (rrh->md); /* does not free 'md' itself */
+  GNUNET_free (rrh->md);
+  GNUNET_free (rrh);
+}
+
+
+/* exchange_api_refreshes_reveal.c */
diff --git a/src/lib/exchange_api_reserve.c b/src/lib/exchange_api_reserve.c
deleted file mode 100644
index 95a4a90e..00000000
--- a/src/lib/exchange_api_reserve.c
+++ /dev/null
@@ -1,1306 +0,0 @@
-/*
-  This file is part of TALER
-  Copyright (C) 2014-2020 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 lib/exchange_api_reserve.c
- * @brief Implementation of the /reserve requests of the exchange's HTTP API
- * @author Christian Grothoff
- */
-#include "platform.h"
-#include <jansson.h>
-#include <microhttpd.h> /* just for HTTP status codes */
-#include <gnunet/gnunet_util_lib.h>
-#include <gnunet/gnunet_json_lib.h>
-#include <gnunet/gnunet_curl_lib.h>
-#include "taler_exchange_service.h"
-#include "taler_json_lib.h"
-#include "exchange_api_handle.h"
-#include "taler_signatures.h"
-#include "exchange_api_curl_defaults.h"
-
-
-/* ********************** /reserve/status ********************** */
-
-/**
- * @brief A Withdraw Status Handle
- */
-struct TALER_EXCHANGE_ReservesGetHandle
-{
-
-  /**
-   * The connection to exchange this request handle will use
-   */
-  struct TALER_EXCHANGE_Handle *exchange;
-
-  /**
-   * The url for this request.
-   */
-  char *url;
-
-  /**
-   * Handle for the request.
-   */
-  struct GNUNET_CURL_Job *job;
-
-  /**
-   * Function to call with the result.
-   */
-  TALER_EXCHANGE_ReservesGetCallback cb;
-
-  /**
-   * Public key of the reserve we are querying.
-   */
-  struct TALER_ReservePublicKeyP reserve_pub;
-
-  /**
-   * Closure for @a cb.
-   */
-  void *cb_cls;
-
-};
-
-
-/**
- * Parse history given in JSON format and return it in binary
- * format.
- *
- * @param exchange connection to the exchange we can use
- * @param history JSON array with the history
- * @param reserve_pub public key of the reserve to inspect
- * @param currency currency we expect the balance to be in
- * @param[out] balance final balance
- * @param history_length number of entries in @a history
- * @param[out] rhistory array of length @a history_length, set to the
- *             parsed history entries
- * @return #GNUNET_OK if history was valid and @a rhistory and @a balance
- *         were set,
- *         #GNUNET_SYSERR if there was a protocol violation in @a history
- */
-static int
-parse_reserve_history (struct TALER_EXCHANGE_Handle *exchange,
-                       const json_t *history,
-                       const struct TALER_ReservePublicKeyP *reserve_pub,
-                       const char *currency,
-                       struct TALER_Amount *balance,
-                       unsigned int history_length,
-                       struct TALER_EXCHANGE_ReserveHistory *rhistory)
-{
-  struct GNUNET_HashCode uuid[history_length];
-  unsigned int uuid_off;
-  struct TALER_Amount total_in;
-  struct TALER_Amount total_out;
-  size_t off;
-
-  GNUNET_assert (GNUNET_OK ==
-                 TALER_amount_get_zero (currency,
-                                        &total_in));
-  GNUNET_assert (GNUNET_OK ==
-                 TALER_amount_get_zero (currency,
-                                        &total_out));
-  uuid_off = 0;
-  for (off = 0; off<history_length; off++)
-  {
-    json_t *transaction;
-    struct TALER_Amount amount;
-    const char *type;
-    struct GNUNET_JSON_Specification hist_spec[] = {
-      GNUNET_JSON_spec_string ("type", &type),
-      TALER_JSON_spec_amount ("amount",
-                              &amount),
-      /* 'wire' and 'signature' are optional depending on 'type'! */
-      GNUNET_JSON_spec_end ()
-    };
-
-    transaction = json_array_get (history,
-                                  off);
-    if (GNUNET_OK !=
-        GNUNET_JSON_parse (transaction,
-                           hist_spec,
-                           NULL, NULL))
-    {
-      GNUNET_break_op (0);
-      return GNUNET_SYSERR;
-    }
-    rhistory[off].amount = amount;
-
-    if (0 == strcasecmp (type,
-                         "DEPOSIT"))
-    {
-      const char *wire_url;
-      void *wire_reference;
-      size_t wire_reference_size;
-      struct GNUNET_TIME_Absolute timestamp;
-
-      struct GNUNET_JSON_Specification withdraw_spec[] = {
-        GNUNET_JSON_spec_varsize ("wire_reference",
-                                  &wire_reference,
-                                  &wire_reference_size),
-        GNUNET_JSON_spec_absolute_time ("timestamp",
-                                        &timestamp),
-        GNUNET_JSON_spec_string ("sender_account_url",
-                                 &wire_url),
-        GNUNET_JSON_spec_end ()
-      };
-
-      rhistory[off].type = TALER_EXCHANGE_RTT_DEPOSIT;
-      if (GNUNET_OK !=
-          TALER_amount_add (&total_in,
-                            &total_in,
-                            &amount))
-      {
-        /* overflow in history already!? inconceivable! Bad exchange! */
-        GNUNET_break_op (0);
-        return GNUNET_SYSERR;
-      }
-      if (GNUNET_OK !=
-          GNUNET_JSON_parse (transaction,
-                             withdraw_spec,
-                             NULL, NULL))
-      {
-        GNUNET_break_op (0);
-        return GNUNET_SYSERR;
-      }
-      rhistory[off].details.in_details.sender_url = GNUNET_strdup (wire_url);
-      rhistory[off].details.in_details.wire_reference = wire_reference;
-      rhistory[off].details.in_details.wire_reference_size =
-        wire_reference_size;
-      rhistory[off].details.in_details.timestamp = timestamp;
-      /* end type==DEPOSIT */
-    }
-    else if (0 == strcasecmp (type,
-                              "WITHDRAW"))
-    {
-      struct TALER_ReserveSignatureP sig;
-      struct TALER_WithdrawRequestPS withdraw_purpose;
-      struct GNUNET_JSON_Specification withdraw_spec[] = {
-        GNUNET_JSON_spec_fixed_auto ("reserve_sig",
-                                     &sig),
-        TALER_JSON_spec_amount_nbo ("withdraw_fee",
-                                    &withdraw_purpose.withdraw_fee),
-        GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
-                                     &withdraw_purpose.h_denomination_pub),
-        GNUNET_JSON_spec_fixed_auto ("h_coin_envelope",
-                                     &withdraw_purpose.h_coin_envelope),
-        GNUNET_JSON_spec_end ()
-      };
-
-      rhistory[off].type = TALER_EXCHANGE_RTT_WITHDRAWAL;
-      if (GNUNET_OK !=
-          GNUNET_JSON_parse (transaction,
-                             withdraw_spec,
-                             NULL, NULL))
-      {
-        GNUNET_break_op (0);
-        return GNUNET_SYSERR;
-      }
-      withdraw_purpose.purpose.size
-        = htonl (sizeof (withdraw_purpose));
-      withdraw_purpose.purpose.purpose
-        = htonl (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW);
-      withdraw_purpose.reserve_pub = *reserve_pub;
-      TALER_amount_hton (&withdraw_purpose.amount_with_fee,
-                         &amount);
-      /* Check that the signature is a valid withdraw request */
-      if (GNUNET_OK !=
-          GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW,
-                                      &withdraw_purpose.purpose,
-                                      &sig.eddsa_signature,
-                                      &reserve_pub->eddsa_pub))
-      {
-        GNUNET_break_op (0);
-        GNUNET_JSON_parse_free (withdraw_spec);
-        return GNUNET_SYSERR;
-      }
-      /* check that withdraw fee matches expectations! */
-      {
-        const struct TALER_EXCHANGE_Keys *key_state;
-        const struct TALER_EXCHANGE_DenomPublicKey *dki;
-        struct TALER_Amount fee;
-
-        key_state = TALER_EXCHANGE_get_keys (exchange);
-        dki = TALER_EXCHANGE_get_denomination_key_by_hash (key_state,
-                                                           &withdraw_purpose.
-                                                           h_denomination_pub);
-        TALER_amount_ntoh (&fee,
-                           &withdraw_purpose.withdraw_fee);
-        if ( (GNUNET_YES !=
-              TALER_amount_cmp_currency (&fee,
-                                         &dki->fee_withdraw)) ||
-             (0 !=
-              TALER_amount_cmp (&fee,
-                                &dki->fee_withdraw)) )
-        {
-          GNUNET_break_op (0);
-          GNUNET_JSON_parse_free (withdraw_spec);
-          return GNUNET_SYSERR;
-        }
-      }
-      rhistory[off].details.out_authorization_sig
-        = json_object_get (transaction,
-                           "signature");
-      /* Check check that the same withdraw transaction
-         isn't listed twice by the exchange. We use the
-         "uuid" array to remember the hashes of all
-         purposes, and compare the hashes to find
-         duplicates. *///
-      GNUNET_CRYPTO_hash (&withdraw_purpose,
-                          ntohl (withdraw_purpose.purpose.size),
-                          &uuid[uuid_off]);
-      for (unsigned int i = 0; i<uuid_off; i++)
-      {
-        if (0 == GNUNET_memcmp (&uuid[uuid_off],
-                                &uuid[i]))
-        {
-          GNUNET_break_op (0);
-          GNUNET_JSON_parse_free (withdraw_spec);
-          return GNUNET_SYSERR;
-        }
-      }
-      uuid_off++;
-
-      if (GNUNET_OK !=
-          TALER_amount_add (&total_out,
-                            &total_out,
-                            &amount))
-      {
-        /* overflow in history already!? inconceivable! Bad exchange! */
-        GNUNET_break_op (0);
-        GNUNET_JSON_parse_free (withdraw_spec);
-        return GNUNET_SYSERR;
-      }
-      /* end type==WITHDRAW */
-    }
-    else if (0 == strcasecmp (type,
-                              "RECOUP"))
-    {
-      struct TALER_RecoupConfirmationPS pc;
-      struct GNUNET_TIME_Absolute timestamp;
-      const struct TALER_EXCHANGE_Keys *key_state;
-      struct GNUNET_JSON_Specification recoup_spec[] = {
-        GNUNET_JSON_spec_fixed_auto ("coin_pub",
-                                     &pc.coin_pub),
-        GNUNET_JSON_spec_fixed_auto ("exchange_sig",
-                                     &rhistory[off].details.recoup_details.
-                                     exchange_sig),
-        GNUNET_JSON_spec_fixed_auto ("exchange_pub",
-                                     &rhistory[off].details.recoup_details.
-                                     exchange_pub),
-        GNUNET_JSON_spec_absolute_time_nbo ("timestamp",
-                                            &pc.timestamp),
-        GNUNET_JSON_spec_end ()
-      };
-
-      rhistory[off].type = TALER_EXCHANGE_RTT_RECOUP;
-      rhistory[off].amount = amount;
-      if (GNUNET_OK !=
-          GNUNET_JSON_parse (transaction,
-                             recoup_spec,
-                             NULL, NULL))
-      {
-        GNUNET_break_op (0);
-        return GNUNET_SYSERR;
-      }
-      rhistory[off].details.recoup_details.coin_pub = pc.coin_pub;
-      TALER_amount_hton (&pc.recoup_amount,
-                         &amount);
-      pc.purpose.size = htonl (sizeof (pc));
-      pc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP);
-      pc.reserve_pub = *reserve_pub;
-      timestamp = GNUNET_TIME_absolute_ntoh (pc.timestamp);
-      rhistory[off].details.recoup_details.timestamp = timestamp;
-
-      key_state = TALER_EXCHANGE_get_keys (exchange);
-      if (GNUNET_OK !=
-          TALER_EXCHANGE_test_signing_key (key_state,
-                                           &rhistory[off].details.
-                                           recoup_details.exchange_pub))
-      {
-        GNUNET_break_op (0);
-        return GNUNET_SYSERR;
-      }
-      if (GNUNET_OK !=
-          GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_EXCHANGE_CONFIRM_RECOUP,
-                                      &pc.purpose,
-                                      &rhistory[off].details.recoup_details.
-                                      exchange_sig.eddsa_signature,
-                                      &rhistory[off].details.recoup_details.
-                                      exchange_pub.eddsa_pub))
-      {
-        GNUNET_break_op (0);
-        return GNUNET_SYSERR;
-      }
-      if (GNUNET_OK !=
-          TALER_amount_add (&total_in,
-                            &total_in,
-                            &rhistory[off].amount))
-      {
-        /* overflow in history already!? inconceivable! Bad exchange! */
-        GNUNET_break_op (0);
-        return GNUNET_SYSERR;
-      }
-      /* end type==RECOUP */
-    }
-    else if (0 == strcasecmp (type,
-                              "CLOSING"))
-    {
-      const struct TALER_EXCHANGE_Keys *key_state;
-      struct TALER_ReserveCloseConfirmationPS rcc;
-      struct GNUNET_TIME_Absolute timestamp;
-      struct GNUNET_JSON_Specification closing_spec[] = {
-        GNUNET_JSON_spec_string ("receiver_account_details",
-                                 &rhistory[off].details.close_details.
-                                 receiver_account_details),
-        GNUNET_JSON_spec_fixed_auto ("wtid",
-                                     
&rhistory[off].details.close_details.wtid),
-        GNUNET_JSON_spec_fixed_auto ("exchange_sig",
-                                     &rhistory[off].details.close_details.
-                                     exchange_sig),
-        GNUNET_JSON_spec_fixed_auto ("exchange_pub",
-                                     &rhistory[off].details.close_details.
-                                     exchange_pub),
-        TALER_JSON_spec_amount_nbo ("closing_fee",
-                                    &rcc.closing_fee),
-        GNUNET_JSON_spec_absolute_time_nbo ("timestamp",
-                                            &rcc.timestamp),
-        GNUNET_JSON_spec_end ()
-      };
-
-      rhistory[off].type = TALER_EXCHANGE_RTT_CLOSE;
-      rhistory[off].amount = amount;
-      if (GNUNET_OK !=
-          GNUNET_JSON_parse (transaction,
-                             closing_spec,
-                             NULL, NULL))
-      {
-        GNUNET_break_op (0);
-        return GNUNET_SYSERR;
-      }
-      TALER_amount_hton (&rcc.closing_amount,
-                         &amount);
-      GNUNET_CRYPTO_hash (
-        rhistory[off].details.close_details.receiver_account_details,
-        strlen (
-          rhistory[off].details.close_details.receiver_account_details) + 1,
-        &rcc.h_wire);
-      rcc.wtid = rhistory[off].details.close_details.wtid;
-      rcc.purpose.size = htonl (sizeof (rcc));
-      rcc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_RESERVE_CLOSED);
-      rcc.reserve_pub = *reserve_pub;
-      timestamp = GNUNET_TIME_absolute_ntoh (rcc.timestamp);
-      rhistory[off].details.close_details.timestamp = timestamp;
-
-      key_state = TALER_EXCHANGE_get_keys (exchange);
-      if (GNUNET_OK !=
-          TALER_EXCHANGE_test_signing_key (key_state,
-                                           
&rhistory[off].details.close_details.
-                                           exchange_pub))
-      {
-        GNUNET_break_op (0);
-        return GNUNET_SYSERR;
-      }
-      if (GNUNET_OK !=
-          GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_EXCHANGE_RESERVE_CLOSED,
-                                      &rcc.purpose,
-                                      &rhistory[off].details.close_details.
-                                      exchange_sig.eddsa_signature,
-                                      &rhistory[off].details.close_details.
-                                      exchange_pub.eddsa_pub))
-      {
-        GNUNET_break_op (0);
-        return GNUNET_SYSERR;
-      }
-      if (GNUNET_OK !=
-          TALER_amount_add (&total_out,
-                            &total_out,
-                            &rhistory[off].amount))
-      {
-        /* overflow in history already!? inconceivable! Bad exchange! */
-        GNUNET_break_op (0);
-        return GNUNET_SYSERR;
-      }
-      /* end type==CLOSING */
-    }
-    else
-    {
-      /* unexpected 'type', protocol incompatibility, complain! */
-      GNUNET_break_op (0);
-      return GNUNET_SYSERR;
-    }
-  }
-
-  /* check balance = total_in - total_out < withdraw-amount */
-  if (GNUNET_SYSERR ==
-      TALER_amount_subtract (balance,
-                             &total_in,
-                             &total_out))
-  {
-    /* total_in < total_out, why did the exchange ever allow this!? */
-    GNUNET_break_op (0);
-    return GNUNET_SYSERR;
-  }
-  return GNUNET_OK;
-}
-
-
-/**
- * Free memory (potentially) allocated by #parse_reserve_history().
- *
- * @param rhistory result to free
- * @param len number of entries in @a rhistory
- */
-static void
-free_rhistory (struct TALER_EXCHANGE_ReserveHistory *rhistory,
-               unsigned int len)
-{
-  for (unsigned int i = 0; i<len; i++)
-  {
-    switch (rhistory[i].type)
-    {
-    case TALER_EXCHANGE_RTT_DEPOSIT:
-      GNUNET_free_non_null (rhistory[i].details.in_details.wire_reference);
-      GNUNET_free_non_null (rhistory[i].details.in_details.sender_url);
-      break;
-    case TALER_EXCHANGE_RTT_WITHDRAWAL:
-      break;
-    case TALER_EXCHANGE_RTT_RECOUP:
-      break;
-    case TALER_EXCHANGE_RTT_CLOSE:
-      // should we free "receiver_account_details" ?
-      break;
-    }
-  }
-  GNUNET_free (rhistory);
-}
-
-
-/**
- * We received an #MHD_HTTP_OK status code. Handle the JSON
- * response.
- *
- * @param rsh handle of the request
- * @param j JSON response
- * @return #GNUNET_OK on success
- */
-static int
-handle_reserve_status_ok (struct TALER_EXCHANGE_ReservesGetHandle *rsh,
-                          const json_t *j)
-{
-  json_t *history;
-  unsigned int len;
-  struct TALER_Amount balance;
-  struct TALER_Amount balance_from_history;
-  struct GNUNET_JSON_Specification spec[] = {
-    TALER_JSON_spec_amount ("balance", &balance),
-    GNUNET_JSON_spec_end ()
-  };
-
-  if (GNUNET_OK !=
-      GNUNET_JSON_parse (j,
-                         spec,
-                         NULL,
-                         NULL))
-  {
-    GNUNET_break_op (0);
-    return GNUNET_SYSERR;
-  }
-  history = json_object_get (j,
-                             "history");
-  if (NULL == history)
-  {
-    GNUNET_break_op (0);
-    return GNUNET_SYSERR;
-  }
-  len = json_array_size (history);
-  {
-    struct TALER_EXCHANGE_ReserveHistory *rhistory;
-
-    rhistory = GNUNET_new_array (len,
-                                 struct TALER_EXCHANGE_ReserveHistory);
-    if (GNUNET_OK !=
-        parse_reserve_history (rsh->exchange,
-                               history,
-                               &rsh->reserve_pub,
-                               balance.currency,
-                               &balance_from_history,
-                               len,
-                               rhistory))
-    {
-      GNUNET_break_op (0);
-      free_rhistory (rhistory,
-                     len);
-      return GNUNET_SYSERR;
-    }
-    if (0 !=
-        TALER_amount_cmp (&balance_from_history,
-                          &balance))
-    {
-      /* exchange cannot add up balances!? */
-      GNUNET_break_op (0);
-      free_rhistory (rhistory,
-                     len);
-      return GNUNET_SYSERR;
-    }
-    if (NULL != rsh->cb)
-    {
-      rsh->cb (rsh->cb_cls,
-               MHD_HTTP_OK,
-               TALER_EC_NONE,
-               j,
-               &balance,
-               len,
-               rhistory);
-      rsh->cb = NULL;
-    }
-    free_rhistory (rhistory,
-                   len);
-  }
-  return GNUNET_OK;
-}
-
-
-/**
- * Function called when we're done processing the
- * HTTP /reserve/status request.
- *
- * @param cls the `struct TALER_EXCHANGE_ReservesGetHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response parsed JSON result, NULL on error
- */
-static void
-handle_reserve_status_finished (void *cls,
-                                long response_code,
-                                const void *response)
-{
-  struct TALER_EXCHANGE_ReservesGetHandle *rsh = cls;
-  const json_t *j = response;
-
-  rsh->job = NULL;
-  switch (response_code)
-  {
-  case 0:
-    break;
-  case MHD_HTTP_OK:
-    if (GNUNET_OK !=
-        handle_reserve_status_ok (rsh,
-                                  j))
-      response_code = 0;
-    break;
-  case MHD_HTTP_BAD_REQUEST:
-    /* This should never happen, either us or the exchange is buggy
-       (or API version conflict); just pass JSON reply to the application */
-    break;
-  case MHD_HTTP_NOT_FOUND:
-    /* Nothing really to verify, this should never
-       happen, we should pass the JSON reply to the application */
-    break;
-  case MHD_HTTP_INTERNAL_SERVER_ERROR:
-    /* Server had an internal issue; we should retry, but this API
-       leaves this to the application */
-    break;
-  default:
-    /* unexpected response code */
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unexpected response code %u\n",
-                (unsigned int) response_code);
-    GNUNET_break (0);
-    response_code = 0;
-    break;
-  }
-  if (NULL != rsh->cb)
-  {
-    rsh->cb (rsh->cb_cls,
-             response_code,
-             TALER_JSON_get_error_code (j),
-             j,
-             NULL,
-             0, NULL);
-    rsh->cb = NULL;
-  }
-  TALER_EXCHANGE_reserves_get_cancel (rsh);
-}
-
-
-/**
- * Submit a request to obtain the transaction history of a reserve
- * from the exchange.  Note that while we return the full response to the
- * caller for further processing, we do already verify that the
- * response is well-formed (i.e. that signatures included in the
- * response are all valid and add up to the balance).  If the exchange's
- * reply is not well-formed, we return an HTTP status code of zero to
- * @a cb.
- *
- * @param exchange the exchange handle; the exchange must be ready to operate
- * @param reserve_pub public key of the reserve to inspect
- * @param cb the callback to call when a reply for this request is available
- * @param cb_cls closure for the above callback
- * @return a handle for this request; NULL if the inputs are invalid (i.e.
- *         signatures fail to verify).  In this case, the callback is not 
called.
- */
-struct TALER_EXCHANGE_ReservesGetHandle *
-TALER_EXCHANGE_reserves_get (struct TALER_EXCHANGE_Handle *exchange,
-                             const struct
-                             TALER_ReservePublicKeyP *reserve_pub,
-                             TALER_EXCHANGE_ReservesGetCallback cb,
-                             void *cb_cls)
-{
-  struct TALER_EXCHANGE_ReservesGetHandle *rsh;
-  struct GNUNET_CURL_Context *ctx;
-  CURL *eh;
-  char arg_str[sizeof (struct TALER_ReservePublicKeyP) * 2 + 16];
-
-  if (GNUNET_YES !=
-      TEAH_handle_is_ready (exchange))
-  {
-    GNUNET_break (0);
-    return NULL;
-  }
-  {
-    char pub_str[sizeof (struct TALER_ReservePublicKeyP) * 2];
-    char *end;
-
-    end = GNUNET_STRINGS_data_to_string (reserve_pub,
-                                         sizeof (struct
-                                                 TALER_ReservePublicKeyP),
-                                         pub_str,
-                                         sizeof (pub_str));
-    *end = '\0';
-    GNUNET_snprintf (arg_str,
-                     sizeof (arg_str),
-                     "/reserves/%s",
-                     pub_str);
-  }
-  rsh = GNUNET_new (struct TALER_EXCHANGE_ReservesGetHandle);
-  rsh->exchange = exchange;
-  rsh->cb = cb;
-  rsh->cb_cls = cb_cls;
-  rsh->reserve_pub = *reserve_pub;
-  rsh->url = TEAH_path_to_url (exchange,
-                               arg_str);
-  eh = TEL_curl_easy_get (rsh->url);
-  ctx = TEAH_handle_to_context (exchange);
-  rsh->job = GNUNET_CURL_job_add (ctx,
-                                  eh,
-                                  GNUNET_NO,
-                                  &handle_reserve_status_finished,
-                                  rsh);
-  return rsh;
-}
-
-
-/**
- * Cancel a reserve status request.  This function cannot be used
- * on a request handle if a response is already served for it.
- *
- * @param rsh the reserve status request handle
- */
-void
-TALER_EXCHANGE_reserves_get_cancel (struct
-                                    TALER_EXCHANGE_ReservesGetHandle *rsh)
-{
-  if (NULL != rsh->job)
-  {
-    GNUNET_CURL_job_cancel (rsh->job);
-    rsh->job = NULL;
-  }
-  GNUNET_free (rsh->url);
-  GNUNET_free (rsh);
-}
-
-
-/* ********************** /reserve/withdraw ********************** */
-
-/**
- * @brief A Withdraw Sign Handle
- */
-struct TALER_EXCHANGE_WithdrawHandle
-{
-
-  /**
-   * The connection to exchange this request handle will use
-   */
-  struct TALER_EXCHANGE_Handle *exchange;
-
-  /**
-   * The url for this request.
-   */
-  char *url;
-
-  /**
-   * Context for #TEH_curl_easy_post(). Keeps the data that must
-   * persist for Curl to make the upload.
-   */
-  struct TALER_CURL_PostContext ctx;
-
-  /**
-   * Handle for the request.
-   */
-  struct GNUNET_CURL_Job *job;
-
-  /**
-   * Function to call with the result.
-   */
-  TALER_EXCHANGE_WithdrawCallback cb;
-
-  /**
-   * Secrets of the planchet.
-   */
-  struct TALER_PlanchetSecretsP ps;
-
-  /**
-   * Denomination key we are withdrawing.
-   */
-  struct TALER_EXCHANGE_DenomPublicKey pk;
-
-  /**
-   * Closure for @a cb.
-   */
-  void *cb_cls;
-
-  /**
-   * Hash of the public key of the coin we are signing.
-   */
-  struct GNUNET_HashCode c_hash;
-
-  /**
-   * Public key of the reserve we are withdrawing from.
-   */
-  struct TALER_ReservePublicKeyP reserve_pub;
-
-};
-
-
-/**
- * We got a 200 OK response for the /reserves/$RESERVE_PUB/withdraw operation.
- * Extract the coin's signature and return it to the caller.  The signature we
- * get from the exchange is for the blinded value.  Thus, we first must
- * unblind it and then should verify its validity against our coin's hash.
- *
- * If everything checks out, we return the unblinded signature
- * to the application via the callback.
- *
- * @param wsh operation handle
- * @param json reply from the exchange
- * @return #GNUNET_OK on success, #GNUNET_SYSERR on errors
- */
-static int
-reserve_withdraw_ok (struct TALER_EXCHANGE_WithdrawHandle *wsh,
-                     const json_t *json)
-{
-  struct GNUNET_CRYPTO_RsaSignature *blind_sig;
-  struct TALER_FreshCoin fc;
-  struct GNUNET_JSON_Specification spec[] = {
-    GNUNET_JSON_spec_rsa_signature ("ev_sig",
-                                    &blind_sig),
-    GNUNET_JSON_spec_end ()
-  };
-
-  if (GNUNET_OK !=
-      GNUNET_JSON_parse (json,
-                         spec,
-                         NULL, NULL))
-  {
-    GNUNET_break_op (0);
-    return GNUNET_SYSERR;
-  }
-  if (GNUNET_OK !=
-      TALER_planchet_to_coin (&wsh->pk.key,
-                              blind_sig,
-                              &wsh->ps,
-                              &wsh->c_hash,
-                              &fc))
-  {
-    GNUNET_break_op (0);
-    GNUNET_JSON_parse_free (spec);
-    return GNUNET_SYSERR;
-  }
-  GNUNET_JSON_parse_free (spec);
-
-  /* signature is valid, return it to the application */
-  wsh->cb (wsh->cb_cls,
-           MHD_HTTP_OK,
-           TALER_EC_NONE,
-           &fc.sig,
-           json);
-  /* make sure callback isn't called again after return */
-  wsh->cb = NULL;
-  GNUNET_CRYPTO_rsa_signature_free (fc.sig.rsa_signature);
-  return GNUNET_OK;
-}
-
-
-/**
- * We got a 409 CONFLICT response for the /reserves/$RESERVE_PUB/withdraw 
operation.
- * Check the signatures on the withdraw transactions in the provided
- * history and that the balances add up.  We don't do anything directly
- * with the information, as the JSON will be returned to the application.
- * However, our job is ensuring that the exchange followed the protocol, and
- * this in particular means checking all of the signatures in the history.
- *
- * @param wsh operation handle
- * @param json reply from the exchange
- * @return #GNUNET_OK on success, #GNUNET_SYSERR on errors
- */
-static int
-reserve_withdraw_payment_required (struct
-                                   TALER_EXCHANGE_WithdrawHandle *wsh,
-                                   const json_t *json)
-{
-  struct TALER_Amount balance;
-  struct TALER_Amount balance_from_history;
-  struct TALER_Amount requested_amount;
-  json_t *history;
-  size_t len;
-  struct GNUNET_JSON_Specification spec[] = {
-    TALER_JSON_spec_amount ("balance", &balance),
-    GNUNET_JSON_spec_end ()
-  };
-
-  if (GNUNET_OK !=
-      GNUNET_JSON_parse (json,
-                         spec,
-                         NULL, NULL))
-  {
-    GNUNET_break_op (0);
-    return GNUNET_SYSERR;
-  }
-  history = json_object_get (json,
-                             "history");
-  if (NULL == history)
-  {
-    GNUNET_break_op (0);
-    return GNUNET_SYSERR;
-  }
-
-  /* go over transaction history and compute
-     total incoming and outgoing amounts */
-  len = json_array_size (history);
-  {
-    struct TALER_EXCHANGE_ReserveHistory *rhistory;
-
-    /* Use heap allocation as "len" may be very big and thus this may
-       not fit on the stack. Use "GNUNET_malloc_large" as a malicious
-       exchange may theoretically try to crash us by giving a history
-       that does not fit into our memory. */
-    rhistory = GNUNET_malloc_large (sizeof (struct
-                                            TALER_EXCHANGE_ReserveHistory)
-                                    * len);
-    if (NULL == rhistory)
-    {
-      GNUNET_break (0);
-      return GNUNET_SYSERR;
-    }
-
-    if (GNUNET_OK !=
-        parse_reserve_history (wsh->exchange,
-                               history,
-                               &wsh->reserve_pub,
-                               balance.currency,
-                               &balance_from_history,
-                               len,
-                               rhistory))
-    {
-      GNUNET_break_op (0);
-      free_rhistory (rhistory,
-                     len);
-      return GNUNET_SYSERR;
-    }
-    free_rhistory (rhistory,
-                   len);
-  }
-
-  if (0 !=
-      TALER_amount_cmp (&balance_from_history,
-                        &balance))
-  {
-    /* exchange cannot add up balances!? */
-    GNUNET_break_op (0);
-    return GNUNET_SYSERR;
-  }
-  /* Compute how much we expected to charge to the reserve */
-  if (GNUNET_OK !=
-      TALER_amount_add (&requested_amount,
-                        &wsh->pk.value,
-                        &wsh->pk.fee_withdraw))
-  {
-    /* Overflow here? Very strange, our CPU must be fried... */
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
-  }
-  /* Check that funds were really insufficient */
-  if (0 >= TALER_amount_cmp (&requested_amount,
-                             &balance))
-  {
-    /* Requested amount is smaller or equal to reported balance,
-       so this should not have failed. */
-    GNUNET_break_op (0);
-    return GNUNET_SYSERR;
-  }
-  return GNUNET_OK;
-}
-
-
-/**
- * Function called when we're done processing the
- * HTTP /reserves/$RESERVE_PUB/withdraw request.
- *
- * @param cls the `struct TALER_EXCHANGE_WithdrawHandle`
- * @param response_code HTTP response code, 0 on error
- * @param response parsed JSON result, NULL on error
- */
-static void
-handle_reserve_withdraw_finished (void *cls,
-                                  long response_code,
-                                  const void *response)
-{
-  struct TALER_EXCHANGE_WithdrawHandle *wsh = cls;
-  const json_t *j = response;
-
-  wsh->job = NULL;
-  switch (response_code)
-  {
-  case 0:
-    break;
-  case MHD_HTTP_OK:
-    if (GNUNET_OK !=
-        reserve_withdraw_ok (wsh,
-                             j))
-    {
-      GNUNET_break_op (0);
-      response_code = 0;
-    }
-    break;
-  case MHD_HTTP_BAD_REQUEST:
-    /* This should never happen, either us or the exchange is buggy
-       (or API version conflict); just pass JSON reply to the application */
-    break;
-  case MHD_HTTP_CONFLICT:
-    /* The exchange says that the reserve has insufficient funds;
-       check the signatures in the history... */
-    if (GNUNET_OK !=
-        reserve_withdraw_payment_required (wsh,
-                                           j))
-    {
-      GNUNET_break_op (0);
-      response_code = 0;
-    }
-    break;
-  case MHD_HTTP_FORBIDDEN:
-    GNUNET_break (0);
-    /* Nothing really to verify, exchange says one of the signatures is
-       invalid; as we checked them, this should never happen, we
-       should pass the JSON reply to the application */
-    break;
-  case MHD_HTTP_NOT_FOUND:
-    /* Nothing really to verify, the exchange basically just says
-       that it doesn't know this reserve.  Can happen if we
-       query before the wire transfer went through.
-       We should simply pass the JSON reply to the application. */
-    break;
-  case MHD_HTTP_INTERNAL_SERVER_ERROR:
-    /* Server had an internal issue; we should retry, but this API
-       leaves this to the application */
-    break;
-  default:
-    /* unexpected response code */
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Unexpected response code %u\n",
-                (unsigned int) response_code);
-    GNUNET_break (0);
-    response_code = 0;
-    break;
-  }
-  if (NULL != wsh->cb)
-  {
-    wsh->cb (wsh->cb_cls,
-             response_code,
-             TALER_JSON_get_error_code (j),
-             NULL,
-             j);
-    wsh->cb = NULL;
-  }
-  TALER_EXCHANGE_withdraw_cancel (wsh);
-}
-
-
-/**
- * Helper function for #TALER_EXCHANGE_withdraw2() and
- * #TALER_EXCHANGE_withdraw().
- *
- * @param exchange the exchange handle; the exchange must be ready to operate
- * @param pk kind of coin to create
- * @param reserve_sig signature from the reserve authorizing the withdrawal
- * @param reserve_pub public key of the reserve to withdraw from
- * @param ps secrets of the planchet
- *        caller must have committed this value to disk before the call (with 
@a pk)
- * @param pd planchet details matching @a ps
- * @param res_cb the callback to call when the final result for this request 
is available
- * @param res_cb_cls closure for @a res_cb
- * @return NULL
- *         if the inputs are invalid (i.e. denomination key not with this 
exchange).
- *         In this case, the callback is not called.
- */
-struct TALER_EXCHANGE_WithdrawHandle *
-reserve_withdraw_internal (struct TALER_EXCHANGE_Handle *exchange,
-                           const struct TALER_EXCHANGE_DenomPublicKey *pk,
-                           const struct TALER_ReserveSignatureP *reserve_sig,
-                           const struct TALER_ReservePublicKeyP *reserve_pub,
-                           const struct TALER_PlanchetSecretsP *ps,
-                           const struct TALER_PlanchetDetail *pd,
-                           TALER_EXCHANGE_WithdrawCallback res_cb,
-                           void *res_cb_cls)
-{
-  struct TALER_EXCHANGE_WithdrawHandle *wsh;
-  struct GNUNET_CURL_Context *ctx;
-  json_t *withdraw_obj;
-  CURL *eh;
-  struct GNUNET_HashCode h_denom_pub;
-  char arg_str[sizeof (struct TALER_ReservePublicKeyP) * 2 + 32];
-
-  {
-    char pub_str[sizeof (struct TALER_ReservePublicKeyP) * 2];
-    char *end;
-
-    end = GNUNET_STRINGS_data_to_string (reserve_pub,
-                                         sizeof (struct
-                                                 TALER_ReservePublicKeyP),
-                                         pub_str,
-                                         sizeof (pub_str));
-    *end = '\0';
-    GNUNET_snprintf (arg_str,
-                     sizeof (arg_str),
-                     "/reserves/%s/withdraw",
-                     pub_str);
-  }
-  wsh = GNUNET_new (struct TALER_EXCHANGE_WithdrawHandle);
-  wsh->exchange = exchange;
-  wsh->cb = res_cb;
-  wsh->cb_cls = res_cb_cls;
-  wsh->pk = *pk;
-  wsh->pk.key.rsa_public_key
-    = GNUNET_CRYPTO_rsa_public_key_dup (pk->key.rsa_public_key);
-  wsh->reserve_pub = *reserve_pub;
-  wsh->c_hash = pd->c_hash;
-  GNUNET_CRYPTO_rsa_public_key_hash (pk->key.rsa_public_key,
-                                     &h_denom_pub);
-  withdraw_obj = json_pack ("{s:o, s:o," /* denom_pub_hash and coin_ev */
-                            " s:o}",/* reserve_pub and reserve_sig */
-                            "denom_pub_hash", GNUNET_JSON_from_data_auto (
-                              &h_denom_pub),
-                            "coin_ev", GNUNET_JSON_from_data (pd->coin_ev,
-                                                              
pd->coin_ev_size),
-                            "reserve_sig", GNUNET_JSON_from_data_auto (
-                              reserve_sig));
-  if (NULL == withdraw_obj)
-  {
-    GNUNET_break (0);
-    GNUNET_CRYPTO_rsa_public_key_free (wsh->pk.key.rsa_public_key);
-    GNUNET_free (wsh);
-    return NULL;
-  }
-  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              "Attempting to withdraw from reserve %s\n",
-              TALER_B2S (reserve_pub));
-  wsh->ps = *ps;
-  wsh->url = TEAH_path_to_url (exchange,
-                               arg_str);
-  eh = TEL_curl_easy_get (wsh->url);
-  if (GNUNET_OK !=
-      TALER_curl_easy_post (&wsh->ctx,
-                            eh,
-                            withdraw_obj))
-  {
-    GNUNET_break (0);
-    curl_easy_cleanup (eh);
-    json_decref (withdraw_obj);
-    GNUNET_free (wsh->url);
-    GNUNET_CRYPTO_rsa_public_key_free (wsh->pk.key.rsa_public_key);
-    GNUNET_free (wsh);
-    return NULL;
-  }
-  json_decref (withdraw_obj);
-  ctx = TEAH_handle_to_context (exchange);
-  wsh->job = GNUNET_CURL_job_add2 (ctx,
-                                   eh,
-                                   wsh->ctx.headers,
-                                   &handle_reserve_withdraw_finished,
-                                   wsh);
-  return wsh;
-}
-
-
-/**
- * Withdraw a coin from the exchange using a /reserve/withdraw request.  Note
- * that to ensure that no money is lost in case of hardware failures,
- * the caller must have committed (most of) the arguments to disk
- * before calling, and be ready to repeat the request with the same
- * arguments in case of failures.
- *
- * @param exchange the exchange handle; the exchange must be ready to operate
- * @param pk kind of coin to create
- * @param reserve_priv private key of the reserve to withdraw from
- * @param ps secrets of the planchet
- *        caller must have committed this value to disk before the call (with 
@a pk)
- * @param res_cb the callback to call when the final result for this request 
is available
- * @param res_cb_cls closure for the above callback
- * @return handle for the operation on success, NULL on error, i.e.
- *         if the inputs are invalid (i.e. denomination key not with this 
exchange).
- *         In this case, the callback is not called.
- */
-struct TALER_EXCHANGE_WithdrawHandle *
-TALER_EXCHANGE_withdraw (struct TALER_EXCHANGE_Handle *exchange,
-                         const struct TALER_EXCHANGE_DenomPublicKey *pk,
-                         const struct
-                         TALER_ReservePrivateKeyP *reserve_priv,
-                         const struct TALER_PlanchetSecretsP *ps,
-                         TALER_EXCHANGE_WithdrawCallback
-                         res_cb,
-                         void *res_cb_cls)
-{
-  struct TALER_Amount amount_with_fee;
-  struct TALER_ReserveSignatureP reserve_sig;
-  struct TALER_WithdrawRequestPS req;
-  struct TALER_PlanchetDetail pd;
-  struct TALER_EXCHANGE_WithdrawHandle *wsh;
-
-  GNUNET_CRYPTO_eddsa_key_get_public (&reserve_priv->eddsa_priv,
-                                      &req.reserve_pub.eddsa_pub);
-  req.purpose.size = htonl (sizeof (struct TALER_WithdrawRequestPS));
-  req.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW);
-  if (GNUNET_OK !=
-      TALER_amount_add (&amount_with_fee,
-                        &pk->fee_withdraw,
-                        &pk->value))
-  {
-    /* exchange gave us denomination keys that overflow like this!? */
-    GNUNET_break_op (0);
-    return NULL;
-  }
-  TALER_amount_hton (&req.amount_with_fee,
-                     &amount_with_fee);
-  TALER_amount_hton (&req.withdraw_fee,
-                     &pk->fee_withdraw);
-  if (GNUNET_OK !=
-      TALER_planchet_prepare (&pk->key,
-                              ps,
-                              &pd))
-  {
-    GNUNET_break_op (0);
-    return NULL;
-  }
-  req.h_denomination_pub = pd.denom_pub_hash;
-  GNUNET_CRYPTO_hash (pd.coin_ev,
-                      pd.coin_ev_size,
-                      &req.h_coin_envelope);
-  GNUNET_assert (GNUNET_OK ==
-                 GNUNET_CRYPTO_eddsa_sign (&reserve_priv->eddsa_priv,
-                                           &req.purpose,
-                                           &reserve_sig.eddsa_signature));
-  wsh = reserve_withdraw_internal (exchange,
-                                   pk,
-                                   &reserve_sig,
-                                   &req.reserve_pub,
-                                   ps,
-                                   &pd,
-                                   res_cb,
-                                   res_cb_cls);
-  GNUNET_free (pd.coin_ev);
-  return wsh;
-}
-
-
-/**
- * Withdraw a coin from the exchange using a /reserve/withdraw
- * request.  This API is typically used by a wallet to withdraw a tip
- * where the reserve's signature was created by the merchant already.
- *
- * Note that to ensure that no money is lost in case of hardware
- * failures, the caller must have committed (most of) the arguments to
- * disk before calling, and be ready to repeat the request with the
- * same arguments in case of failures.
- *
- * @param exchange the exchange handle; the exchange must be ready to operate
- * @param pk kind of coin to create
- * @param reserve_sig signature from the reserve authorizing the withdrawal
- * @param reserve_pub public key of the reserve to withdraw from
- * @param ps secrets of the planchet
- *        caller must have committed this value to disk before the call (with 
@a pk)
- * @param res_cb the callback to call when the final result for this request 
is available
- * @param res_cb_cls closure for @a res_cb
- * @return NULL
- *         if the inputs are invalid (i.e. denomination key not with this 
exchange).
- *         In this case, the callback is not called.
- */
-struct TALER_EXCHANGE_WithdrawHandle *
-TALER_EXCHANGE_withdraw2 (struct TALER_EXCHANGE_Handle *exchange,
-                          const struct
-                          TALER_EXCHANGE_DenomPublicKey *pk,
-                          const struct
-                          TALER_ReserveSignatureP *reserve_sig,
-                          const struct
-                          TALER_ReservePublicKeyP *reserve_pub,
-                          const struct TALER_PlanchetSecretsP *ps,
-                          TALER_EXCHANGE_WithdrawCallback
-                          res_cb,
-                          void *res_cb_cls)
-{
-  struct TALER_EXCHANGE_WithdrawHandle *wsh;
-  struct TALER_PlanchetDetail pd;
-
-  if (GNUNET_OK !=
-      TALER_planchet_prepare (&pk->key,
-                              ps,
-                              &pd))
-  {
-    GNUNET_break_op (0);
-    return NULL;
-  }
-  wsh = reserve_withdraw_internal (exchange,
-                                   pk,
-                                   reserve_sig,
-                                   reserve_pub,
-                                   ps,
-                                   &pd,
-                                   res_cb,
-                                   res_cb_cls);
-  GNUNET_free (pd.coin_ev);
-  return wsh;
-}
-
-
-/**
- * Cancel a withdraw status request.  This function cannot be used
- * on a request handle if a response is already served for it.
- *
- * @param sign the withdraw sign request handle
- */
-void
-TALER_EXCHANGE_withdraw_cancel (struct
-                                TALER_EXCHANGE_WithdrawHandle *
-                                sign)
-{
-  if (NULL != sign->job)
-  {
-    GNUNET_CURL_job_cancel (sign->job);
-    sign->job = NULL;
-  }
-  GNUNET_free (sign->url);
-  TALER_curl_easy_post_finished (&sign->ctx);
-  GNUNET_CRYPTO_rsa_public_key_free (sign->pk.key.rsa_public_key);
-  GNUNET_free (sign);
-}
-
-
-/* end of exchange_api_reserve.c */
diff --git a/src/lib/exchange_api_reserves_get.c 
b/src/lib/exchange_api_reserves_get.c
new file mode 100644
index 00000000..62e28f05
--- /dev/null
+++ b/src/lib/exchange_api_reserves_get.c
@@ -0,0 +1,308 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2014-2020 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 lib/exchange_api_reserves_get.c
+ * @brief Implementation of the GET /reserves/$RESERVE_PUB requests
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <jansson.h>
+#include <microhttpd.h> /* just for HTTP status codes */
+#include <gnunet/gnunet_util_lib.h>
+#include <gnunet/gnunet_json_lib.h>
+#include <gnunet/gnunet_curl_lib.h>
+#include "taler_exchange_service.h"
+#include "taler_json_lib.h"
+#include "exchange_api_handle.h"
+#include "taler_signatures.h"
+#include "exchange_api_curl_defaults.h"
+
+
+/**
+ * @brief A /reserves/ GET Handle
+ */
+struct TALER_EXCHANGE_ReservesGetHandle
+{
+
+  /**
+   * The connection to exchange this request handle will use
+   */
+  struct TALER_EXCHANGE_Handle *exchange;
+
+  /**
+   * The url for this request.
+   */
+  char *url;
+
+  /**
+   * Handle for the request.
+   */
+  struct GNUNET_CURL_Job *job;
+
+  /**
+   * Function to call with the result.
+   */
+  TALER_EXCHANGE_ReservesGetCallback cb;
+
+  /**
+   * Public key of the reserve we are querying.
+   */
+  struct TALER_ReservePublicKeyP reserve_pub;
+
+  /**
+   * Closure for @a cb.
+   */
+  void *cb_cls;
+
+};
+
+
+/**
+ * We received an #MHD_HTTP_OK status code. Handle the JSON
+ * response.
+ *
+ * @param rgh handle of the request
+ * @param j JSON response
+ * @return #GNUNET_OK on success
+ */
+static int
+handle_reserves_get_ok (struct TALER_EXCHANGE_ReservesGetHandle *rgh,
+                        const json_t *j)
+{
+  json_t *history;
+  unsigned int len;
+  struct TALER_Amount balance;
+  struct TALER_Amount balance_from_history;
+  struct GNUNET_JSON_Specification spec[] = {
+    TALER_JSON_spec_amount ("balance", &balance),
+    GNUNET_JSON_spec_end ()
+  };
+
+  if (GNUNET_OK !=
+      GNUNET_JSON_parse (j,
+                         spec,
+                         NULL,
+                         NULL))
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  history = json_object_get (j,
+                             "history");
+  if (NULL == history)
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  len = json_array_size (history);
+  {
+    struct TALER_EXCHANGE_ReserveHistory *rhistory;
+
+    rhistory = GNUNET_new_array (len,
+                                 struct TALER_EXCHANGE_ReserveHistory);
+    if (GNUNET_OK !=
+        TALER_EXCHANGE_parse_reserve_history (rgh->exchange,
+                                              history,
+                                              &rgh->reserve_pub,
+                                              balance.currency,
+                                              &balance_from_history,
+                                              len,
+                                              rhistory))
+    {
+      GNUNET_break_op (0);
+      TALER_EXCHANGE_free_reserve_history (rhistory,
+                                           len);
+      return GNUNET_SYSERR;
+    }
+    if (0 !=
+        TALER_amount_cmp (&balance_from_history,
+                          &balance))
+    {
+      /* exchange cannot add up balances!? */
+      GNUNET_break_op (0);
+      TALER_EXCHANGE_free_reserve_history (rhistory,
+                                           len);
+      return GNUNET_SYSERR;
+    }
+    if (NULL != rgh->cb)
+    {
+      rgh->cb (rgh->cb_cls,
+               MHD_HTTP_OK,
+               TALER_EC_NONE,
+               j,
+               &balance,
+               len,
+               rhistory);
+      rgh->cb = NULL;
+    }
+    TALER_EXCHANGE_free_reserve_history (rhistory,
+                                         len);
+  }
+  return GNUNET_OK;
+}
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP /reserves/ GET request.
+ *
+ * @param cls the `struct TALER_EXCHANGE_ReservesGetHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response parsed JSON result, NULL on error
+ */
+static void
+handle_reserves_get_finished (void *cls,
+                              long response_code,
+                              const void *response)
+{
+  struct TALER_EXCHANGE_ReservesGetHandle *rgh = cls;
+  const json_t *j = response;
+
+  rgh->job = NULL;
+  switch (response_code)
+  {
+  case 0:
+    break;
+  case MHD_HTTP_OK:
+    if (GNUNET_OK !=
+        handle_reserves_get_ok (rgh,
+                                j))
+      response_code = 0;
+    break;
+  case MHD_HTTP_BAD_REQUEST:
+    /* This should never happen, either us or the exchange is buggy
+       (or API version conflict); just pass JSON reply to the application */
+    break;
+  case MHD_HTTP_NOT_FOUND:
+    /* Nothing really to verify, this should never
+       happen, we should pass the JSON reply to the application */
+    break;
+  case MHD_HTTP_INTERNAL_SERVER_ERROR:
+    /* Server had an internal issue; we should retry, but this API
+       leaves this to the application */
+    break;
+  default:
+    /* unexpected response code */
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Unexpected response code %u\n",
+                (unsigned int) response_code);
+    GNUNET_break (0);
+    response_code = 0;
+    break;
+  }
+  if (NULL != rgh->cb)
+  {
+    rgh->cb (rgh->cb_cls,
+             response_code,
+             TALER_JSON_get_error_code (j),
+             j,
+             NULL,
+             0, NULL);
+    rgh->cb = NULL;
+  }
+  TALER_EXCHANGE_reserves_get_cancel (rgh);
+}
+
+
+/**
+ * Submit a request to obtain the transaction history of a reserve
+ * from the exchange.  Note that while we return the full response to the
+ * caller for further processing, we do already verify that the
+ * response is well-formed (i.e. that signatures included in the
+ * response are all valid and add up to the balance).  If the exchange's
+ * reply is not well-formed, we return an HTTP status code of zero to
+ * @a cb.
+ *
+ * @param exchange the exchange handle; the exchange must be ready to operate
+ * @param reserve_pub public key of the reserve to inspect
+ * @param cb the callback to call when a reply for this request is available
+ * @param cb_cls closure for the above callback
+ * @return a handle for this request; NULL if the inputs are invalid (i.e.
+ *         signatures fail to verify).  In this case, the callback is not 
called.
+ */
+struct TALER_EXCHANGE_ReservesGetHandle *
+TALER_EXCHANGE_reserves_get (struct TALER_EXCHANGE_Handle *exchange,
+                             const struct
+                             TALER_ReservePublicKeyP *reserve_pub,
+                             TALER_EXCHANGE_ReservesGetCallback cb,
+                             void *cb_cls)
+{
+  struct TALER_EXCHANGE_ReservesGetHandle *rgh;
+  struct GNUNET_CURL_Context *ctx;
+  CURL *eh;
+  char arg_str[sizeof (struct TALER_ReservePublicKeyP) * 2 + 16];
+
+  if (GNUNET_YES !=
+      TEAH_handle_is_ready (exchange))
+  {
+    GNUNET_break (0);
+    return NULL;
+  }
+  {
+    char pub_str[sizeof (struct TALER_ReservePublicKeyP) * 2];
+    char *end;
+
+    end = GNUNET_STRINGS_data_to_string (reserve_pub,
+                                         sizeof (struct
+                                                 TALER_ReservePublicKeyP),
+                                         pub_str,
+                                         sizeof (pub_str));
+    *end = '\0';
+    GNUNET_snprintf (arg_str,
+                     sizeof (arg_str),
+                     "/reserves/%s",
+                     pub_str);
+  }
+  rgh = GNUNET_new (struct TALER_EXCHANGE_ReservesGetHandle);
+  rgh->exchange = exchange;
+  rgh->cb = cb;
+  rgh->cb_cls = cb_cls;
+  rgh->reserve_pub = *reserve_pub;
+  rgh->url = TEAH_path_to_url (exchange,
+                               arg_str);
+  eh = TEL_curl_easy_get (rgh->url);
+  ctx = TEAH_handle_to_context (exchange);
+  rgh->job = GNUNET_CURL_job_add (ctx,
+                                  eh,
+                                  GNUNET_NO,
+                                  &handle_reserves_get_finished,
+                                  rgh);
+  return rgh;
+}
+
+
+/**
+ * Cancel a reserve status request.  This function cannot be used
+ * on a request handle if a response is already served for it.
+ *
+ * @param rgh the reserve status request handle
+ */
+void
+TALER_EXCHANGE_reserves_get_cancel (struct
+                                    TALER_EXCHANGE_ReservesGetHandle *rgh)
+{
+  if (NULL != rgh->job)
+  {
+    GNUNET_CURL_job_cancel (rgh->job);
+    rgh->job = NULL;
+  }
+  GNUNET_free (rgh->url);
+  GNUNET_free (rgh);
+}
+
+
+/* end of exchange_api_reserve.c */
diff --git a/src/lib/exchange_api_track_transfer.c 
b/src/lib/exchange_api_transfers_get.c
similarity index 93%
rename from src/lib/exchange_api_track_transfer.c
rename to src/lib/exchange_api_transfers_get.c
index c5d484d2..8ea8918c 100644
--- a/src/lib/exchange_api_track_transfer.c
+++ b/src/lib/exchange_api_transfers_get.c
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  Copyright (C) 2014, 2015, 2016 Taler Systems SA
+  Copyright (C) 2014-2020 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
@@ -15,8 +15,8 @@
   <http://www.gnu.org/licenses/>
 */
 /**
- * @file lib/exchange_api_track_transfer.c
- * @brief Implementation of the /track/transfer request of the exchange's HTTP 
API
+ * @file lib/exchange_api_transfers_get.c
+ * @brief Implementation of the GET /transfers/ request
  * @author Christian Grothoff
  */
 #include "platform.h"
@@ -32,7 +32,7 @@
 
 
 /**
- * @brief A /track/transfer Handle
+ * @brief A /transfers/ GET Handle
  */
 struct TALER_EXCHANGE_TransfersGetHandle
 {
@@ -66,12 +66,12 @@ struct TALER_EXCHANGE_TransfersGetHandle
 
 
 /**
- * We got a #MHD_HTTP_OK response for the /track/transfer request.
+ * We got a #MHD_HTTP_OK response for the /transfers/ request.
  * Check that the response is well-formed and if it is, call the
  * callback.  If not, return an error code.
  *
  * This code is very similar to
- * merchant_api_track_transfer.c::check_track_transfer_response_ok.
+ * merchant_api_track_transfer.c::check_transfers_get_response_ok.
  * Any changes should likely be reflected there as well.
  *
  * @param wdh handle to the operation
@@ -80,9 +80,9 @@ struct TALER_EXCHANGE_TransfersGetHandle
  *         #GNUNET_SYSERR if the response was bogus
  */
 static int
-check_track_transfer_response_ok (struct
-                                  TALER_EXCHANGE_TransfersGetHandle *wdh,
-                                  const json_t *json)
+check_transfers_get_response_ok (struct
+                                 TALER_EXCHANGE_TransfersGetHandle *wdh,
+                                 const json_t *json)
 {
   json_t *details_j;
   struct GNUNET_HashCode h_wire;
@@ -248,16 +248,16 @@ check_track_transfer_response_ok (struct
 
 /**
  * Function called when we're done processing the
- * HTTP /track/transfer request.
+ * HTTP /transfers/ request.
  *
  * @param cls the `struct TALER_EXCHANGE_TransfersGetHandle`
  * @param response_code HTTP response code, 0 on error
  * @param response parsed JSON result, NULL on error
  */
 static void
-handle_track_transfer_finished (void *cls,
-                                long response_code,
-                                const void *response)
+handle_transfers_get_finished (void *cls,
+                               long response_code,
+                               const void *response)
 {
   struct TALER_EXCHANGE_TransfersGetHandle *wdh = cls;
   const json_t *j = response;
@@ -269,8 +269,8 @@ handle_track_transfer_finished (void *cls,
     break;
   case MHD_HTTP_OK:
     if (GNUNET_OK ==
-        check_track_transfer_response_ok (wdh,
-                                          j))
+        check_transfers_get_response_ok (wdh,
+                                         j))
       return;
     GNUNET_break_op (0);
     response_code = 0;
@@ -371,7 +371,7 @@ TALER_EXCHANGE_transfers_get (struct TALER_EXCHANGE_Handle 
*exchange,
   wdh->job = GNUNET_CURL_job_add (ctx,
                                   eh,
                                   GNUNET_YES,
-                                  &handle_track_transfer_finished,
+                                  &handle_transfers_get_finished,
                                   wdh);
   return wdh;
 }
diff --git a/src/lib/exchange_api_withdraw.c b/src/lib/exchange_api_withdraw.c
new file mode 100644
index 00000000..c6323537
--- /dev/null
+++ b/src/lib/exchange_api_withdraw.c
@@ -0,0 +1,611 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2014-2020 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 lib/exchange_api_withdraw.c
+ * @brief Implementation of the /reserves/$RESERVE_PUB/withdraw requests
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <jansson.h>
+#include <microhttpd.h> /* just for HTTP status codes */
+#include <gnunet/gnunet_util_lib.h>
+#include <gnunet/gnunet_json_lib.h>
+#include <gnunet/gnunet_curl_lib.h>
+#include "taler_exchange_service.h"
+#include "taler_json_lib.h"
+#include "exchange_api_handle.h"
+#include "taler_signatures.h"
+#include "exchange_api_curl_defaults.h"
+
+
+/**
+ * @brief A Withdraw Handle
+ */
+struct TALER_EXCHANGE_WithdrawHandle
+{
+
+  /**
+   * The connection to exchange this request handle will use
+   */
+  struct TALER_EXCHANGE_Handle *exchange;
+
+  /**
+   * The url for this request.
+   */
+  char *url;
+
+  /**
+   * Context for #TEH_curl_easy_post(). Keeps the data that must
+   * persist for Curl to make the upload.
+   */
+  struct TALER_CURL_PostContext ctx;
+
+  /**
+   * Handle for the request.
+   */
+  struct GNUNET_CURL_Job *job;
+
+  /**
+   * Function to call with the result.
+   */
+  TALER_EXCHANGE_WithdrawCallback cb;
+
+  /**
+   * Secrets of the planchet.
+   */
+  struct TALER_PlanchetSecretsP ps;
+
+  /**
+   * Denomination key we are withdrawing.
+   */
+  struct TALER_EXCHANGE_DenomPublicKey pk;
+
+  /**
+   * Closure for @a cb.
+   */
+  void *cb_cls;
+
+  /**
+   * Hash of the public key of the coin we are signing.
+   */
+  struct GNUNET_HashCode c_hash;
+
+  /**
+   * Public key of the reserve we are withdrawing from.
+   */
+  struct TALER_ReservePublicKeyP reserve_pub;
+
+};
+
+
+/**
+ * We got a 200 OK response for the /reserves/$RESERVE_PUB/withdraw operation.
+ * Extract the coin's signature and return it to the caller.  The signature we
+ * get from the exchange is for the blinded value.  Thus, we first must
+ * unblind it and then should verify its validity against our coin's hash.
+ *
+ * If everything checks out, we return the unblinded signature
+ * to the application via the callback.
+ *
+ * @param wh operation handle
+ * @param json reply from the exchange
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on errors
+ */
+static int
+reserve_withdraw_ok (struct TALER_EXCHANGE_WithdrawHandle *wh,
+                     const json_t *json)
+{
+  struct GNUNET_CRYPTO_RsaSignature *blind_sig;
+  struct TALER_FreshCoin fc;
+  struct GNUNET_JSON_Specification spec[] = {
+    GNUNET_JSON_spec_rsa_signature ("ev_sig",
+                                    &blind_sig),
+    GNUNET_JSON_spec_end ()
+  };
+
+  if (GNUNET_OK !=
+      GNUNET_JSON_parse (json,
+                         spec,
+                         NULL, NULL))
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  if (GNUNET_OK !=
+      TALER_planchet_to_coin (&wh->pk.key,
+                              blind_sig,
+                              &wh->ps,
+                              &wh->c_hash,
+                              &fc))
+  {
+    GNUNET_break_op (0);
+    GNUNET_JSON_parse_free (spec);
+    return GNUNET_SYSERR;
+  }
+  GNUNET_JSON_parse_free (spec);
+
+  /* signature is valid, return it to the application */
+  wh->cb (wh->cb_cls,
+          MHD_HTTP_OK,
+          TALER_EC_NONE,
+          &fc.sig,
+          json);
+  /* make sure callback isn't called again after return */
+  wh->cb = NULL;
+  GNUNET_CRYPTO_rsa_signature_free (fc.sig.rsa_signature);
+  return GNUNET_OK;
+}
+
+
+/**
+ * We got a 409 CONFLICT response for the /reserves/$RESERVE_PUB/withdraw 
operation.
+ * Check the signatures on the withdraw transactions in the provided
+ * history and that the balances add up.  We don't do anything directly
+ * with the information, as the JSON will be returned to the application.
+ * However, our job is ensuring that the exchange followed the protocol, and
+ * this in particular means checking all of the signatures in the history.
+ *
+ * @param wh operation handle
+ * @param json reply from the exchange
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on errors
+ */
+static int
+reserve_withdraw_payment_required (struct
+                                   TALER_EXCHANGE_WithdrawHandle *wh,
+                                   const json_t *json)
+{
+  struct TALER_Amount balance;
+  struct TALER_Amount balance_from_history;
+  struct TALER_Amount requested_amount;
+  json_t *history;
+  size_t len;
+  struct GNUNET_JSON_Specification spec[] = {
+    TALER_JSON_spec_amount ("balance", &balance),
+    GNUNET_JSON_spec_end ()
+  };
+
+  if (GNUNET_OK !=
+      GNUNET_JSON_parse (json,
+                         spec,
+                         NULL, NULL))
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  history = json_object_get (json,
+                             "history");
+  if (NULL == history)
+  {
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+
+  /* go over transaction history and compute
+     total incoming and outgoing amounts */
+  len = json_array_size (history);
+  {
+    struct TALER_EXCHANGE_ReserveHistory *rhistory;
+
+    /* Use heap allocation as "len" may be very big and thus this may
+       not fit on the stack. Use "GNUNET_malloc_large" as a malicious
+       exchange may theoretically try to crash us by giving a history
+       that does not fit into our memory. */
+    rhistory = GNUNET_malloc_large (sizeof (struct
+                                            TALER_EXCHANGE_ReserveHistory)
+                                    * len);
+    if (NULL == rhistory)
+    {
+      GNUNET_break (0);
+      return GNUNET_SYSERR;
+    }
+
+    if (GNUNET_OK !=
+        TALER_EXCHANGE_parse_reserve_history (wh->exchange,
+                                              history,
+                                              &wh->reserve_pub,
+                                              balance.currency,
+                                              &balance_from_history,
+                                              len,
+                                              rhistory))
+    {
+      GNUNET_break_op (0);
+      TALER_EXCHANGE_free_reserve_history (rhistory,
+                                           len);
+      return GNUNET_SYSERR;
+    }
+    TALER_EXCHANGE_free_reserve_history (rhistory,
+                                         len);
+  }
+
+  if (0 !=
+      TALER_amount_cmp (&balance_from_history,
+                        &balance))
+  {
+    /* exchange cannot add up balances!? */
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  /* Compute how much we expected to charge to the reserve */
+  if (GNUNET_OK !=
+      TALER_amount_add (&requested_amount,
+                        &wh->pk.value,
+                        &wh->pk.fee_withdraw))
+  {
+    /* Overflow here? Very strange, our CPU must be fried... */
+    GNUNET_break (0);
+    return GNUNET_SYSERR;
+  }
+  /* Check that funds were really insufficient */
+  if (0 >= TALER_amount_cmp (&requested_amount,
+                             &balance))
+  {
+    /* Requested amount is smaller or equal to reported balance,
+       so this should not have failed. */
+    GNUNET_break_op (0);
+    return GNUNET_SYSERR;
+  }
+  return GNUNET_OK;
+}
+
+
+/**
+ * Function called when we're done processing the
+ * HTTP /reserves/$RESERVE_PUB/withdraw request.
+ *
+ * @param cls the `struct TALER_EXCHANGE_WithdrawHandle`
+ * @param response_code HTTP response code, 0 on error
+ * @param response parsed JSON result, NULL on error
+ */
+static void
+handle_reserve_withdraw_finished (void *cls,
+                                  long response_code,
+                                  const void *response)
+{
+  struct TALER_EXCHANGE_WithdrawHandle *wh = cls;
+  const json_t *j = response;
+
+  wh->job = NULL;
+  switch (response_code)
+  {
+  case 0:
+    break;
+  case MHD_HTTP_OK:
+    if (GNUNET_OK !=
+        reserve_withdraw_ok (wh,
+                             j))
+    {
+      GNUNET_break_op (0);
+      response_code = 0;
+    }
+    break;
+  case MHD_HTTP_BAD_REQUEST:
+    /* This should never happen, either us or the exchange is buggy
+       (or API version conflict); just pass JSON reply to the application */
+    break;
+  case MHD_HTTP_CONFLICT:
+    /* The exchange says that the reserve has insufficient funds;
+       check the signatures in the history... */
+    if (GNUNET_OK !=
+        reserve_withdraw_payment_required (wh,
+                                           j))
+    {
+      GNUNET_break_op (0);
+      response_code = 0;
+    }
+    break;
+  case MHD_HTTP_FORBIDDEN:
+    GNUNET_break (0);
+    /* Nothing really to verify, exchange says one of the signatures is
+       invalid; as we checked them, this should never happen, we
+       should pass the JSON reply to the application */
+    break;
+  case MHD_HTTP_NOT_FOUND:
+    /* Nothing really to verify, the exchange basically just says
+       that it doesn't know this reserve.  Can happen if we
+       query before the wire transfer went through.
+       We should simply pass the JSON reply to the application. */
+    break;
+  case MHD_HTTP_INTERNAL_SERVER_ERROR:
+    /* Server had an internal issue; we should retry, but this API
+       leaves this to the application */
+    break;
+  default:
+    /* unexpected response code */
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Unexpected response code %u\n",
+                (unsigned int) response_code);
+    GNUNET_break (0);
+    response_code = 0;
+    break;
+  }
+  if (NULL != wh->cb)
+  {
+    wh->cb (wh->cb_cls,
+            response_code,
+            TALER_JSON_get_error_code (j),
+            NULL,
+            j);
+    wh->cb = NULL;
+  }
+  TALER_EXCHANGE_withdraw_cancel (wh);
+}
+
+
+/**
+ * Helper function for #TALER_EXCHANGE_withdraw2() and
+ * #TALER_EXCHANGE_withdraw().
+ *
+ * @param exchange the exchange handle; the exchange must be ready to operate
+ * @param pk kind of coin to create
+ * @param reserve_sig signature from the reserve authorizing the withdrawal
+ * @param reserve_pub public key of the reserve to withdraw from
+ * @param ps secrets of the planchet
+ *        caller must have committed this value to disk before the call (with 
@a pk)
+ * @param pd planchet details matching @a ps
+ * @param res_cb the callback to call when the final result for this request 
is available
+ * @param res_cb_cls closure for @a res_cb
+ * @return NULL
+ *         if the inputs are invalid (i.e. denomination key not with this 
exchange).
+ *         In this case, the callback is not called.
+ */
+struct TALER_EXCHANGE_WithdrawHandle *
+reserve_withdraw_internal (struct TALER_EXCHANGE_Handle *exchange,
+                           const struct TALER_EXCHANGE_DenomPublicKey *pk,
+                           const struct TALER_ReserveSignatureP *reserve_sig,
+                           const struct TALER_ReservePublicKeyP *reserve_pub,
+                           const struct TALER_PlanchetSecretsP *ps,
+                           const struct TALER_PlanchetDetail *pd,
+                           TALER_EXCHANGE_WithdrawCallback res_cb,
+                           void *res_cb_cls)
+{
+  struct TALER_EXCHANGE_WithdrawHandle *wh;
+  struct GNUNET_CURL_Context *ctx;
+  json_t *withdraw_obj;
+  CURL *eh;
+  struct GNUNET_HashCode h_denom_pub;
+  char arg_str[sizeof (struct TALER_ReservePublicKeyP) * 2 + 32];
+
+  {
+    char pub_str[sizeof (struct TALER_ReservePublicKeyP) * 2];
+    char *end;
+
+    end = GNUNET_STRINGS_data_to_string (reserve_pub,
+                                         sizeof (struct
+                                                 TALER_ReservePublicKeyP),
+                                         pub_str,
+                                         sizeof (pub_str));
+    *end = '\0';
+    GNUNET_snprintf (arg_str,
+                     sizeof (arg_str),
+                     "/reserves/%s/withdraw",
+                     pub_str);
+  }
+  wh = GNUNET_new (struct TALER_EXCHANGE_WithdrawHandle);
+  wh->exchange = exchange;
+  wh->cb = res_cb;
+  wh->cb_cls = res_cb_cls;
+  wh->pk = *pk;
+  wh->pk.key.rsa_public_key
+    = GNUNET_CRYPTO_rsa_public_key_dup (pk->key.rsa_public_key);
+  wh->reserve_pub = *reserve_pub;
+  wh->c_hash = pd->c_hash;
+  GNUNET_CRYPTO_rsa_public_key_hash (pk->key.rsa_public_key,
+                                     &h_denom_pub);
+  withdraw_obj = json_pack ("{s:o, s:o," /* denom_pub_hash and coin_ev */
+                            " s:o}",/* reserve_pub and reserve_sig */
+                            "denom_pub_hash", GNUNET_JSON_from_data_auto (
+                              &h_denom_pub),
+                            "coin_ev", GNUNET_JSON_from_data (pd->coin_ev,
+                                                              
pd->coin_ev_size),
+                            "reserve_sig", GNUNET_JSON_from_data_auto (
+                              reserve_sig));
+  if (NULL == withdraw_obj)
+  {
+    GNUNET_break (0);
+    GNUNET_CRYPTO_rsa_public_key_free (wh->pk.key.rsa_public_key);
+    GNUNET_free (wh);
+    return NULL;
+  }
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Attempting to withdraw from reserve %s\n",
+              TALER_B2S (reserve_pub));
+  wh->ps = *ps;
+  wh->url = TEAH_path_to_url (exchange,
+                              arg_str);
+  eh = TEL_curl_easy_get (wh->url);
+  if (GNUNET_OK !=
+      TALER_curl_easy_post (&wh->ctx,
+                            eh,
+                            withdraw_obj))
+  {
+    GNUNET_break (0);
+    curl_easy_cleanup (eh);
+    json_decref (withdraw_obj);
+    GNUNET_free (wh->url);
+    GNUNET_CRYPTO_rsa_public_key_free (wh->pk.key.rsa_public_key);
+    GNUNET_free (wh);
+    return NULL;
+  }
+  json_decref (withdraw_obj);
+  ctx = TEAH_handle_to_context (exchange);
+  wh->job = GNUNET_CURL_job_add2 (ctx,
+                                  eh,
+                                  wh->ctx.headers,
+                                  &handle_reserve_withdraw_finished,
+                                  wh);
+  return wh;
+}
+
+
+/**
+ * Withdraw a coin from the exchange using a /reserve/withdraw request.  Note
+ * that to ensure that no money is lost in case of hardware failures,
+ * the caller must have committed (most of) the arguments to disk
+ * before calling, and be ready to repeat the request with the same
+ * arguments in case of failures.
+ *
+ * @param exchange the exchange handle; the exchange must be ready to operate
+ * @param pk kind of coin to create
+ * @param reserve_priv private key of the reserve to withdraw from
+ * @param ps secrets of the planchet
+ *        caller must have committed this value to disk before the call (with 
@a pk)
+ * @param res_cb the callback to call when the final result for this request 
is available
+ * @param res_cb_cls closure for the above callback
+ * @return handle for the operation on success, NULL on error, i.e.
+ *         if the inputs are invalid (i.e. denomination key not with this 
exchange).
+ *         In this case, the callback is not called.
+ */
+struct TALER_EXCHANGE_WithdrawHandle *
+TALER_EXCHANGE_withdraw (struct TALER_EXCHANGE_Handle *exchange,
+                         const struct TALER_EXCHANGE_DenomPublicKey *pk,
+                         const struct
+                         TALER_ReservePrivateKeyP *reserve_priv,
+                         const struct TALER_PlanchetSecretsP *ps,
+                         TALER_EXCHANGE_WithdrawCallback
+                         res_cb,
+                         void *res_cb_cls)
+{
+  struct TALER_Amount amount_with_fee;
+  struct TALER_ReserveSignatureP reserve_sig;
+  struct TALER_WithdrawRequestPS req;
+  struct TALER_PlanchetDetail pd;
+  struct TALER_EXCHANGE_WithdrawHandle *wh;
+
+  GNUNET_CRYPTO_eddsa_key_get_public (&reserve_priv->eddsa_priv,
+                                      &req.reserve_pub.eddsa_pub);
+  req.purpose.size = htonl (sizeof (struct TALER_WithdrawRequestPS));
+  req.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_RESERVE_WITHDRAW);
+  if (GNUNET_OK !=
+      TALER_amount_add (&amount_with_fee,
+                        &pk->fee_withdraw,
+                        &pk->value))
+  {
+    /* exchange gave us denomination keys that overflow like this!? */
+    GNUNET_break_op (0);
+    return NULL;
+  }
+  TALER_amount_hton (&req.amount_with_fee,
+                     &amount_with_fee);
+  TALER_amount_hton (&req.withdraw_fee,
+                     &pk->fee_withdraw);
+  if (GNUNET_OK !=
+      TALER_planchet_prepare (&pk->key,
+                              ps,
+                              &pd))
+  {
+    GNUNET_break_op (0);
+    return NULL;
+  }
+  req.h_denomination_pub = pd.denom_pub_hash;
+  GNUNET_CRYPTO_hash (pd.coin_ev,
+                      pd.coin_ev_size,
+                      &req.h_coin_envelope);
+  GNUNET_assert (GNUNET_OK ==
+                 GNUNET_CRYPTO_eddsa_sign (&reserve_priv->eddsa_priv,
+                                           &req.purpose,
+                                           &reserve_sig.eddsa_signature));
+  wh = reserve_withdraw_internal (exchange,
+                                  pk,
+                                  &reserve_sig,
+                                  &req.reserve_pub,
+                                  ps,
+                                  &pd,
+                                  res_cb,
+                                  res_cb_cls);
+  GNUNET_free (pd.coin_ev);
+  return wh;
+}
+
+
+/**
+ * Withdraw a coin from the exchange using a /reserve/withdraw
+ * request.  This API is typically used by a wallet to withdraw a tip
+ * where the reserve's signature was created by the merchant already.
+ *
+ * Note that to ensure that no money is lost in case of hardware
+ * failures, the caller must have committed (most of) the arguments to
+ * disk before calling, and be ready to repeat the request with the
+ * same arguments in case of failures.
+ *
+ * @param exchange the exchange handle; the exchange must be ready to operate
+ * @param pk kind of coin to create
+ * @param reserve_sig signature from the reserve authorizing the withdrawal
+ * @param reserve_pub public key of the reserve to withdraw from
+ * @param ps secrets of the planchet
+ *        caller must have committed this value to disk before the call (with 
@a pk)
+ * @param res_cb the callback to call when the final result for this request 
is available
+ * @param res_cb_cls closure for @a res_cb
+ * @return NULL
+ *         if the inputs are invalid (i.e. denomination key not with this 
exchange).
+ *         In this case, the callback is not called.
+ */
+struct TALER_EXCHANGE_WithdrawHandle *
+TALER_EXCHANGE_withdraw2 (struct TALER_EXCHANGE_Handle *exchange,
+                          const struct
+                          TALER_EXCHANGE_DenomPublicKey *pk,
+                          const struct
+                          TALER_ReserveSignatureP *reserve_sig,
+                          const struct
+                          TALER_ReservePublicKeyP *reserve_pub,
+                          const struct TALER_PlanchetSecretsP *ps,
+                          TALER_EXCHANGE_WithdrawCallback
+                          res_cb,
+                          void *res_cb_cls)
+{
+  struct TALER_EXCHANGE_WithdrawHandle *wh;
+  struct TALER_PlanchetDetail pd;
+
+  if (GNUNET_OK !=
+      TALER_planchet_prepare (&pk->key,
+                              ps,
+                              &pd))
+  {
+    GNUNET_break_op (0);
+    return NULL;
+  }
+  wh = reserve_withdraw_internal (exchange,
+                                  pk,
+                                  reserve_sig,
+                                  reserve_pub,
+                                  ps,
+                                  &pd,
+                                  res_cb,
+                                  res_cb_cls);
+  GNUNET_free (pd.coin_ev);
+  return wh;
+}
+
+
+/**
+ * Cancel a withdraw status request.  This function cannot be used
+ * on a request handle if a response is already served for it.
+ *
+ * @param sign the withdraw sign request handle
+ */
+void
+TALER_EXCHANGE_withdraw_cancel (struct TALER_EXCHANGE_WithdrawHandle *wh)
+{
+  if (NULL != wh->job)
+  {
+    GNUNET_CURL_job_cancel (wh->job);
+    wh->job = NULL;
+  }
+  GNUNET_free (wh->url);
+  TALER_curl_easy_post_finished (&wh->ctx);
+  GNUNET_CRYPTO_rsa_public_key_free (wh->pk.key.rsa_public_key);
+  GNUNET_free (wh);
+}
diff --git a/src/testing/test_auditor_api.c b/src/testing/test_auditor_api.c
index 144f004d..a2d7e00a 100644
--- a/src/testing/test_auditor_api.c
+++ b/src/testing/test_auditor_api.c
@@ -190,10 +190,10 @@ run (void *cls,
     /**
      * Melt the rest of the coin's value (EUR:4.00 = 3x EUR:1.03 + 7x
      * EUR:0.13) */
-    TALER_TESTING_cmd_refresh_melt_double ("refresh-melt-1",
-                                           "refresh-withdraw-coin-1",
-                                           MHD_HTTP_OK,
-                                           NULL),
+    TALER_TESTING_cmd_melt_double ("refresh-melt-1",
+                                   "refresh-withdraw-coin-1",
+                                   MHD_HTTP_OK,
+                                   NULL),
     /**
      * Complete (successful) melt operation, and withdraw the coins
      */
diff --git a/src/testing/test_exchange_api.c b/src/testing/test_exchange_api.c
index 38d0c921..ba655d9b 100644
--- a/src/testing/test_exchange_api.c
+++ b/src/testing/test_exchange_api.c
@@ -239,10 +239,10 @@ run (void *cls,
     /**
      * Melt the rest of the coin's value
      * (EUR:4.00 = 3x EUR:1.03 + 7x EUR:0.13) */
-    TALER_TESTING_cmd_refresh_melt_double ("refresh-melt-1",
-                                           "refresh-withdraw-coin-1",
-                                           MHD_HTTP_OK,
-                                           NULL),
+    TALER_TESTING_cmd_melt_double ("refresh-melt-1",
+                                   "refresh-withdraw-coin-1",
+                                   MHD_HTTP_OK,
+                                   NULL),
     /**
      * Complete (successful) melt operation, and
      * withdraw the coins
@@ -286,16 +286,16 @@ run (void *cls,
                                MHD_HTTP_OK),
     /* Test running a failing melt operation (same operation
      * again must fail) */
-    TALER_TESTING_cmd_refresh_melt ("refresh-melt-failing",
-                                    "refresh-withdraw-coin-1",
-                                    MHD_HTTP_CONFLICT,
-                                    NULL),
+    TALER_TESTING_cmd_melt ("refresh-melt-failing",
+                            "refresh-withdraw-coin-1",
+                            MHD_HTTP_CONFLICT,
+                            NULL),
     /* Test running a failing melt operation (on a coin that
        was itself revealed and subsequently deposited) */
-    TALER_TESTING_cmd_refresh_melt ("refresh-melt-failing-2",
-                                    "refresh-reveal-1",
-                                    MHD_HTTP_CONFLICT,
-                                    NULL),
+    TALER_TESTING_cmd_melt ("refresh-melt-failing-2",
+                            "refresh-reveal-1",
+                            MHD_HTTP_CONFLICT,
+                            NULL),
 
     TALER_TESTING_cmd_end ()
   };
diff --git a/src/testing/test_exchange_api_revocation.c 
b/src/testing/test_exchange_api_revocation.c
index 45463ded..6a9cdefe 100644
--- a/src/testing/test_exchange_api_revocation.c
+++ b/src/testing/test_exchange_api_revocation.c
@@ -98,10 +98,10 @@ run (void *cls,
     /**
      * Melt SOME of the rest of the coin's value
      * (EUR:3.17 = 3x EUR:1.03 + 7x EUR:0.13) */
-    TALER_TESTING_cmd_refresh_melt ("refresh-melt-1",
-                                    "withdraw-revocation-coin-1",
-                                    MHD_HTTP_OK,
-                                    NULL),
+    TALER_TESTING_cmd_melt ("refresh-melt-1",
+                            "withdraw-revocation-coin-1",
+                            MHD_HTTP_OK,
+                            NULL),
     /**
      * Complete (successful) melt operation, and withdraw the coins
      */
@@ -133,11 +133,11 @@ run (void *cls,
     /* Melt original coin AGAIN, but only create one 0.1 EUR coin;
        This costs EUR:0.03 in refresh and EUR:01 in withdraw fees,
        leaving EUR:3.69. */
-    TALER_TESTING_cmd_refresh_melt ("refresh-melt-2",
-                                    "withdraw-revocation-coin-1",
-                                    MHD_HTTP_OK,
-                                    "EUR:0.1",
-                                    NULL),
+    TALER_TESTING_cmd_melt ("refresh-melt-2",
+                            "withdraw-revocation-coin-1",
+                            MHD_HTTP_OK,
+                            "EUR:0.1",
+                            NULL),
     /**
      * Complete (successful) melt operation, and withdraw the coins
      */
diff --git a/src/testing/test_exchange_api_twisted.c 
b/src/testing/test_exchange_api_twisted.c
index a3a038d4..146e28de 100644
--- a/src/testing/test_exchange_api_twisted.c
+++ b/src/testing/test_exchange_api_twisted.c
@@ -140,7 +140,7 @@ run (void *cls,
     /**
      * Melt the rest of the coin's value
      * (EUR:4.00 = 3x EUR:1.03 + 7x EUR:0.13) */
-    TALER_TESTING_cmd_refresh_melt
+    TALER_TESTING_cmd_melt
       ("refresh-melt",
       "refresh-withdraw-coin",
       MHD_HTTP_OK,
diff --git a/src/testing/testing_api_cmd_refresh.c 
b/src/testing/testing_api_cmd_refresh.c
index 73b74daf..dd861fae 100644
--- a/src/testing/testing_api_cmd_refresh.c
+++ b/src/testing/testing_api_cmd_refresh.c
@@ -87,7 +87,7 @@ struct RefreshMeltState
   /**
    * Melt handle while operation is running.
    */
-  struct TALER_EXCHANGE_RefreshMeltHandle *rmh;
+  struct TALER_EXCHANGE_MeltHandle *rmh;
 
   /**
    * Interpreter state.
@@ -173,7 +173,7 @@ struct RefreshRevealState
   /**
    * Reveal handle while operation is running.
    */
-  struct TALER_EXCHANGE_RefreshRevealHandle *rrh;
+  struct TALER_EXCHANGE_RefreshesRevealHandle *rrh;
 
   /**
    * Convenience struct to keep in one place all the
@@ -230,7 +230,7 @@ struct RefreshLinkState
   /**
    * Handle to the ongoing operation.
    */
-  struct TALER_EXCHANGE_RefreshLinkHandle *rlh;
+  struct TALER_EXCHANGE_LinkHandle *rlh;
 
   /**
    * Interpreter state.
@@ -423,7 +423,7 @@ refresh_reveal_run (void *cls,
     return;
   }
   rms = melt_cmd->cls;
-  rrs->rrh = TALER_EXCHANGE_refresh_reveal
+  rrs->rrh = TALER_EXCHANGE_refreshes_reveal
                (is->exchange,
                rms->refresh_data_length,
                rms->refresh_data,
@@ -459,7 +459,7 @@ refresh_reveal_cleanup (void *cls,
                 rrs->is->ip,
                 cmd->label);
 
-    TALER_EXCHANGE_refresh_reveal_cancel (rrs->rrh);
+    TALER_EXCHANGE_refreshes_reveal_cancel (rrs->rrh);
     rrs->rrh = NULL;
   }
   if (NULL != rrs->retry_task)
@@ -730,7 +730,7 @@ refresh_link_run (void *cls,
   }
 
   /* finally, use private key from withdraw sign command */
-  rls->rlh = TALER_EXCHANGE_refresh_link
+  rls->rlh = TALER_EXCHANGE_link
                (is->exchange, coin_priv, &link_cb, rls);
 
   if (NULL == rls->rlh)
@@ -762,7 +762,7 @@ refresh_link_cleanup (void *cls,
                 "Command %u (%s) did not complete\n",
                 rls->is->ip,
                 cmd->label);
-    TALER_EXCHANGE_refresh_link_cancel (rls->rlh);
+    TALER_EXCHANGE_link_cancel (rls->rlh);
     rls->rlh = NULL;
   }
   if (NULL != rls->retry_task)
@@ -782,13 +782,13 @@ refresh_link_cleanup (void *cls,
  * @param is the interpreter state.
  */
 static void
-refresh_melt_run (void *cls,
-                  const struct TALER_TESTING_Command *cmd,
-                  struct TALER_TESTING_Interpreter *is);
+melt_run (void *cls,
+          const struct TALER_TESTING_Command *cmd,
+          struct TALER_TESTING_Interpreter *is);
 
 
 /**
- * Task scheduled to re-try #refresh_melt_run.
+ * Task scheduled to re-try #melt_run.
  *
  * @param cls a `struct RefreshMeltState`
  */
@@ -798,9 +798,9 @@ do_melt_retry (void *cls)
   struct RefreshMeltState *rms = cls;
 
   rms->retry_task = NULL;
-  refresh_melt_run (rms,
-                    NULL,
-                    rms->is);
+  melt_run (rms,
+            NULL,
+            rms->is);
 }
 
 
@@ -870,7 +870,7 @@ melt_cb (void *cls,
   {
     TALER_LOG_DEBUG ("Doubling the melt (%s)\n",
                      rms->is->commands[rms->is->ip].label);
-    rms->rmh = TALER_EXCHANGE_refresh_melt
+    rms->rmh = TALER_EXCHANGE_melt
                  (rms->is->exchange, rms->refresh_data_length,
                  rms->refresh_data, &melt_cb, rms);
     rms->double_melt = GNUNET_NO;
@@ -888,9 +888,9 @@ melt_cb (void *cls,
  * @param is the interpreter state.
  */
 static void
-refresh_melt_run (void *cls,
-                  const struct TALER_TESTING_Command *cmd,
-                  struct TALER_TESTING_Interpreter *is)
+melt_run (void *cls,
+          const struct TALER_TESTING_Command *cmd,
+          struct TALER_TESTING_Interpreter *is)
 {
   struct RefreshMeltState *rms = cls;
   unsigned int num_fresh_coins;
@@ -1006,11 +1006,11 @@ refresh_melt_run (void *cls,
       TALER_TESTING_interpreter_fail (rms->is);
       return;
     }
-    rms->rmh = TALER_EXCHANGE_refresh_melt (is->exchange,
-                                            rms->refresh_data_length,
-                                            rms->refresh_data,
-                                            &melt_cb,
-                                            rms);
+    rms->rmh = TALER_EXCHANGE_melt (is->exchange,
+                                    rms->refresh_data_length,
+                                    rms->refresh_data,
+                                    &melt_cb,
+                                    rms);
 
     if (NULL == rms->rmh)
     {
@@ -1030,8 +1030,8 @@ refresh_melt_run (void *cls,
  * @param cmd the command which is being cleaned up.
  */
 static void
-refresh_melt_cleanup (void *cls,
-                      const struct TALER_TESTING_Command *cmd)
+melt_cleanup (void *cls,
+              const struct TALER_TESTING_Command *cmd)
 {
   struct RefreshMeltState *rms = cls;
 
@@ -1040,7 +1040,7 @@ refresh_melt_cleanup (void *cls,
     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
                 "Command %u (%s) did not complete\n",
                 rms->is->ip, rms->is->commands[rms->is->ip].label);
-    TALER_EXCHANGE_refresh_melt_cancel (rms->rmh);
+    TALER_EXCHANGE_melt_cancel (rms->rmh);
     rms->rmh = NULL;
   }
   if (NULL != rms->retry_task)
@@ -1073,10 +1073,10 @@ refresh_melt_cleanup (void *cls,
  * @return #GNUNET_OK on success.
  */
 static int
-refresh_melt_traits (void *cls,
-                     const void **ret,
-                     const char *trait,
-                     unsigned int index)
+melt_traits (void *cls,
+             const void **ret,
+             const char *trait,
+             unsigned int index)
 {
   struct RefreshMeltState *rms = cls;
 
@@ -1161,10 +1161,10 @@ parse_amounts (struct RefreshMeltState *rms,
  * @return the command.
  */
 struct TALER_TESTING_Command
-TALER_TESTING_cmd_refresh_melt (const char *label,
-                                const char *coin_reference,
-                                unsigned int expected_response_code,
-                                ...)
+TALER_TESTING_cmd_melt (const char *label,
+                        const char *coin_reference,
+                        unsigned int expected_response_code,
+                        ...)
 {
   struct RefreshMeltState *rms;
   va_list ap;
@@ -1180,9 +1180,9 @@ TALER_TESTING_cmd_refresh_melt (const char *label,
     struct TALER_TESTING_Command cmd = {
       .label = label,
       .cls = rms,
-      .run = &refresh_melt_run,
-      .cleanup = &refresh_melt_cleanup,
-      .traits = &refresh_melt_traits
+      .run = &melt_run,
+      .cleanup = &melt_cleanup,
+      .traits = &melt_traits
     };
 
     return cmd;
@@ -1203,10 +1203,10 @@ TALER_TESTING_cmd_refresh_melt (const char *label,
  * @return the command.
  */
 struct TALER_TESTING_Command
-TALER_TESTING_cmd_refresh_melt_double (const char *label,
-                                       const char *coin_reference,
-                                       unsigned int expected_response_code,
-                                       ...)
+TALER_TESTING_cmd_melt_double (const char *label,
+                               const char *coin_reference,
+                               unsigned int expected_response_code,
+                               ...)
 {
   struct RefreshMeltState *rms;
   va_list ap;
@@ -1223,9 +1223,9 @@ TALER_TESTING_cmd_refresh_melt_double (const char *label,
     struct TALER_TESTING_Command cmd = {
       .label = label,
       .cls = rms,
-      .run = &refresh_melt_run,
-      .cleanup = &refresh_melt_cleanup,
-      .traits = &refresh_melt_traits
+      .run = &melt_run,
+      .cleanup = &melt_cleanup,
+      .traits = &melt_traits
     };
 
     return cmd;
@@ -1240,11 +1240,11 @@ TALER_TESTING_cmd_refresh_melt_double (const char 
*label,
  * @return modified command.
  */
 struct TALER_TESTING_Command
-TALER_TESTING_cmd_refresh_melt_with_retry (struct TALER_TESTING_Command cmd)
+TALER_TESTING_cmd_melt_with_retry (struct TALER_TESTING_Command cmd)
 {
   struct RefreshMeltState *rms;
 
-  GNUNET_assert (&refresh_melt_run == cmd.run);
+  GNUNET_assert (&melt_run == cmd.run);
   rms = cmd.cls;
   rms->do_retry = GNUNET_YES;
   return cmd;

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



reply via email to

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