gnunet-svn
[Top][All Lists]
Advanced

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

[GNUnet-SVN] [taler-exchange] 06/07: split httpd_refresh.c into refresh_


From: gnunet
Subject: [GNUnet-SVN] [taler-exchange] 06/07: split httpd_refresh.c into refresh_melt, refresh_link and refresh_reveal
Date: Mon, 19 Jun 2017 21:18:04 +0200

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

grothoff pushed a commit to branch master
in repository exchange.

commit f8e62141f27c296b326fc810f18206d16d41b468
Author: Christian Grothoff <address@hidden>
AuthorDate: Mon Jun 19 21:04:49 2017 +0200

    split httpd_refresh.c into refresh_melt, refresh_link and refresh_reveal
---
 src/exchange/Makefile.am                           |   4 +-
 src/exchange/taler-exchange-httpd.c                |   6 +-
 src/exchange/taler-exchange-httpd_db.c             | 945 ---------------------
 src/exchange/taler-exchange-httpd_db.h             |  94 +-
 src/exchange/taler-exchange-httpd_refresh_link.c   | 286 +++++++
 src/exchange/taler-exchange-httpd_refresh_link.h   |  49 ++
 ...fresh.c => taler-exchange-httpd_refresh_melt.c} | 622 ++++++++++----
 src/exchange/taler-exchange-httpd_refresh_melt.h   |  52 ++
 ..._db.c => taler-exchange-httpd_refresh_reveal.c} | 729 +++++-----------
 ...esh.h => taler-exchange-httpd_refresh_reveal.h} |  50 +-
 src/exchange/taler-exchange-httpd_responses.c      | 245 ------
 src/exchange/taler-exchange-httpd_responses.h      | 105 ---
 12 files changed, 1062 insertions(+), 2125 deletions(-)

diff --git a/src/exchange/Makefile.am b/src/exchange/Makefile.am
index 1d83724..9ff98d1 100644
--- a/src/exchange/Makefile.am
+++ b/src/exchange/Makefile.am
@@ -50,7 +50,9 @@ taler_exchange_httpd_SOURCES = \
   taler-exchange-httpd_mhd.c taler-exchange-httpd_mhd.h \
   taler-exchange-httpd_parsing.c taler-exchange-httpd_parsing.h \
   taler-exchange-httpd_payback.c taler-exchange-httpd_payback.h \
-  taler-exchange-httpd_refresh.c taler-exchange-httpd_refresh.h \
+  taler-exchange-httpd_refresh_link.c taler-exchange-httpd_refresh_link.h \
+  taler-exchange-httpd_refresh_melt.c taler-exchange-httpd_refresh_melt.h \
+  taler-exchange-httpd_refresh_reveal.c taler-exchange-httpd_refresh_reveal.h \
   taler-exchange-httpd_refund.c taler-exchange-httpd_refund.h \
   taler-exchange-httpd_reserve_status.c taler-exchange-httpd_reserve_status.h \
   taler-exchange-httpd_reserve_withdraw.c 
taler-exchange-httpd_reserve_withdraw.h \
diff --git a/src/exchange/taler-exchange-httpd.c 
b/src/exchange/taler-exchange-httpd.c
index 43c8a80..1a4d286 100644
--- a/src/exchange/taler-exchange-httpd.c
+++ b/src/exchange/taler-exchange-httpd.c
@@ -34,11 +34,13 @@
 #include "taler-exchange-httpd_reserve_status.h"
 #include "taler-exchange-httpd_reserve_withdraw.h"
 #include "taler-exchange-httpd_payback.h"
-#include "taler-exchange-httpd_wire.h"
-#include "taler-exchange-httpd_refresh.h"
+#include "taler-exchange-httpd_refresh_link.h"
+#include "taler-exchange-httpd_refresh_melt.h"
+#include "taler-exchange-httpd_refresh_reveal.h"
 #include "taler-exchange-httpd_track_transfer.h"
 #include "taler-exchange-httpd_track_transaction.h"
 #include "taler-exchange-httpd_keystate.h"
+#include "taler-exchange-httpd_wire.h"
 #if HAVE_DEVELOPER
 #include "taler-exchange-httpd_test.h"
 #endif
diff --git a/src/exchange/taler-exchange-httpd_db.c 
b/src/exchange/taler-exchange-httpd_db.c
index 9871b7f..e7be6af 100644
--- a/src/exchange/taler-exchange-httpd_db.c
+++ b/src/exchange/taler-exchange-httpd_db.c
@@ -32,85 +32,6 @@
  */
 #define MAX_TRANSACTION_COMMIT_RETRIES 3
 
-/**
- * Code to begin a transaction, must be inline as we define a block
- * that ends with #COMMIT_TRANSACTION() within which we perform a number
- * of retries.  Note that this code may call "return" internally, so
- * it must be called within a function where any cleanup will be done
- * by the caller. Furthermore, the function's return value must
- * match that of a #TEH_RESPONSE_reply_internal_db_error() status code.
- *
- * @param session session handle
- * @param connection connection handle
- */
-#define START_TRANSACTION(session,connection)                 \
-{ /* start new scope, will be ended by COMMIT_TRANSACTION() */\
-  unsigned int transaction_retries = 0;                       \
-  enum GNUNET_DB_QueryStatus transaction_commit_result;       \
-transaction_start_label: /* we will use goto for retries */   \
-  if (GNUNET_OK !=                                            \
-      TEH_plugin->start (TEH_plugin->cls,                     \
-                         session))                            \
-  {                                                           \
-    GNUNET_break (0);                                         \
-    return TEH_RESPONSE_reply_internal_db_error (connection, \
-                                                TALER_EC_DB_START_FAILED);     
     \
-  }
-
-/**
- * Code to conclude a transaction, dual to #START_TRANSACTION().  Note
- * that this code may call "return" internally, so it must be called
- * within a function where any cleanup will be done by the caller.
- * Furthermore, the function's return value must match that of a
- * #TEH_RESPONSE_reply_internal_db_error() status code.
- *
- * @param session session handle
- * @param connection connection handle
- */
-#define COMMIT_TRANSACTION(session,connection)                             \
-  transaction_commit_result =                                              \
-    TEH_plugin->commit (TEH_plugin->cls,                                   \
-                        session);                                          \
-  if (GNUNET_DB_STATUS_HARD_ERROR == transaction_commit_result)            \
-  {                                                                        \
-    TALER_LOG_WARNING ("Transaction commit failed in %s\n", __FUNCTION__); \
-    return TEH_RESPONSE_reply_commit_error (connection, \
-                                           TALER_EC_DB_COMMIT_FAILED_HARD); \
-  }                                                       \
-  if (GNUNET_DB_STATUS_SOFT_ERROR == transaction_commit_result)            \
-  {                                                                        \
-    TALER_LOG_WARNING ("Transaction commit failed in %s\n", __FUNCTION__); \
-    if (transaction_retries++ <= MAX_TRANSACTION_COMMIT_RETRIES)           \
-      goto transaction_start_label;                                        \
-    TALER_LOG_WARNING ("Transaction commit failed %u times in %s\n",       \
-                       transaction_retries,                                \
-                       __FUNCTION__);                                      \
-    return TEH_RESPONSE_reply_commit_error (connection, \
-                                           
TALER_EC_DB_COMMIT_FAILED_ON_RETRY);                                \
-  }                                                                        \
-} /* end of scope opened by BEGIN_TRANSACTION */
-
-
-/**
- * Code to include to retry a transaction, must only be used in between
- * #START_TRANSACTION and #COMMIT_TRANSACTION.
- *
- * @param session session handle
- * @param connection connection handle
- */
-#define RETRY_TRANSACTION(session,connection)                                  
  \
-  do {                                                                         
  \
-    TEH_plugin->rollback (TEH_plugin->cls,                                     
  \
-                          session);                                            
  \
-    if (transaction_retries++ <= MAX_TRANSACTION_COMMIT_RETRIES)               
  \
-      goto transaction_start_label;                                            
  \
-    TALER_LOG_WARNING ("Transaction commit failed %u times in %s\n",           
  \
-                       transaction_retries,                                    
  \
-                       __FUNCTION__);                                          
  \
-    return TEH_RESPONSE_reply_commit_error (connection,                        
  \
-                                           
TALER_EC_DB_COMMIT_FAILED_ON_RETRY); \
-  } while (0)
-
 
 /**
  * Run a database transaction for @a connection.
@@ -279,870 +200,4 @@ TEH_DB_calculate_transaction_list_totals (struct 
TALER_EXCHANGEDB_TransactionLis
 }
 
 
-/**
- * Parse coin melt requests from a JSON object and write them to
- * the database.
- *
- * @param connection the connection to send errors to
- * @param session the database connection
- * @param key_state the exchange's key state
- * @param session_hash hash identifying the refresh session
- * @param coin_details details about the coin being melted
- * @param[out] meltp on success, set to melt details
- * @return #GNUNET_OK on success,
- *         #GNUNET_NO if an error message was generated,
- *         #GNUNET_SYSERR on internal errors (no response generated)
- */
-static int
-refresh_check_melt (struct MHD_Connection *connection,
-                    struct TALER_EXCHANGEDB_Session *session,
-                    const struct TEH_KS_StateHandle *key_state,
-                    const struct GNUNET_HashCode *session_hash,
-                    const struct TEH_DB_MeltDetails *coin_details,
-                    struct TALER_EXCHANGEDB_RefreshMelt *meltp)
-{
-  struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dk;
-  struct TALER_EXCHANGEDB_DenominationKeyInformationP *dki;
-  struct TALER_EXCHANGEDB_TransactionList *tl;
-  struct TALER_Amount coin_value;
-  struct TALER_Amount coin_residual;
-  struct TALER_Amount spent;
-  int res;
-  enum GNUNET_DB_QueryStatus qs;
-
-  dk = TEH_KS_denomination_key_lookup (key_state,
-                                       &coin_details->coin_info.denom_pub,
-                                       TEH_KS_DKU_DEPOSIT);
-  if (NULL == dk)
-    return (MHD_YES ==
-            TEH_RESPONSE_reply_internal_error (connection,
-                                              
TALER_EC_REFRESH_MELT_DB_DENOMINATION_KEY_NOT_FOUND,
-                                              "denomination key no longer 
available while executing transaction"))
-        ? GNUNET_NO : GNUNET_SYSERR;
-  dki = &dk->issue;
-  TALER_amount_ntoh (&coin_value,
-                     &dki->properties.value);
-  /* fee for THIS transaction; the melt amount includes the fee! */
-  spent = coin_details->melt_amount_with_fee;
-  /* add historic transaction costs of this coin */
-  qs = TEH_plugin->get_coin_transactions (TEH_plugin->cls,
-                                          session,
-                                          &coin_details->coin_info.coin_pub,
-                                         &tl);
-  (void) qs; /* FIXME #5010 */
-  if (GNUNET_OK !=
-      TEH_DB_calculate_transaction_list_totals (tl,
-                                               &spent,
-                                               &spent))
-  {
-    GNUNET_break (0);
-    TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
-                                            tl);
-    return (MHD_YES ==
-            TEH_RESPONSE_reply_internal_db_error (connection,
-                                                 
TALER_EC_REFRESH_MELT_COIN_HISTORY_COMPUTATION_FAILED))
-      ? GNUNET_NO : GNUNET_SYSERR;
-  }
-  /* Refuse to refresh when the coin's value is insufficient
-     for the cost of all transactions. */
-  if (TALER_amount_cmp (&coin_value,
-                        &spent) < 0)
-  {
-    GNUNET_assert (GNUNET_SYSERR !=
-                   TALER_amount_subtract (&coin_residual,
-                                          &spent,
-                                          
&coin_details->melt_amount_with_fee));
-    res = (MHD_YES ==
-           TEH_RESPONSE_reply_refresh_melt_insufficient_funds (connection,
-                                                               
&coin_details->coin_info.coin_pub,
-                                                               coin_value,
-                                                               tl,
-                                                               
coin_details->melt_amount_with_fee,
-                                                               coin_residual))
-        ? GNUNET_NO : GNUNET_SYSERR;
-    TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
-                                            tl);
-    return res;
-  }
-  TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
-                                          tl);
-
-  meltp->coin = coin_details->coin_info;
-  meltp->coin_sig = coin_details->melt_sig;
-  meltp->session_hash = *session_hash;
-  meltp->amount_with_fee = coin_details->melt_amount_with_fee;
-  meltp->melt_fee = coin_details->melt_fee;
-  return GNUNET_OK;
-}
-
-
-/**
- * Execute a "/refresh/melt".  We have been given a list of valid
- * coins and a request to melt them into the given
- * @a refresh_session_pub.  Check that the coins all have the
- * required value left and if so, store that they have been
- * melted and confirm the melting operation to the client.
- *
- * @param connection the MHD connection to handle
- * @param session_hash hash code of the session the coins are melted into
- * @param num_new_denoms number of entries in @a denom_pubs, size of 
y-dimension of @a commit_coin array
- * @param denom_pubs public keys of the coins we want to withdraw in the end
- * @param coin_melt_detail signature and (residual) value of the respective 
coin should be melted
- * @param commit_coin 2d array of coin commitments (what the exchange is to 
sign
- *                    once the "/refres/reveal" of cut and choose is done),
- *                    x-dimension must be #TALER_CNC_KAPPA
- * @param transfer_pubs array of transfer public keys (what the exchange is
- *                    to return via "/refresh/link" to enable linkage in the
- *                    future) of length #TALER_CNC_KAPPA
- * @return MHD result code
- */
-int
-TEH_DB_execute_refresh_melt (struct MHD_Connection *connection,
-                             const struct GNUNET_HashCode *session_hash,
-                             unsigned int num_new_denoms,
-                             const struct TALER_DenominationPublicKey 
*denom_pubs,
-                             const struct TEH_DB_MeltDetails *coin_melt_detail,
-                             struct TALER_EXCHANGEDB_RefreshCommitCoin *const* 
commit_coin,
-                             const struct TALER_TransferPublicKeyP 
*transfer_pubs)
-{
-  struct TEH_KS_StateHandle *key_state;
-  struct TALER_EXCHANGEDB_RefreshSession refresh_session;
-  struct TALER_EXCHANGEDB_Session *session;
-  int res;
-
-  if (NULL == (session = TEH_plugin->get_session (TEH_plugin->cls)))
-  {
-    GNUNET_break (0);
-    return TEH_RESPONSE_reply_internal_db_error (connection,
-                                                TALER_EC_DB_SETUP_FAILED);
-  }
-  START_TRANSACTION (session, connection);
-  res = TEH_plugin->get_refresh_session (TEH_plugin->cls,
-                                         session,
-                                         session_hash,
-                                         &refresh_session);
-  if (GNUNET_YES == res)
-  {
-    TEH_plugin->rollback (TEH_plugin->cls,
-                          session);
-    res = TEH_RESPONSE_reply_refresh_melt_success (connection,
-                                                   session_hash,
-                                                   
refresh_session.noreveal_index);
-    return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
-  }
-  if (GNUNET_SYSERR == res)
-  {
-    TEH_plugin->rollback (TEH_plugin->cls,
-                          session);
-    return TEH_RESPONSE_reply_internal_db_error (connection,
-                                                
TALER_EC_REFRESH_MELT_DB_FETCH_ERROR);
-  }
-
-  /* store 'global' session data */
-  refresh_session.num_newcoins = num_new_denoms;
-  refresh_session.noreveal_index
-    = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_STRONG,
-                               TALER_CNC_KAPPA);
-  key_state = TEH_KS_acquire ();
-  if (GNUNET_OK !=
-      (res = refresh_check_melt (connection,
-                                 session,
-                                 key_state,
-                                 session_hash,
-                                 coin_melt_detail,
-                                 &refresh_session.melt)))
-  {
-    TEH_KS_release (key_state);
-    TEH_plugin->rollback (TEH_plugin->cls,
-                          session);
-    return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
-  }
-  TEH_KS_release (key_state);
-
-  if (GNUNET_OK !=
-      (res = TEH_plugin->create_refresh_session (TEH_plugin->cls,
-                                                 session,
-                                                 session_hash,
-                                                 &refresh_session)))
-  {
-    TEH_plugin->rollback (TEH_plugin->cls,
-                          session);
-    return TEH_RESPONSE_reply_internal_db_error (connection,
-                                                
TALER_EC_REFRESH_MELT_DB_STORE_SESSION_ERROR);
-  }
-
-  /* store requested new denominations */
-  if (GNUNET_OK !=
-      TEH_plugin->insert_refresh_order (TEH_plugin->cls,
-                                        session,
-                                        session_hash,
-                                        num_new_denoms,
-                                        denom_pubs))
-  {
-    TEH_plugin->rollback (TEH_plugin->cls,
-                          session);
-    return TEH_RESPONSE_reply_internal_db_error (connection,
-                                                
TALER_EC_REFRESH_MELT_DB_STORE_ORDER_ERROR);
-  }
-
-  if (GNUNET_OK !=
-      TEH_plugin->insert_refresh_commit_coins (TEH_plugin->cls,
-                                               session,
-                                               session_hash,
-                                               num_new_denoms,
-                                               
commit_coin[refresh_session.noreveal_index]))
-  {
-    TEH_plugin->rollback (TEH_plugin->cls,
-                          session);
-    return TEH_RESPONSE_reply_internal_db_error (connection,
-                                                
TALER_EC_REFRESH_MELT_DB_STORE_ORDER_ERROR);
-  }
-  if (GNUNET_OK !=
-      TEH_plugin->insert_refresh_transfer_public_key (TEH_plugin->cls,
-                                                      session,
-                                                      session_hash,
-                                                      
&transfer_pubs[refresh_session.noreveal_index]))
-  {
-    TEH_plugin->rollback (TEH_plugin->cls,
-                          session);
-    return TEH_RESPONSE_reply_internal_db_error (connection,
-                                                
TALER_EC_REFRESH_MELT_DB_STORE_TRANSFER_ERROR);
-  }
-
-  COMMIT_TRANSACTION (session, connection);
-  return TEH_RESPONSE_reply_refresh_melt_success (connection,
-                                                  session_hash,
-                                                  
refresh_session.noreveal_index);
-}
-
-
-/**
- * Check if the given @a transfer_privs correspond to an honest
- * commitment for the given session.
- * Checks that the transfer private keys match their commitments.
- * Then derives the shared secret for each #TALER_CNC_KAPPA, and check that 
they match.
- *
- * @param connection the MHD connection to handle
- * @param session database connection to use
- * @param session_hash hash of session to query
- * @param off commitment offset to check
- * @param transfer_priv private transfer key
- * @param melt information about the melted coin
- * @param num_newcoins number of newcoins being generated
- * @param denom_pubs array of @a num_newcoins keys for the new coins
- * @param hash_context hash context to update by hashing in the data
- *                     from this offset
- * @return #GNUNET_OK if the committment was honest,
- *         #GNUNET_NO if there was a problem and we generated an error message
- *         #GNUNET_SYSERR if we could not even generate an error message
- */
-static int
-check_commitment (struct MHD_Connection *connection,
-                  struct TALER_EXCHANGEDB_Session *session,
-                  const struct GNUNET_HashCode *session_hash,
-                  unsigned int off,
-                  const struct TALER_TransferPrivateKeyP *transfer_priv,
-                  const struct TALER_EXCHANGEDB_RefreshMelt *melt,
-                  unsigned int num_newcoins,
-                  const struct TALER_DenominationPublicKey *denom_pubs,
-                  struct GNUNET_HashContext *hash_context)
-{
-  struct TALER_TransferSecretP transfer_secret;
-  unsigned int j;
-
-  TALER_link_reveal_transfer_secret (transfer_priv,
-                                     &melt->coin.coin_pub,
-                                     &transfer_secret);
-
-  /* Check that the commitments for all new coins were correct */
-  for (j = 0; j < num_newcoins; j++)
-  {
-    struct TALER_FreshCoinP fc;
-    struct TALER_CoinSpendPublicKeyP coin_pub;
-    struct GNUNET_HashCode h_msg;
-    char *buf;
-    size_t buf_len;
-
-    TALER_setup_fresh_coin (&transfer_secret,
-                            j,
-                            &fc);
-    GNUNET_CRYPTO_eddsa_key_get_public (&fc.coin_priv.eddsa_priv,
-                                        &coin_pub.eddsa_pub);
-    GNUNET_CRYPTO_hash (&coin_pub,
-                        sizeof (struct TALER_CoinSpendPublicKeyP),
-                        &h_msg);
-    if (GNUNET_YES !=
-        GNUNET_CRYPTO_rsa_blind (&h_msg,
-                                 &fc.blinding_key.bks,
-                                 denom_pubs[j].rsa_public_key,
-                                 &buf,
-                                 &buf_len))
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                  "Blind failed (bad denomination key!?)\n");
-      return (MHD_YES ==
-             TEH_RESPONSE_reply_internal_error (connection,
-                                                
TALER_EC_REFRESH_REVEAL_BLINDING_ERROR,
-                                                "Blinding error"))
-        ? GNUNET_NO : GNUNET_SYSERR;
-    }
-    GNUNET_CRYPTO_hash_context_read (hash_context,
-                                     buf,
-                                     buf_len);
-    GNUNET_free (buf);
-  }
-  return GNUNET_OK;
-}
-
-
-/**
- * Exchange a coin as part of a refresh operation.  Obtains the
- * envelope from the database and performs the signing operation.
- *
- * @param connection the MHD connection to handle
- * @param session database connection to use
- * @param session_hash hash of session to query
- * @param key_state key state to lookup denomination pubs
- * @param denom_pub denomination key for the coin to create
- * @param commit_coin the coin that was committed
- * @param coin_off number of the coin
- * @return NULL on error, otherwise signature over the coin
- */
-static struct TALER_DenominationSignature
-refresh_exchange_coin (struct MHD_Connection *connection,
-                       struct TALER_EXCHANGEDB_Session *session,
-                       const struct GNUNET_HashCode *session_hash,
-                       struct TEH_KS_StateHandle *key_state,
-                       const struct TALER_DenominationPublicKey *denom_pub,
-                       const struct TALER_EXCHANGEDB_RefreshCommitCoin 
*commit_coin,
-                       unsigned int coin_off)
-{
-  struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki;
-  struct TALER_DenominationSignature ev_sig;
-
-  dki = TEH_KS_denomination_key_lookup (key_state,
-                                        denom_pub,
-                                       TEH_KS_DKU_WITHDRAW);
-  if (NULL == dki)
-  {
-    GNUNET_break (0);
-    ev_sig.rsa_signature = NULL;
-    return ev_sig;
-  }
-  if (GNUNET_OK ==
-      TEH_plugin->get_refresh_out (TEH_plugin->cls,
-                                   session,
-                                   session_hash,
-                                   coin_off,
-                                   &ev_sig))
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                "Returning cached reply for /refresh/reveal signature\n");
-    return ev_sig;
-  }
-
-  ev_sig.rsa_signature
-    = GNUNET_CRYPTO_rsa_sign_blinded (dki->denom_priv.rsa_private_key,
-                                      commit_coin->coin_ev,
-                                      commit_coin->coin_ev_size);
-  if (NULL == ev_sig.rsa_signature)
-  {
-    GNUNET_break (0);
-    return ev_sig;
-  }
-  if (GNUNET_SYSERR ==
-      TEH_plugin->insert_refresh_out (TEH_plugin->cls,
-                                      session,
-                                      session_hash,
-                                      coin_off,
-                                      &ev_sig))
-  {
-    GNUNET_break (0);
-    GNUNET_CRYPTO_rsa_signature_free (ev_sig.rsa_signature);
-    ev_sig.rsa_signature = NULL;
-  }
-
-  return ev_sig;
-}
-
-
-/**
- * The client request was well-formed, now execute the DB transaction
- * of a "/refresh/reveal" operation.  We use the @a ev_sigs and
- * @a commit_coins to clean up resources after this function returns
- * as we might experience retries of the database transaction.
- *
- * @param connection the MHD connection to handle
- * @param session database session
- * @param session_hash hash identifying the refresh session
- * @param refresh_session information about the refresh operation we are doing
- * @param denom_pubs array of "num_newcoins" denomination keys for the new 
coins
- * @param[out] ev_sigs where to store generated signatures for the new coins,
- *                     array of length "num_newcoins", memory released by the
- *                     caller
- * @param[out] commit_coins array of length "num_newcoins" to be used for
- *                     information about the new coins from the commitment.
- * @return MHD result code
- */
-static int
-execute_refresh_reveal_transaction (struct MHD_Connection *connection,
-                                    struct TALER_EXCHANGEDB_Session *session,
-                                    const struct GNUNET_HashCode *session_hash,
-                                    const struct 
TALER_EXCHANGEDB_RefreshSession *refresh_session,
-                                    const struct TALER_DenominationPublicKey 
*denom_pubs,
-                                    struct TALER_DenominationSignature 
*ev_sigs,
-                                    struct TALER_EXCHANGEDB_RefreshCommitCoin 
*commit_coins)
-{
-  unsigned int j;
-  struct TEH_KS_StateHandle *key_state;
-  int ret;
-
-  START_TRANSACTION (session, connection);
-  key_state = TEH_KS_acquire ();
-  for (j=0;j<refresh_session->num_newcoins;j++)
-  {
-    if (NULL == ev_sigs[j].rsa_signature) /* could be non-NULL during retries 
*/
-      ev_sigs[j] = refresh_exchange_coin (connection,
-                                          session,
-                                          session_hash,
-                                          key_state,
-                                          &denom_pubs[j],
-                                          &commit_coins[j],
-                                          j);
-    if (NULL == ev_sigs[j].rsa_signature)
-    {
-      TEH_plugin->rollback (TEH_plugin->cls,
-                            session);
-      ret = TEH_RESPONSE_reply_internal_db_error (connection,
-                                                 
TALER_EC_REFRESH_REVEAL_SIGNING_ERROR);
-      goto cleanup;
-    }
-  }
-  COMMIT_TRANSACTION (session, connection);
-  ret = TEH_RESPONSE_reply_refresh_reveal_success (connection,
-                                                   
refresh_session->num_newcoins,
-                                                   ev_sigs);
- cleanup:
-  TEH_KS_release (key_state);
-  return ret;
-}
-
-
-/**
- * Execute a "/refresh/reveal".  The client is revealing to us the
- * transfer keys for @a #TALER_CNC_KAPPA-1 sets of coins.  Verify that the
- * revealed transfer keys would allow linkage to the blinded coins,
- * and if so, return the signed coins for corresponding to the set of
- * coins that was not chosen.
- *
- * @param connection the MHD connection to handle
- * @param session_hash hash identifying the refresh session
- * @param transfer_privs array with the revealed transfer keys,
- *                      length must be #TALER_CNC_KAPPA - 1
- * @return MHD result code
- */
-int
-TEH_DB_execute_refresh_reveal (struct MHD_Connection *connection,
-                               const struct GNUNET_HashCode *session_hash,
-                               struct TALER_TransferPrivateKeyP 
*transfer_privs)
-{
-  int res;
-  struct TALER_EXCHANGEDB_Session *session;
-  struct TALER_EXCHANGEDB_RefreshSession refresh_session;
-  struct TALER_DenominationPublicKey *denom_pubs;
-  struct TALER_DenominationSignature *ev_sigs;
-  struct TALER_EXCHANGEDB_RefreshCommitCoin *commit_coins;
-  unsigned int i;
-  unsigned int j;
-  unsigned int off;
-  struct GNUNET_HashContext *hash_context;
-  struct GNUNET_HashCode sh_check;
-  int ret;
-  struct TALER_TransferPublicKeyP gamma_tp;
-
-  if (NULL == (session = TEH_plugin->get_session (TEH_plugin->cls)))
-  {
-    GNUNET_break (0);
-    return TEH_RESPONSE_reply_internal_db_error (connection,
-                                                TALER_EC_DB_SETUP_FAILED);
-  }
-
-  res = TEH_plugin->get_refresh_session (TEH_plugin->cls,
-                                         session,
-                                         session_hash,
-                                         &refresh_session);
-  if (GNUNET_NO == res)
-    return TEH_RESPONSE_reply_arg_invalid (connection,
-                                          
TALER_EC_REFRESH_REVEAL_SESSION_UNKNOWN,
-                                           "session_hash");
-  if ( (GNUNET_SYSERR == res) ||
-       (refresh_session.noreveal_index >= TALER_CNC_KAPPA) )
-    return TEH_RESPONSE_reply_internal_db_error (connection,
-                                                
TALER_EC_REFRESH_REVEAL_DB_FETCH_SESSION_ERROR);
-  denom_pubs = GNUNET_new_array (refresh_session.num_newcoins,
-                                 struct TALER_DenominationPublicKey);
-  if (GNUNET_OK !=
-      TEH_plugin->get_refresh_order (TEH_plugin->cls,
-                                     session,
-                                     session_hash,
-                                     refresh_session.num_newcoins,
-                                     denom_pubs))
-  {
-    GNUNET_break (0);
-    GNUNET_free (denom_pubs);
-    GNUNET_CRYPTO_rsa_signature_free 
(refresh_session.melt.coin.denom_sig.rsa_signature);
-    GNUNET_CRYPTO_rsa_public_key_free 
(refresh_session.melt.coin.denom_pub.rsa_public_key);
-    return (MHD_YES == TEH_RESPONSE_reply_internal_db_error (connection,
-                                                            
TALER_EC_REFRESH_REVEAL_DB_FETCH_ORDER_ERROR))
-        ? GNUNET_NO : GNUNET_SYSERR;
-  }
-
-  hash_context = GNUNET_CRYPTO_hash_context_start ();
-  /* first, iterate over transfer public keys for hash_context */
-  off = 0;
-  for (i=0;i<TALER_CNC_KAPPA;i++)
-  {
-    if (i == refresh_session.noreveal_index)
-    {
-      off = 1;
-      /* obtain gamma_tp from db */
-      if (GNUNET_OK !=
-          TEH_plugin->get_refresh_transfer_public_key (TEH_plugin->cls,
-                                                       session,
-                                                       session_hash,
-                                                       &gamma_tp))
-      {
-        GNUNET_break (0);
-        GNUNET_free (denom_pubs);
-        GNUNET_CRYPTO_rsa_signature_free 
(refresh_session.melt.coin.denom_sig.rsa_signature);
-        GNUNET_CRYPTO_rsa_public_key_free 
(refresh_session.melt.coin.denom_pub.rsa_public_key);
-        GNUNET_CRYPTO_hash_context_abort (hash_context);
-        return (MHD_YES == TEH_RESPONSE_reply_internal_db_error (connection,
-                                                                
TALER_EC_REFRESH_REVEAL_DB_FETCH_TRANSFER_ERROR))
-          ? GNUNET_NO : GNUNET_SYSERR;
-      }
-      GNUNET_CRYPTO_hash_context_read (hash_context,
-                                       &gamma_tp,
-                                       sizeof (struct 
TALER_TransferPublicKeyP));
-    }
-    else
-    {
-      /* compute tp from private key */
-      struct TALER_TransferPublicKeyP tp;
-
-      GNUNET_CRYPTO_ecdhe_key_get_public (&transfer_privs[i - off].ecdhe_priv,
-                                          &tp.ecdhe_pub);
-      GNUNET_CRYPTO_hash_context_read (hash_context,
-                                       &tp,
-                                       sizeof (struct 
TALER_TransferPublicKeyP));
-    }
-  }
-
-  /* next, add all of the hashes from the denomination keys to the
-     hash_context */
-  {
-    struct TALER_DenominationPublicKey 
denom_pubs[refresh_session.num_newcoins];
-
-    if (GNUNET_OK !=
-        TEH_plugin->get_refresh_order (TEH_plugin->cls,
-                                       session,
-                                       session_hash,
-                                       refresh_session.num_newcoins,
-                                       denom_pubs))
-    {
-      GNUNET_break (0);
-      GNUNET_free (denom_pubs);
-      GNUNET_CRYPTO_rsa_signature_free 
(refresh_session.melt.coin.denom_sig.rsa_signature);
-      GNUNET_CRYPTO_rsa_public_key_free 
(refresh_session.melt.coin.denom_pub.rsa_public_key);
-      GNUNET_CRYPTO_hash_context_abort (hash_context);
-      return (MHD_YES == TEH_RESPONSE_reply_internal_db_error (connection,
-                                                              
TALER_EC_REFRESH_REVEAL_DB_FETCH_ORDER_ERROR))
-        ? GNUNET_NO : GNUNET_SYSERR;
-    }
-    for (i=0;i<refresh_session.num_newcoins;i++)
-    {
-      char *buf;
-      size_t buf_size;
-
-      buf_size = GNUNET_CRYPTO_rsa_public_key_encode 
(denom_pubs[i].rsa_public_key,
-                                                      &buf);
-      GNUNET_CRYPTO_hash_context_read (hash_context,
-                                       buf,
-                                       buf_size);
-      GNUNET_free (buf);
-      GNUNET_CRYPTO_rsa_public_key_free (denom_pubs[i].rsa_public_key);
-    }
-  }
-
-  /* next, add public key of coin and amount being refreshed */
-  {
-    struct TALER_AmountNBO melt_amountn;
-
-    GNUNET_CRYPTO_hash_context_read (hash_context,
-                                     &refresh_session.melt.coin.coin_pub,
-                                     sizeof (struct 
TALER_CoinSpendPublicKeyP));
-    TALER_amount_hton (&melt_amountn,
-                       &refresh_session.melt.amount_with_fee);
-    GNUNET_CRYPTO_hash_context_read (hash_context,
-                                     &melt_amountn,
-                                     sizeof (struct TALER_AmountNBO));
-  }
-
-  commit_coins = GNUNET_new_array (refresh_session.num_newcoins,
-                                   struct TALER_EXCHANGEDB_RefreshCommitCoin);
-  off = 0;
-  for (i=0;i<TALER_CNC_KAPPA;i++)
-  {
-    if (i == refresh_session.noreveal_index)
-    {
-      off = 1;
-      /* obtain commit_coins for the selected gamma value from DB */
-      if (GNUNET_OK !=
-          TEH_plugin->get_refresh_commit_coins (TEH_plugin->cls,
-                                                session,
-                                                session_hash,
-                                                refresh_session.num_newcoins,
-                                                commit_coins))
-      {
-        GNUNET_break (0);
-        GNUNET_free (denom_pubs);
-        GNUNET_CRYPTO_rsa_signature_free 
(refresh_session.melt.coin.denom_sig.rsa_signature);
-        GNUNET_CRYPTO_rsa_public_key_free 
(refresh_session.melt.coin.denom_pub.rsa_public_key);
-        GNUNET_CRYPTO_hash_context_abort (hash_context);
-        return TEH_RESPONSE_reply_internal_db_error (connection,
-                                                    
TALER_EC_REFRESH_REVEAL_DB_FETCH_COMMIT_ERROR);
-      }
-      /* add envelopes to hash_context */
-      for (j=0;j<refresh_session.num_newcoins;j++)
-      {
-        GNUNET_CRYPTO_hash_context_read (hash_context,
-                                         commit_coins[j].coin_ev,
-                                         commit_coins[j].coin_ev_size);
-      }
-      continue;
-    }
-    if (GNUNET_OK !=
-        (res = check_commitment (connection,
-                                 session,
-                                 session_hash,
-                                 i,
-                                 &transfer_privs[i - off],
-                                 &refresh_session.melt,
-                                 refresh_session.num_newcoins,
-                                 denom_pubs,
-                                 hash_context)))
-    {
-      GNUNET_break_op (0);
-      for (j=0;j<refresh_session.num_newcoins;j++)
-      {
-        GNUNET_CRYPTO_rsa_public_key_free (denom_pubs[j].rsa_public_key);
-        GNUNET_free (commit_coins[j].coin_ev);
-      }
-      GNUNET_free (commit_coins);
-      GNUNET_free (denom_pubs);
-      GNUNET_CRYPTO_rsa_signature_free 
(refresh_session.melt.coin.denom_sig.rsa_signature);
-      GNUNET_CRYPTO_rsa_public_key_free 
(refresh_session.melt.coin.denom_pub.rsa_public_key);
-      GNUNET_CRYPTO_hash_context_abort (hash_context);
-      return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
-    }
-  }
-
-  /* Check session hash matches */
-  GNUNET_CRYPTO_hash_context_finish (hash_context,
-                                     &sh_check);
-  if (0 != memcmp (&sh_check,
-                   session_hash,
-                   sizeof (struct GNUNET_HashCode)))
-  {
-    GNUNET_break_op (0);
-    ret = TEH_RESPONSE_reply_refresh_reveal_missmatch (connection,
-                                                       &refresh_session,
-                                                       commit_coins,
-                                                       denom_pubs,
-                                                       &gamma_tp);
-    for (j=0;j<refresh_session.num_newcoins;j++)
-    {
-      GNUNET_free (commit_coins[j].coin_ev);
-      GNUNET_CRYPTO_rsa_public_key_free (denom_pubs[j].rsa_public_key);
-    }
-    GNUNET_free (commit_coins);
-    GNUNET_free (denom_pubs);
-    GNUNET_CRYPTO_rsa_signature_free 
(refresh_session.melt.coin.denom_sig.rsa_signature);
-    GNUNET_CRYPTO_rsa_public_key_free 
(refresh_session.melt.coin.denom_pub.rsa_public_key);
-
-    return ret;
-  }
-
-  /* Client request OK, start transaction */
-  ev_sigs = GNUNET_new_array (refresh_session.num_newcoins,
-                              struct TALER_DenominationSignature);
-
-  /* FIXME: might need to store revealed transfer private keys for
-     the auditor for later; should pass them as arguments here! #4792*/
-  res = execute_refresh_reveal_transaction (connection,
-                                            session,
-                                            session_hash,
-                                            &refresh_session,
-                                            denom_pubs,
-                                            ev_sigs,
-                                            commit_coins);
-  for (i=0;i<refresh_session.num_newcoins;i++)
-  {
-    if (NULL != ev_sigs[i].rsa_signature)
-      GNUNET_CRYPTO_rsa_signature_free (ev_sigs[i].rsa_signature);
-    GNUNET_free (commit_coins[i].coin_ev);
-  }
-  for (j=0;j<refresh_session.num_newcoins;j++)
-    if (NULL != denom_pubs[j].rsa_public_key)
-      GNUNET_CRYPTO_rsa_public_key_free (denom_pubs[j].rsa_public_key);
-  GNUNET_CRYPTO_rsa_signature_free 
(refresh_session.melt.coin.denom_sig.rsa_signature);
-  GNUNET_CRYPTO_rsa_public_key_free 
(refresh_session.melt.coin.denom_pub.rsa_public_key);
-  GNUNET_free (ev_sigs);
-  GNUNET_free (denom_pubs);
-  GNUNET_free (commit_coins);
-  return res;
-}
-
-
-/**
- * Closure for #handle_transfer_data().
- */
-struct HTD_Context
-{
-
-  /**
-   * Session link data we collect.
-   */
-  struct TEH_RESPONSE_LinkSessionInfo *sessions;
-
-  /**
-   * Database session. Nothing to do with @a sessions.
-   */
-  struct TALER_EXCHANGEDB_Session *session;
-
-  /**
-   * MHD connection, for queueing replies.
-   */
-  struct MHD_Connection *connection;
-
-  /**
-   * Number of sessions the coin was melted into.
-   */
-  unsigned int num_sessions;
-
-  /**
-   * How are we expected to proceed. #GNUNET_SYSERR if we
-   * failed to return an error (should return #MHD_NO).
-   * #GNUNET_NO if we succeeded in queueing an MHD error
-   * (should return #MHD_YES from #TEH_execute_refresh_link),
-   * #GNUNET_OK if we should call #TEH_RESPONSE_reply_refresh_link_success().
-   */
-  int status;
-};
-
-
-/**
- * Function called with the session hashes and transfer secret
- * information for a given coin.  Gets the linkage data and
- * builds the reply for the client.
- *
- *
- * @param cls closure, a `struct HTD_Context`
- * @param session_hash a session the coin was melted in
- * @param transfer_pub public transfer key for the session
- */
-static void
-handle_transfer_data (void *cls,
-                      const struct GNUNET_HashCode *session_hash,
-                      const struct TALER_TransferPublicKeyP *transfer_pub)
-{
-  struct HTD_Context *ctx = cls;
-  struct TALER_EXCHANGEDB_LinkDataList *ldl;
-  struct TEH_RESPONSE_LinkSessionInfo *lsi;
-
-  if (GNUNET_OK != ctx->status)
-    return;
-  ldl = TEH_plugin->get_link_data_list (TEH_plugin->cls,
-                                        ctx->session,
-                                        session_hash);
-  if (NULL == ldl)
-  {
-    ctx->status = GNUNET_NO;
-    if (MHD_NO ==
-        TEH_RESPONSE_reply_json_pack (ctx->connection,
-                                      MHD_HTTP_NOT_FOUND,
-                                      "{s:s}",
-                                      "error",
-                                      "link data not found (link)"))
-      ctx->status = GNUNET_SYSERR;
-    return;
-  }
-  GNUNET_array_grow (ctx->sessions,
-                     ctx->num_sessions,
-                     ctx->num_sessions + 1);
-  lsi = &ctx->sessions[ctx->num_sessions - 1];
-  lsi->transfer_pub = *transfer_pub;
-  lsi->ldl = ldl;
-}
-
-
-/**
- * Execute a "/refresh/link".  Returns the linkage information that
- * will allow the owner of a coin to follow the refresh trail to
- * the refreshed coin.
- *
- * @param connection the MHD connection to handle
- * @param coin_pub public key of the coin to link
- * @return MHD result code
- */
-int
-TEH_DB_execute_refresh_link (struct MHD_Connection *connection,
-                             const struct TALER_CoinSpendPublicKeyP *coin_pub)
-{
-  struct HTD_Context ctx;
-  int res;
-  unsigned int i;
-
-  if (NULL == (ctx.session = TEH_plugin->get_session (TEH_plugin->cls)))
-  {
-    GNUNET_break (0);
-    return TEH_RESPONSE_reply_internal_db_error (connection,
-                                                TALER_EC_DB_SETUP_FAILED);
-  }
-  ctx.connection = connection;
-  ctx.num_sessions = 0;
-  ctx.sessions = NULL;
-  ctx.status = GNUNET_OK;
-  res = TEH_plugin->get_transfer (TEH_plugin->cls,
-                                  ctx.session,
-                                  coin_pub,
-                                  &handle_transfer_data,
-                                  &ctx);
-  if (GNUNET_SYSERR == ctx.status)
-  {
-    res = MHD_NO;
-    goto cleanup;
-  }
-  if (GNUNET_NO == ctx.status)
-  {
-    res = MHD_YES;
-    goto cleanup;
-  }
-  GNUNET_assert (GNUNET_OK == ctx.status);
-  if (0 == ctx.num_sessions)
-    return TEH_RESPONSE_reply_arg_unknown (connection,
-                                          TALER_EC_REFRESH_LINK_COIN_UNKNOWN,
-                                           "coin_pub");
-  res = TEH_RESPONSE_reply_refresh_link_success (connection,
-                                                 ctx.num_sessions,
-                                                 ctx.sessions);
- cleanup:
-  for (i=0;i<ctx.num_sessions;i++)
-    TEH_plugin->free_link_data_list (TEH_plugin->cls,
-                                     ctx.sessions[i].ldl);
-  GNUNET_free_non_null (ctx.sessions);
-  return res;
-}
-
-
 /* end of taler-exchange-httpd_db.c */
diff --git a/src/exchange/taler-exchange-httpd_db.h 
b/src/exchange/taler-exchange-httpd_db.h
index 85a1604..a61f932 100644
--- a/src/exchange/taler-exchange-httpd_db.h
+++ b/src/exchange/taler-exchange-httpd_db.h
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  Copyright (C) 2014, 2015 GNUnet e.V.
+  Copyright (C) 2014-2017 GNUnet e.V.
 
   TALER is free software; you can redistribute it and/or modify it under the
   terms of the GNU General Public License as published by the Free Software
@@ -83,97 +83,5 @@ TEH_DB_calculate_transaction_list_totals (struct 
TALER_EXCHANGEDB_TransactionLis
                                          struct TALER_Amount *ret);
 
 
-/**
- * @brief Details about a melt operation of an individual coin.
- */
-struct TEH_DB_MeltDetails
-{
-
-  /**
-   * Information about the coin being melted.
-   */
-  struct TALER_CoinPublicInfo coin_info;
-
-  /**
-   * Signature allowing the melt (using
-   * a `struct TALER_EXCHANGEDB_RefreshMeltConfirmSignRequestBody`) to sign 
over.
-   */
-  struct TALER_CoinSpendSignatureP melt_sig;
-
-  /**
-   * How much of the coin's value did the client allow to be melted?
-   * This amount includes the fees, so the final amount contributed
-   * to the melt is this value minus the fee for melting the coin.
-   */
-  struct TALER_Amount melt_amount_with_fee;
-
-  /**
-   * What fee is earned by the exchange?  Set delayed during
-   * #verify_coin_public_info().
-   */
-  struct TALER_Amount melt_fee;
-};
-
-
-/**
- * Execute a "/refresh/melt". We have been given a list of valid
- * coins and a request to melt them into the given
- * @a refresh_session_pub.  Check that the coins all have the
- * required value left and if so, store that they have been
- * melted and confirm the melting operation to the client.
- *
- * @param connection the MHD connection to handle
- * @param session_hash hash code of the session the coins are melted into
- * @param num_new_denoms number of entries in @a denom_pubs, size of 
y-dimension of @a commit_coin array
- * @param denom_pubs array of public denomination keys for the refresh (?)
- * @param coin_melt_detail signatures and (residual) value of and information 
about the respective coin to be melted
- * @param commit_coin 2d array of coin commitments (what the exchange is to 
sign
- *                    once the "/refres/reveal" of cut and choose is done)
- * @param transfer_pubs array of transfer public keys (what the exchange is
- *                    to return via "/refresh/link" to enable linkage in the
- *                    future) of length #TALER_CNC_KAPPA
- * @return MHD result code
- */
-int
-TEH_DB_execute_refresh_melt (struct MHD_Connection *connection,
-                             const struct GNUNET_HashCode *session_hash,
-                             unsigned int num_new_denoms,
-                             const struct TALER_DenominationPublicKey 
*denom_pubs,
-                             const struct TEH_DB_MeltDetails *coin_melt_detail,
-                             struct TALER_EXCHANGEDB_RefreshCommitCoin *const* 
commit_coin,
-                             const struct TALER_TransferPublicKeyP 
*transfer_pubs);
-
-
-/**
- * Execute a "/refresh/reveal".  The client is revealing to us the
- * transfer keys for #TALER_CNC_KAPPA-1 sets of coins.  Verify that the
- * revealed transfer keys would allow linkage to the blinded coins,
- * and if so, return the signed coins for corresponding to the set of
- * coins that was not chosen.
- *
- * @param connection the MHD connection to handle
- * @param session_hash hash over the refresh session
- * @param transfer_privs array of length #TALER_CNC_KAPPA-1 with the revealed 
transfer keys
- * @return MHD result code
- */
-int
-TEH_DB_execute_refresh_reveal (struct MHD_Connection *connection,
-                               const struct GNUNET_HashCode *session_hash,
-                               struct TALER_TransferPrivateKeyP 
*transfer_privs);
-
-
-/**
- * Execute a "/refresh/link".  Returns the linkage information that
- * will allow the owner of a coin to follow the refresh trail to the
- * refreshed coin.
- *
- * @param connection the MHD connection to handle
- * @param coin_pub public key of the coin to link
- * @return MHD result code
- */
-int
-TEH_DB_execute_refresh_link (struct MHD_Connection *connection,
-                             const struct TALER_CoinSpendPublicKeyP *coin_pub);
-
 #endif
 /* TALER_EXCHANGE_HTTPD_DB_H */
diff --git a/src/exchange/taler-exchange-httpd_refresh_link.c 
b/src/exchange/taler-exchange-httpd_refresh_link.c
new file mode 100644
index 0000000..c85431f
--- /dev/null
+++ b/src/exchange/taler-exchange-httpd_refresh_link.c
@@ -0,0 +1,286 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2014-2017 Inria & GNUnet e.V.
+
+  TALER is free software; you can redistribute it and/or modify it under the
+  terms of the GNU Affero General Public License as published by the Free 
Software
+  Foundation; either version 3, or (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+  A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more 
details.
+
+  You should have received a copy of the GNU Affero General Public License 
along with
+  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-exchange-httpd_refresh_link.c
+ * @brief Handle /refresh/link requests
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <jansson.h>
+#include <microhttpd.h>
+#include "taler-exchange-httpd_parsing.h"
+#include "taler-exchange-httpd_mhd.h"
+#include "taler-exchange-httpd_refresh_link.h"
+#include "taler-exchange-httpd_responses.h"
+#include "taler-exchange-httpd_keystate.h"
+
+
+/**
+ * @brief Information for each session a coin was melted into.
+ */
+struct TEH_RESPONSE_LinkSessionInfo
+{
+  /**
+   * Transfer public key of the coin.
+   */
+  struct TALER_TransferPublicKeyP transfer_pub;
+
+  /**
+   * Linked data of coins being created in the session.
+   */
+  struct TALER_EXCHANGEDB_LinkDataList *ldl;
+
+};
+
+
+/**
+ * Closure for #handle_transfer_data().
+ */
+struct HTD_Context
+{
+
+  /**
+   * Session link data we collect.
+   */
+  struct TEH_RESPONSE_LinkSessionInfo *sessions;
+
+  /**
+   * Database session. Nothing to do with @a sessions.
+   */
+  struct TALER_EXCHANGEDB_Session *session;
+
+  /**
+   * MHD connection, for queueing replies.
+   */
+  struct MHD_Connection *connection;
+
+  /**
+   * Number of sessions the coin was melted into.
+   */
+  unsigned int num_sessions;
+
+  /**
+   * How are we expected to proceed. #GNUNET_SYSERR if we
+   * failed to return an error (should return #MHD_NO).
+   * #GNUNET_NO if we succeeded in queueing an MHD error
+   * (should return #MHD_YES from #TEH_execute_refresh_link),
+   * #GNUNET_OK if we should call #reply_refresh_link_success().
+   */
+  int status;
+};
+
+
+/**
+ * Send a response for "/refresh/link".
+ *
+ * @param connection the connection to send the response to
+ * @param num_sessions number of sessions the coin was used in
+ * @param sessions array of @a num_session entries with
+ *                  information for each session
+ * @return a MHD result code
+ */
+static int
+reply_refresh_link_success (struct MHD_Connection *connection,
+                           unsigned int num_sessions,
+                           const struct TEH_RESPONSE_LinkSessionInfo *sessions)
+{
+  json_t *root;
+  json_t *mlist;
+  int res;
+  unsigned int i;
+
+  mlist = json_array ();
+  for (i=0;i<num_sessions;i++)
+  {
+    const struct TALER_EXCHANGEDB_LinkDataList *pos;
+    json_t *list = json_array ();
+
+    for (pos = sessions[i].ldl; NULL != pos; pos = pos->next)
+    {
+      json_t *obj;
+
+      obj = json_object ();
+      json_object_set_new (obj,
+                           "denom_pub",
+                           GNUNET_JSON_from_rsa_public_key 
(pos->denom_pub.rsa_public_key));
+      json_object_set_new (obj,
+                           "ev_sig",
+                           GNUNET_JSON_from_rsa_signature 
(pos->ev_sig.rsa_signature));
+      GNUNET_assert (0 ==
+                     json_array_append_new (list,
+                                            obj));
+    }
+    root = json_object ();
+    json_object_set_new (root,
+                         "new_coins",
+                         list);
+    json_object_set_new (root,
+                         "transfer_pub",
+                         GNUNET_JSON_from_data_auto 
(&sessions[i].transfer_pub));
+    GNUNET_assert (0 ==
+                   json_array_append_new (mlist,
+                                          root));
+  }
+  res = TEH_RESPONSE_reply_json (connection,
+                                 mlist,
+                                 MHD_HTTP_OK);
+  json_decref (mlist);
+  return res;
+}
+
+
+/**
+ * Function called with the session hashes and transfer secret
+ * information for a given coin.  Gets the linkage data and
+ * builds the reply for the client.
+ *
+ *
+ * @param cls closure, a `struct HTD_Context`
+ * @param session_hash a session the coin was melted in
+ * @param transfer_pub public transfer key for the session
+ */
+static void
+handle_transfer_data (void *cls,
+                      const struct GNUNET_HashCode *session_hash,
+                      const struct TALER_TransferPublicKeyP *transfer_pub)
+{
+  struct HTD_Context *ctx = cls;
+  struct TALER_EXCHANGEDB_LinkDataList *ldl;
+  struct TEH_RESPONSE_LinkSessionInfo *lsi;
+
+  if (GNUNET_OK != ctx->status)
+    return;
+  ldl = TEH_plugin->get_link_data_list (TEH_plugin->cls,
+                                        ctx->session,
+                                        session_hash);
+  if (NULL == ldl)
+  {
+    ctx->status = GNUNET_NO;
+    if (MHD_NO ==
+        TEH_RESPONSE_reply_json_pack (ctx->connection,
+                                      MHD_HTTP_NOT_FOUND,
+                                      "{s:s}",
+                                      "error",
+                                      "link data not found (link)"))
+      ctx->status = GNUNET_SYSERR;
+    return;
+  }
+  GNUNET_array_grow (ctx->sessions,
+                     ctx->num_sessions,
+                     ctx->num_sessions + 1);
+  lsi = &ctx->sessions[ctx->num_sessions - 1];
+  lsi->transfer_pub = *transfer_pub;
+  lsi->ldl = ldl;
+}
+
+
+/**
+ * Execute a "/refresh/link".  Returns the linkage information that
+ * will allow the owner of a coin to follow the refresh trail to
+ * the refreshed coin.
+ *
+ * @param connection the MHD connection to handle
+ * @param coin_pub public key of the coin to link
+ * @return MHD result code
+ */
+static int
+execute_refresh_link (struct MHD_Connection *connection,
+                     const struct TALER_CoinSpendPublicKeyP *coin_pub)
+{
+  struct HTD_Context ctx;
+  int res;
+  unsigned int i;
+
+  if (NULL == (ctx.session = TEH_plugin->get_session (TEH_plugin->cls)))
+  {
+    GNUNET_break (0);
+    return TEH_RESPONSE_reply_internal_db_error (connection,
+                                                TALER_EC_DB_SETUP_FAILED);
+  }
+  ctx.connection = connection;
+  ctx.num_sessions = 0;
+  ctx.sessions = NULL;
+  ctx.status = GNUNET_OK;
+  res = TEH_plugin->get_transfer (TEH_plugin->cls,
+                                  ctx.session,
+                                  coin_pub,
+                                  &handle_transfer_data,
+                                  &ctx);
+  if (GNUNET_SYSERR == ctx.status)
+  {
+    res = MHD_NO;
+    goto cleanup;
+  }
+  if (GNUNET_NO == ctx.status)
+  {
+    res = MHD_YES;
+    goto cleanup;
+  }
+  GNUNET_assert (GNUNET_OK == ctx.status);
+  if (0 == ctx.num_sessions)
+    return TEH_RESPONSE_reply_arg_unknown (connection,
+                                          TALER_EC_REFRESH_LINK_COIN_UNKNOWN,
+                                           "coin_pub");
+  res = reply_refresh_link_success (connection,
+                                   ctx.num_sessions,
+                                   ctx.sessions);
+ cleanup:
+  for (i=0;i<ctx.num_sessions;i++)
+    TEH_plugin->free_link_data_list (TEH_plugin->cls,
+                                     ctx.sessions[i].ldl);
+  GNUNET_free_non_null (ctx.sessions);
+  return res;
+}
+
+
+/**
+ * Handle a "/refresh/link" request.  Note that for "/refresh/link"
+ * we do use a simple HTTP GET, and a HTTP POST!
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[in,out] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+  */
+int
+TEH_REFRESH_handler_refresh_link (struct TEH_RequestHandler *rh,
+                                  struct MHD_Connection *connection,
+                                  void **connection_cls,
+                                  const char *upload_data,
+                                  size_t *upload_data_size)
+{
+  struct TALER_CoinSpendPublicKeyP coin_pub;
+  int res;
+
+  res = TEH_PARSE_mhd_request_arg_data (connection,
+                                        "coin_pub",
+                                        &coin_pub,
+                                        sizeof (struct 
TALER_CoinSpendPublicKeyP));
+  if (GNUNET_SYSERR == res)
+    return MHD_NO;
+  if (GNUNET_OK != res)
+    return MHD_YES;
+  return execute_refresh_link (connection,
+                              &coin_pub);
+}
+
+
+/* end of taler-exchange-httpd_refresh_link.c */
diff --git a/src/exchange/taler-exchange-httpd_refresh_link.h 
b/src/exchange/taler-exchange-httpd_refresh_link.h
new file mode 100644
index 0000000..037b0d3
--- /dev/null
+++ b/src/exchange/taler-exchange-httpd_refresh_link.h
@@ -0,0 +1,49 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2014-2017 GNUnet e.V.
+
+  TALER is free software; you can redistribute it and/or modify it under the
+  terms of the GNU Affero General Public License as published by the Free 
Software
+  Foundation; either version 3, or (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+  A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more 
details.
+
+  You should have received a copy of the GNU Affero General Public License 
along with
+  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-exchange-httpd_refresh_link.h
+ * @brief Handle /refresh/link requests
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#ifndef TALER_EXCHANGE_HTTPD_REFRESH_LINK_H
+#define TALER_EXCHANGE_HTTPD_REFRESH_LINK_H
+
+#include <gnunet/gnunet_util_lib.h>
+#include <microhttpd.h>
+#include "taler-exchange-httpd.h"
+
+
+/**
+ * Handle a "/refresh/link" request
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[in,out] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+  */
+int
+TEH_REFRESH_handler_refresh_link (struct TEH_RequestHandler *rh,
+                                  struct MHD_Connection *connection,
+                                  void **connection_cls,
+                                  const char *upload_data,
+                                  size_t *upload_data_size);
+
+
+#endif
diff --git a/src/exchange/taler-exchange-httpd_refresh.c 
b/src/exchange/taler-exchange-httpd_refresh_melt.c
similarity index 54%
rename from src/exchange/taler-exchange-httpd_refresh.c
rename to src/exchange/taler-exchange-httpd_refresh_melt.c
index 3a8875f..1c4e5e8 100644
--- a/src/exchange/taler-exchange-httpd_refresh.c
+++ b/src/exchange/taler-exchange-httpd_refresh_melt.c
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  Copyright (C) 2014, 2015, 2016 Inria & GNUnet e.V.
+  Copyright (C) 2014-2017 Inria & GNUnet e.V.
 
   TALER is free software; you can redistribute it and/or modify it under the
   terms of the GNU Affero General Public License as published by the Free 
Software
@@ -14,8 +14,8 @@
   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
 */
 /**
- * @file taler-exchange-httpd_refresh.c
- * @brief Handle /refresh/ requests
+ * @file taler-exchange-httpd_refresh_melt.c
+ * @brief Handle /refresh/melt requests
  * @author Florian Dold
  * @author Benedikt Mueller
  * @author Christian Grothoff
@@ -26,12 +26,455 @@
 #include <microhttpd.h>
 #include "taler-exchange-httpd_parsing.h"
 #include "taler-exchange-httpd_mhd.h"
-#include "taler-exchange-httpd_refresh.h"
+#include "taler-exchange-httpd_refresh_melt.h"
 #include "taler-exchange-httpd_responses.h"
 #include "taler-exchange-httpd_keystate.h"
 
 
 /**
+ * How often should we retry a transaction before giving up
+ * (for transactions resulting in serialization/dead locks only).
+ */
+#define MAX_TRANSACTION_COMMIT_RETRIES 3
+
+/**
+ * Code to begin a transaction, must be inline as we define a block
+ * that ends with #COMMIT_TRANSACTION() within which we perform a number
+ * of retries.  Note that this code may call "return" internally, so
+ * it must be called within a function where any cleanup will be done
+ * by the caller. Furthermore, the function's return value must
+ * match that of a #TEH_RESPONSE_reply_internal_db_error() status code.
+ *
+ * @param session session handle
+ * @param connection connection handle
+ */
+#define START_TRANSACTION(session,connection)                 \
+{ /* start new scope, will be ended by COMMIT_TRANSACTION() */\
+  unsigned int transaction_retries = 0;                       \
+  enum GNUNET_DB_QueryStatus transaction_commit_result;       \
+transaction_start_label: /* we will use goto for retries */   \
+  if (GNUNET_OK !=                                            \
+      TEH_plugin->start (TEH_plugin->cls,                     \
+                         session))                            \
+  {                                                           \
+    GNUNET_break (0);                                         \
+    return TEH_RESPONSE_reply_internal_db_error (connection, \
+                                                TALER_EC_DB_START_FAILED);     
     \
+  }
+
+/**
+ * Code to conclude a transaction, dual to #START_TRANSACTION().  Note
+ * that this code may call "return" internally, so it must be called
+ * within a function where any cleanup will be done by the caller.
+ * Furthermore, the function's return value must match that of a
+ * #TEH_RESPONSE_reply_internal_db_error() status code.
+ *
+ * @param session session handle
+ * @param connection connection handle
+ */
+#define COMMIT_TRANSACTION(session,connection)                             \
+  transaction_commit_result =                                              \
+    TEH_plugin->commit (TEH_plugin->cls,                                   \
+                        session);                                          \
+  if (GNUNET_DB_STATUS_HARD_ERROR == transaction_commit_result)            \
+  {                                                                        \
+    TALER_LOG_WARNING ("Transaction commit failed in %s\n", __FUNCTION__); \
+    return TEH_RESPONSE_reply_commit_error (connection, \
+                                           TALER_EC_DB_COMMIT_FAILED_HARD); \
+  }                                                       \
+  if (GNUNET_DB_STATUS_SOFT_ERROR == transaction_commit_result)            \
+  {                                                                        \
+    TALER_LOG_WARNING ("Transaction commit failed in %s\n", __FUNCTION__); \
+    if (transaction_retries++ <= MAX_TRANSACTION_COMMIT_RETRIES)           \
+      goto transaction_start_label;                                        \
+    TALER_LOG_WARNING ("Transaction commit failed %u times in %s\n",       \
+                       transaction_retries,                                \
+                       __FUNCTION__);                                      \
+    return TEH_RESPONSE_reply_commit_error (connection, \
+                                           
TALER_EC_DB_COMMIT_FAILED_ON_RETRY);                                \
+  }                                                                        \
+} /* end of scope opened by BEGIN_TRANSACTION */
+
+
+/**
+ * Code to include to retry a transaction, must only be used in between
+ * #START_TRANSACTION and #COMMIT_TRANSACTION.
+ *
+ * @param session session handle
+ * @param connection connection handle
+ */
+#define RETRY_TRANSACTION(session,connection)                                  
  \
+  do {                                                                         
  \
+    TEH_plugin->rollback (TEH_plugin->cls,                                     
  \
+                          session);                                            
  \
+    if (transaction_retries++ <= MAX_TRANSACTION_COMMIT_RETRIES)               
  \
+      goto transaction_start_label;                                            
  \
+    TALER_LOG_WARNING ("Transaction commit failed %u times in %s\n",           
  \
+                       transaction_retries,                                    
  \
+                       __FUNCTION__);                                          
  \
+    return TEH_RESPONSE_reply_commit_error (connection,                        
  \
+                                           
TALER_EC_DB_COMMIT_FAILED_ON_RETRY); \
+  } while (0)
+
+
+
+
+/**
+ * @brief Details about a melt operation of an individual coin.
+ */
+struct TEH_DB_MeltDetails
+{
+
+  /**
+   * Information about the coin being melted.
+   */
+  struct TALER_CoinPublicInfo coin_info;
+
+  /**
+   * Signature allowing the melt (using
+   * a `struct TALER_EXCHANGEDB_RefreshMeltConfirmSignRequestBody`) to sign 
over.
+   */
+  struct TALER_CoinSpendSignatureP melt_sig;
+
+  /**
+   * How much of the coin's value did the client allow to be melted?
+   * This amount includes the fees, so the final amount contributed
+   * to the melt is this value minus the fee for melting the coin.
+   */
+  struct TALER_Amount melt_amount_with_fee;
+
+  /**
+   * What fee is earned by the exchange?  Set delayed during
+   * #verify_coin_public_info().
+   */
+  struct TALER_Amount melt_fee;
+};
+
+
+/**
+ * Send a response for a failed "/refresh/melt" request.  The
+ * transaction history of the given coin demonstrates that the
+ * @a residual value of the coin is below the @a requested
+ * contribution of the coin for the melt.  Thus, the exchange
+ * refuses the melt operation.
+ *
+ * @param connection the connection to send the response to
+ * @param coin_pub public key of the coin
+ * @param coin_value original value of the coin
+ * @param tl transaction history for the coin
+ * @param requested how much this coin was supposed to contribute, including 
fee
+ * @param residual remaining value of the coin (after subtracting @a tl)
+ * @return a MHD result code
+ */
+static int
+reply_refresh_melt_insufficient_funds (struct MHD_Connection *connection,
+                                      const struct TALER_CoinSpendPublicKeyP 
*coin_pub,
+                                      struct TALER_Amount coin_value,
+                                      struct TALER_EXCHANGEDB_TransactionList 
*tl,
+                                      struct TALER_Amount requested,
+                                      struct TALER_Amount residual)
+{
+  json_t *history;
+
+  history = TEH_RESPONSE_compile_transaction_history (tl);
+  if (NULL == history)
+    return TEH_RESPONSE_reply_internal_db_error (connection,
+                                                
TALER_EC_REFRESH_MELT_HISTORY_DB_ERROR_INSUFFICIENT_FUNDS);
+  return TEH_RESPONSE_reply_json_pack (connection,
+                                       MHD_HTTP_FORBIDDEN,
+                                       "{s:s, s:I, s:o, s:o, s:o, s:o, s:o}",
+                                       "error",
+                                      "insufficient funds",
+                                      "code",
+                                      (json_int_t) 
TALER_EC_REFRESH_MELT_INSUFFICIENT_FUNDS,
+                                       "coin_pub",
+                                       GNUNET_JSON_from_data_auto (coin_pub),
+                                       "original_value",
+                                       TALER_JSON_from_amount (&coin_value),
+                                       "residual_value",
+                                       TALER_JSON_from_amount (&residual),
+                                       "requested_value",
+                                       TALER_JSON_from_amount (&requested),
+                                       "history",
+                                       history);
+}
+
+
+/**
+ * Send a response to a "/refresh/melt" request.
+ *
+ * @param connection the connection to send the response to
+ * @param session_hash hash of the refresh session
+ * @param noreveal_index which index will the client not have to reveal
+ * @return a MHD status code
+ */
+static int
+reply_refresh_melt_success (struct MHD_Connection *connection,
+                           const struct GNUNET_HashCode *session_hash,
+                           uint16_t noreveal_index)
+{
+  struct TALER_RefreshMeltConfirmationPS body;
+  struct TALER_ExchangePublicKeyP pub;
+  struct TALER_ExchangeSignatureP sig;
+  json_t *sig_json;
+
+  body.purpose.size = htonl (sizeof (struct TALER_RefreshMeltConfirmationPS));
+  body.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_MELT);
+  body.session_hash = *session_hash;
+  body.noreveal_index = htons (noreveal_index);
+  body.reserved = htons (0);
+  TEH_KS_sign (&body.purpose,
+               &pub,
+               &sig);
+  sig_json = GNUNET_JSON_from_data_auto (&sig);
+  GNUNET_assert (NULL != sig_json);
+  return TEH_RESPONSE_reply_json_pack (connection,
+                                       MHD_HTTP_OK,
+                                       "{s:i, s:o, s:o}",
+                                       "noreveal_index", (int) noreveal_index,
+                                       "exchange_sig", sig_json,
+                                       "exchange_pub", 
GNUNET_JSON_from_data_auto (&pub));
+}
+
+
+/**
+ * Parse coin melt requests from a JSON object and write them to
+ * the database.
+ *
+ * @param connection the connection to send errors to
+ * @param session the database connection
+ * @param key_state the exchange's key state
+ * @param session_hash hash identifying the refresh session
+ * @param coin_details details about the coin being melted
+ * @param[out] meltp on success, set to melt details
+ * @return #GNUNET_OK on success,
+ *         #GNUNET_NO if an error message was generated,
+ *         #GNUNET_SYSERR on internal errors (no response generated)
+ */
+static int
+refresh_check_melt (struct MHD_Connection *connection,
+                    struct TALER_EXCHANGEDB_Session *session,
+                    const struct TEH_KS_StateHandle *key_state,
+                    const struct GNUNET_HashCode *session_hash,
+                    const struct TEH_DB_MeltDetails *coin_details,
+                    struct TALER_EXCHANGEDB_RefreshMelt *meltp)
+{
+  struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dk;
+  struct TALER_EXCHANGEDB_DenominationKeyInformationP *dki;
+  struct TALER_EXCHANGEDB_TransactionList *tl;
+  struct TALER_Amount coin_value;
+  struct TALER_Amount coin_residual;
+  struct TALER_Amount spent;
+  int res;
+  enum GNUNET_DB_QueryStatus qs;
+
+  dk = TEH_KS_denomination_key_lookup (key_state,
+                                       &coin_details->coin_info.denom_pub,
+                                       TEH_KS_DKU_DEPOSIT);
+  if (NULL == dk)
+    return (MHD_YES ==
+            TEH_RESPONSE_reply_internal_error (connection,
+                                              
TALER_EC_REFRESH_MELT_DB_DENOMINATION_KEY_NOT_FOUND,
+                                              "denomination key no longer 
available while executing transaction"))
+        ? GNUNET_NO : GNUNET_SYSERR;
+  dki = &dk->issue;
+  TALER_amount_ntoh (&coin_value,
+                     &dki->properties.value);
+  /* fee for THIS transaction; the melt amount includes the fee! */
+  spent = coin_details->melt_amount_with_fee;
+  /* add historic transaction costs of this coin */
+  qs = TEH_plugin->get_coin_transactions (TEH_plugin->cls,
+                                          session,
+                                          &coin_details->coin_info.coin_pub,
+                                         &tl);
+  (void) qs; /* FIXME #5010 */
+  if (GNUNET_OK !=
+      TEH_DB_calculate_transaction_list_totals (tl,
+                                               &spent,
+                                               &spent))
+  {
+    GNUNET_break (0);
+    TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
+                                            tl);
+    return (MHD_YES ==
+            TEH_RESPONSE_reply_internal_db_error (connection,
+                                                 
TALER_EC_REFRESH_MELT_COIN_HISTORY_COMPUTATION_FAILED))
+      ? GNUNET_NO : GNUNET_SYSERR;
+  }
+  /* Refuse to refresh when the coin's value is insufficient
+     for the cost of all transactions. */
+  if (TALER_amount_cmp (&coin_value,
+                        &spent) < 0)
+  {
+    GNUNET_assert (GNUNET_SYSERR !=
+                   TALER_amount_subtract (&coin_residual,
+                                          &spent,
+                                          
&coin_details->melt_amount_with_fee));
+    res = (MHD_YES ==
+           reply_refresh_melt_insufficient_funds (connection,
+                                                 
&coin_details->coin_info.coin_pub,
+                                                 coin_value,
+                                                 tl,
+                                                 
coin_details->melt_amount_with_fee,
+                                                 coin_residual))
+        ? GNUNET_NO : GNUNET_SYSERR;
+    TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
+                                            tl);
+    return res;
+  }
+  TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
+                                          tl);
+
+  meltp->coin = coin_details->coin_info;
+  meltp->coin_sig = coin_details->melt_sig;
+  meltp->session_hash = *session_hash;
+  meltp->amount_with_fee = coin_details->melt_amount_with_fee;
+  meltp->melt_fee = coin_details->melt_fee;
+  return GNUNET_OK;
+}
+
+
+/**
+ * Execute a "/refresh/melt".  We have been given a list of valid
+ * coins and a request to melt them into the given
+ * @a refresh_session_pub.  Check that the coins all have the
+ * required value left and if so, store that they have been
+ * melted and confirm the melting operation to the client.
+ *
+ * @param connection the MHD connection to handle
+ * @param session_hash hash code of the session the coins are melted into
+ * @param num_new_denoms number of entries in @a denom_pubs, size of 
y-dimension of @a commit_coin array
+ * @param denom_pubs public keys of the coins we want to withdraw in the end
+ * @param coin_melt_detail signature and (residual) value of the respective 
coin should be melted
+ * @param commit_coin 2d array of coin commitments (what the exchange is to 
sign
+ *                    once the "/refres/reveal" of cut and choose is done),
+ *                    x-dimension must be #TALER_CNC_KAPPA
+ * @param transfer_pubs array of transfer public keys (what the exchange is
+ *                    to return via "/refresh/link" to enable linkage in the
+ *                    future) of length #TALER_CNC_KAPPA
+ * @return MHD result code
+ */
+static int
+execute_refresh_melt (struct MHD_Connection *connection,
+                     const struct GNUNET_HashCode *session_hash,
+                     unsigned int num_new_denoms,
+                     const struct TALER_DenominationPublicKey *denom_pubs,
+                     const struct TEH_DB_MeltDetails *coin_melt_detail,
+                     struct TALER_EXCHANGEDB_RefreshCommitCoin *const* 
commit_coin,
+                     const struct TALER_TransferPublicKeyP *transfer_pubs)
+{
+  struct TEH_KS_StateHandle *key_state;
+  struct TALER_EXCHANGEDB_RefreshSession refresh_session;
+  struct TALER_EXCHANGEDB_Session *session;
+  int res;
+
+  if (NULL == (session = TEH_plugin->get_session (TEH_plugin->cls)))
+  {
+    GNUNET_break (0);
+    return TEH_RESPONSE_reply_internal_db_error (connection,
+                                                TALER_EC_DB_SETUP_FAILED);
+  }
+  START_TRANSACTION (session, connection);
+  res = TEH_plugin->get_refresh_session (TEH_plugin->cls,
+                                         session,
+                                         session_hash,
+                                         &refresh_session);
+  if (GNUNET_YES == res)
+  {
+    TEH_plugin->rollback (TEH_plugin->cls,
+                          session);
+    res = reply_refresh_melt_success (connection,
+                                     session_hash,
+                                     refresh_session.noreveal_index);
+    return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
+  }
+  if (GNUNET_SYSERR == res)
+  {
+    TEH_plugin->rollback (TEH_plugin->cls,
+                          session);
+    return TEH_RESPONSE_reply_internal_db_error (connection,
+                                                
TALER_EC_REFRESH_MELT_DB_FETCH_ERROR);
+  }
+
+  /* store 'global' session data */
+  refresh_session.num_newcoins = num_new_denoms;
+  refresh_session.noreveal_index
+    = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_STRONG,
+                               TALER_CNC_KAPPA);
+  key_state = TEH_KS_acquire ();
+  if (GNUNET_OK !=
+      (res = refresh_check_melt (connection,
+                                 session,
+                                 key_state,
+                                 session_hash,
+                                 coin_melt_detail,
+                                 &refresh_session.melt)))
+  {
+    TEH_KS_release (key_state);
+    TEH_plugin->rollback (TEH_plugin->cls,
+                          session);
+    return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
+  }
+  TEH_KS_release (key_state);
+
+  if (GNUNET_OK !=
+      (res = TEH_plugin->create_refresh_session (TEH_plugin->cls,
+                                                 session,
+                                                 session_hash,
+                                                 &refresh_session)))
+  {
+    TEH_plugin->rollback (TEH_plugin->cls,
+                          session);
+    return TEH_RESPONSE_reply_internal_db_error (connection,
+                                                
TALER_EC_REFRESH_MELT_DB_STORE_SESSION_ERROR);
+  }
+
+  /* store requested new denominations */
+  if (GNUNET_OK !=
+      TEH_plugin->insert_refresh_order (TEH_plugin->cls,
+                                        session,
+                                        session_hash,
+                                        num_new_denoms,
+                                        denom_pubs))
+  {
+    TEH_plugin->rollback (TEH_plugin->cls,
+                          session);
+    return TEH_RESPONSE_reply_internal_db_error (connection,
+                                                
TALER_EC_REFRESH_MELT_DB_STORE_ORDER_ERROR);
+  }
+
+  if (GNUNET_OK !=
+      TEH_plugin->insert_refresh_commit_coins (TEH_plugin->cls,
+                                               session,
+                                               session_hash,
+                                               num_new_denoms,
+                                               
commit_coin[refresh_session.noreveal_index]))
+  {
+    TEH_plugin->rollback (TEH_plugin->cls,
+                          session);
+    return TEH_RESPONSE_reply_internal_db_error (connection,
+                                                
TALER_EC_REFRESH_MELT_DB_STORE_ORDER_ERROR);
+  }
+  if (GNUNET_OK !=
+      TEH_plugin->insert_refresh_transfer_public_key (TEH_plugin->cls,
+                                                      session,
+                                                      session_hash,
+                                                      
&transfer_pubs[refresh_session.noreveal_index]))
+  {
+    TEH_plugin->rollback (TEH_plugin->cls,
+                          session);
+    return TEH_RESPONSE_reply_internal_db_error (connection,
+                                                
TALER_EC_REFRESH_MELT_DB_STORE_TRANSFER_ERROR);
+  }
+
+  COMMIT_TRANSACTION (session, connection);
+  return reply_refresh_melt_success (connection,
+                                    session_hash,
+                                    refresh_session.noreveal_index);
+}
+
+
+/**
  * Handle a "/refresh/melt" request after the main JSON parsing has happened.
  * We now need to validate the coins being melted and the session signature
  * and then hand things of to execute the melt operation.
@@ -149,13 +592,13 @@ handle_refresh_melt_binary (struct MHD_Connection 
*connection,
                                          "error", "value mismatch",
                                         "code", (json_int_t) 
TALER_EC_REFRESH_MELT_FEES_MISSMATCH);
   }
-  return TEH_DB_execute_refresh_melt (connection,
-                                      session_hash,
-                                      num_new_denoms,
-                                      denom_pubs,
-                                      coin_melt_details,
-                                      commit_coin,
-                                      transfer_pubs);
+  return execute_refresh_melt (connection,
+                              session_hash,
+                              num_new_denoms,
+                              denom_pubs,
+                              coin_melt_details,
+                              commit_coin,
+                              transfer_pubs);
 }
 
 
@@ -597,159 +1040,4 @@ TEH_REFRESH_handler_refresh_melt (struct 
TEH_RequestHandler *rh,
 }
 
 
-/**
- * Handle a "/refresh/reveal" request.   Parses the given JSON
- * transfer private keys and if successful, passes everything to
- * #TEH_DB_execute_refresh_reveal() which will verify that the
- * revealed information is valid then returns the signed refreshed
- * coins.
- *
- * @param connection the MHD connection to handle
- * @param session_hash hash identifying the melting session
- * @param tp_json private transfer keys in JSON format
- * @return MHD result code
-  */
-static int
-handle_refresh_reveal_json (struct MHD_Connection *connection,
-                            const struct GNUNET_HashCode *session_hash,
-                            const json_t *tp_json)
-{
-  struct TALER_TransferPrivateKeyP transfer_privs[TALER_CNC_KAPPA - 1];
-  unsigned int i;
-  int res;
-
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "reveal request for session %s\n",
-              GNUNET_h2s (session_hash));
-
-  res = GNUNET_OK;
-  for (i = 0; i < TALER_CNC_KAPPA - 1; i++)
-  {
-    struct GNUNET_JSON_Specification tp_spec[] = {
-      GNUNET_JSON_spec_fixed_auto (NULL, &transfer_privs[i]),
-      GNUNET_JSON_spec_end ()
-    };
-
-    if (GNUNET_OK != res)
-      break;
-    res = TEH_PARSE_json_array (connection,
-                                tp_json,
-                                tp_spec,
-                                i, -1);
-    GNUNET_break_op (GNUNET_OK == res);
-  }
-  if (GNUNET_OK != res)
-    res = (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
-  else
-    res = TEH_DB_execute_refresh_reveal (connection,
-                                        session_hash,
-                                        transfer_privs);
-  return res;
-}
-
-
-/**
- * Handle a "/refresh/reveal" request. This time, the client reveals
- * the private transfer keys except for the cut-and-choose value
- * returned from "/refresh/melt".  This function parses the revealed
- * keys and secrets and ultimately passes everything to
- * #TEH_DB_execute_refresh_reveal() which will verify that the
- * revealed information is valid then returns the signed refreshed
- * coins.
- *
- * @param rh context of the handler
- * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
- * @return MHD result code
-  */
-int
-TEH_REFRESH_handler_refresh_reveal (struct TEH_RequestHandler *rh,
-                                    struct MHD_Connection *connection,
-                                    void **connection_cls,
-                                    const char *upload_data,
-                                    size_t *upload_data_size)
-{
-  struct GNUNET_HashCode session_hash;
-  int res;
-  json_t *root;
-  json_t *transfer_privs;
-  struct GNUNET_JSON_Specification spec[] = {
-    GNUNET_JSON_spec_fixed_auto ("session_hash", &session_hash),
-    GNUNET_JSON_spec_json ("transfer_privs", &transfer_privs),
-    GNUNET_JSON_spec_end ()
-  };
-
-  res = TEH_PARSE_post_json (connection,
-                             connection_cls,
-                             upload_data,
-                             upload_data_size,
-                             &root);
-  if (GNUNET_SYSERR == res)
-    return MHD_NO;
-  if ( (GNUNET_NO == res) || (NULL == root) )
-    return MHD_YES;
-
-  res = TEH_PARSE_json_data (connection,
-                             root,
-                             spec);
-  json_decref (root);
-  if (GNUNET_OK != res)
-  {
-    GNUNET_break_op (0);
-    return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
-  }
-  /* Determine dimensionality of the request (kappa and #old coins) */
-  /* Note we do +1 as 1 row (cut-and-choose!) is missing! */
-  if (TALER_CNC_KAPPA != json_array_size (transfer_privs) + 1)
-  {
-    GNUNET_JSON_parse_free (spec);
-    GNUNET_break_op (0);
-    return TEH_RESPONSE_reply_arg_invalid (connection,
-                                          
TALER_EC_REFRESH_REVEAL_CNC_TRANSFER_ARRAY_SIZE_INVALID,
-                                           "transfer_privs");
-  }
-  res = handle_refresh_reveal_json (connection,
-                                    &session_hash,
-                                    transfer_privs);
-  GNUNET_JSON_parse_free (spec);
-  return res;
-}
-
-
-/**
- * Handle a "/refresh/link" request.  Note that for "/refresh/link"
- * we do use a simple HTTP GET, and a HTTP POST!
- *
- * @param rh context of the handler
- * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
- * @return MHD result code
-  */
-int
-TEH_REFRESH_handler_refresh_link (struct TEH_RequestHandler *rh,
-                                  struct MHD_Connection *connection,
-                                  void **connection_cls,
-                                  const char *upload_data,
-                                  size_t *upload_data_size)
-{
-  struct TALER_CoinSpendPublicKeyP coin_pub;
-  int res;
-
-  res = TEH_PARSE_mhd_request_arg_data (connection,
-                                        "coin_pub",
-                                        &coin_pub,
-                                        sizeof (struct 
TALER_CoinSpendPublicKeyP));
-  if (GNUNET_SYSERR == res)
-    return MHD_NO;
-  if (GNUNET_OK != res)
-    return MHD_YES;
-  return TEH_DB_execute_refresh_link (connection,
-                                      &coin_pub);
-}
-
-
-/* end of taler-exchange-httpd_refresh.c */
+/* end of taler-exchange-httpd_refresh_melt.c */
diff --git a/src/exchange/taler-exchange-httpd_refresh_melt.h 
b/src/exchange/taler-exchange-httpd_refresh_melt.h
new file mode 100644
index 0000000..a938abf
--- /dev/null
+++ b/src/exchange/taler-exchange-httpd_refresh_melt.h
@@ -0,0 +1,52 @@
+/*
+  This file is part of TALER
+  Copyright (C) 2014-2017 GNUnet e.V.
+
+  TALER is free software; you can redistribute it and/or modify it under the
+  terms of the GNU Affero General Public License as published by the Free 
Software
+  Foundation; either version 3, or (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+  A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more 
details.
+
+  You should have received a copy of the GNU Affero General Public License 
along with
+  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-exchange-httpd_refresh_melt.h
+ * @brief Handle /refresh/melt requests
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#ifndef TALER_EXCHANGE_HTTPD_REFRESH_MELT_H
+#define TALER_EXCHANGE_HTTPD_REFRESH_MELT_H
+
+#include <gnunet/gnunet_util_lib.h>
+#include <microhttpd.h>
+#include "taler-exchange-httpd.h"
+
+
+/**
+ * Handle a "/refresh/melt" request.  Parses the request into the JSON
+ * components and then hands things of to #handle_refresh_melt_json()
+ * to validate the melted coins, the signature and execute the melt
+ * using TEH_DB_execute_refresh_melt().
+ *
+ * @param rh context of the handler
+ * @param connection the MHD connection to handle
+ * @param[in,out] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
+ * @return MHD result code
+ */
+int
+TEH_REFRESH_handler_refresh_melt (struct TEH_RequestHandler *rh,
+                                  struct MHD_Connection *connection,
+                                  void **connection_cls,
+                                  const char *upload_data,
+                                  size_t *upload_data_size);
+
+
+#endif
diff --git a/src/exchange/taler-exchange-httpd_db.c 
b/src/exchange/taler-exchange-httpd_refresh_reveal.c
similarity index 55%
copy from src/exchange/taler-exchange-httpd_db.c
copy to src/exchange/taler-exchange-httpd_refresh_reveal.c
index 9871b7f..05422a8 100644
--- a/src/exchange/taler-exchange-httpd_db.c
+++ b/src/exchange/taler-exchange-httpd_refresh_reveal.c
@@ -1,31 +1,36 @@
 /*
   This file is part of TALER
-  Copyright (C) 2014-2017 GNUnet e.V.
+  Copyright (C) 2014-2017 Inria & GNUnet e.V.
 
   TALER is free software; you can redistribute it and/or modify it under the
-  terms of the GNU General Public License as published by the Free Software
+  terms of the GNU Affero General Public License as published by the Free 
Software
   Foundation; either version 3, or (at your option) any later version.
 
   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
-  A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
+  A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more 
details.
 
-  You should have received a copy of the GNU General Public License along with
+  You should have received a copy of the GNU Affero General Public License 
along with
   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
 */
 /**
- * @file taler-exchange-httpd_db.c
- * @brief High-level (transactional-layer) database operations for the 
exchange.
+ * @file taler-exchange-httpd_refresh_reveal.c
+ * @brief Handle /refresh/reveal requests
+ * @author Florian Dold
+ * @author Benedikt Mueller
  * @author Christian Grothoff
  */
 #include "platform.h"
-#include <pthread.h>
+#include <gnunet/gnunet_util_lib.h>
 #include <jansson.h>
-#include <gnunet/gnunet_json_lib.h>
-#include "taler_json_lib.h"
+#include <microhttpd.h>
+#include "taler-exchange-httpd_parsing.h"
+#include "taler-exchange-httpd_mhd.h"
+#include "taler-exchange-httpd_refresh_reveal.h"
 #include "taler-exchange-httpd_responses.h"
 #include "taler-exchange-httpd_keystate.h"
 
+
 /**
  * How often should we retry a transaction before giving up
  * (for transactions resulting in serialization/dead locks only).
@@ -112,409 +117,109 @@ transaction_start_label: /* we will use goto for 
retries */   \
   } while (0)
 
 
-/**
- * Run a database transaction for @a connection.
- * Starts a transaction and calls @a cb.  Upon success,
- * attempts to commit the transaction.  Upon soft failures,
- * retries @a cb a few times.  Upon hard or persistent soft
- * errors, generates an error message for @a connection.
- * 
- * @param connection MHD connection to run @a cb for
- * @param[out] set to MHD response code, if transaction failed
- * @param cb callback implementing transaction logic
- * @param cb_cls closure for @a cb, must be read-only!
- * @return #GNUNET_OK on success, #GNUNET_SYSERR on failure
- */
-int
-TEH_DB_run_transaction (struct MHD_Connection *connection,
-                       int *mhd_ret,
-                       TEH_DB_TransactionCallback cb,
-                       void *cb_cls)
-{
-  struct TALER_EXCHANGEDB_Session *session;
-
-  *mhd_ret = -1; /* invalid value */
-  if (NULL == (session = TEH_plugin->get_session (TEH_plugin->cls)))
-  {
-    GNUNET_break (0);
-    *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
-                                                    TALER_EC_DB_SETUP_FAILED);
-    return GNUNET_SYSERR;
-  }
-  for (unsigned int retries = 0;retries < MAX_TRANSACTION_COMMIT_RETRIES; 
retries++)
-  {
-    enum GNUNET_DB_QueryStatus qs;
 
-    if (GNUNET_OK !=                                            
-       TEH_plugin->start (TEH_plugin->cls,                     
-                          session))                            
-    {                                      
-      GNUNET_break (0);                                         
-      *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection, 
-                                                      
TALER_EC_DB_START_FAILED);
-      return GNUNET_SYSERR;
-    }
-    qs = cb (cb_cls,
-            connection,
-            session,
-            mhd_ret);
-    if (0 > qs)
-      TEH_plugin->rollback (TEH_plugin->cls,
-                           session);      
-    if (GNUNET_DB_STATUS_HARD_ERROR == qs)
-      return GNUNET_SYSERR;
-    if (0 <= qs)
-      qs = TEH_plugin->commit (TEH_plugin->cls,
-                              session);                              
-    if (GNUNET_DB_STATUS_HARD_ERROR == qs)
-    {
-      *mhd_ret = TEH_RESPONSE_reply_commit_error (connection,
-                                                 
TALER_EC_DB_COMMIT_FAILED_HARD);
-      return GNUNET_SYSERR;
-    }
-    /* make sure callback did not violate invariants! */
-    GNUNET_assert (-1 == *mhd_ret);
-    if (0 <= qs)
-      return GNUNET_OK;
-  }
-  TALER_LOG_WARNING ("Transaction commit failed %u times\n",
-                    MAX_TRANSACTION_COMMIT_RETRIES);
-  *mhd_ret = TEH_RESPONSE_reply_commit_error (connection,
-                                             
TALER_EC_DB_COMMIT_FAILED_ON_RETRY);
-  return GNUNET_SYSERR;
-}
 
 
 /**
- * Calculate the total value of all transactions performed.
- * Stores @a off plus the cost of all transactions in @a tl
- * in @a ret.
+ * Send a response for "/refresh/reveal".
  *
- * @param tl transaction list to process
- * @param off offset to use as the starting value
- * @param[out] ret where the resulting total is to be stored
- * @return #GNUNET_OK on success, #GNUNET_SYSERR on errors
+ * @param connection the connection to send the response to
+ * @param num_newcoins number of new coins for which we reveal data
+ * @param sigs array of @a num_newcoins signatures revealed
+ * @return a MHD result code
  */
-int
-TEH_DB_calculate_transaction_list_totals (struct 
TALER_EXCHANGEDB_TransactionList *tl,
-                                         const struct TALER_Amount *off,
-                                         struct TALER_Amount *ret)
+static int
+reply_refresh_reveal_success (struct MHD_Connection *connection,
+                             unsigned int num_newcoins,
+                             const struct TALER_DenominationSignature *sigs)
 {
-  struct TALER_Amount spent = *off;
-  struct TALER_EXCHANGEDB_TransactionList *pos;
-  struct TALER_Amount refunded;
-
-  TALER_amount_get_zero (spent.currency,
-                         &refunded);
-  for (pos = tl; NULL != pos; pos = pos->next)
-  {
-    switch (pos->type)
-    {
-    case TALER_EXCHANGEDB_TT_DEPOSIT:
-      /* spent += pos->amount_with_fee */
-      if (GNUNET_OK !=
-          TALER_amount_add (&spent,
-                            &spent,
-                            &pos->details.deposit->amount_with_fee))
-      {
-        GNUNET_break (0);
-        return GNUNET_SYSERR;
-      }
-      break;
-    case TALER_EXCHANGEDB_TT_REFRESH_MELT:
-      /* spent += pos->amount_with_fee */
-      if (GNUNET_OK !=
-          TALER_amount_add (&spent,
-                            &spent,
-                            &pos->details.melt->amount_with_fee))
-      {
-        GNUNET_break (0);
-        return GNUNET_SYSERR;
-      }
-      break;
-    case TALER_EXCHANGEDB_TT_REFUND:
-      /* refunded += pos->refund_amount - pos->refund_fee */
-      if (GNUNET_OK !=
-          TALER_amount_add (&refunded,
-                            &refunded,
-                            &pos->details.refund->refund_amount))
-      {
-        GNUNET_break (0);
-        return GNUNET_SYSERR;
-      }
-      if (GNUNET_OK !=
-          TALER_amount_subtract (&refunded,
-                                 &refunded,
-                                 &pos->details.refund->refund_fee))
-      {
-        GNUNET_break (0);
-        return GNUNET_SYSERR;
-      }
-      break;
-    case TALER_EXCHANGEDB_TT_PAYBACK:
-      /* spent += pos->value */
-      if (GNUNET_OK !=
-          TALER_amount_add (&spent,
-                            &spent,
-                            &pos->details.payback->value))
-      {
-        GNUNET_break (0);
-        return GNUNET_SYSERR;
-      }
-      break;
-    }
-  }
-  /* spent = spent - refunded */
-  if (GNUNET_SYSERR ==
-      TALER_amount_subtract (&spent,
-                             &spent,
-                             &refunded))
-  {
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
-  }
+  int newcoin_index;
+  json_t *root;
+  json_t *obj;
+  json_t *list;
+  int ret;
 
-  *ret = spent;
-  return GNUNET_OK;
+  list = json_array ();
+  for (newcoin_index = 0; newcoin_index < num_newcoins; newcoin_index++)
+  {
+    obj = json_object ();
+    json_object_set_new (obj,
+                        "ev_sig",
+                        GNUNET_JSON_from_rsa_signature 
(sigs[newcoin_index].rsa_signature));
+    GNUNET_assert (0 ==
+                   json_array_append_new (list,
+                                          obj));
+  }
+  root = json_object ();
+  json_object_set_new (root,
+                       "ev_sigs",
+                       list);
+  ret = TEH_RESPONSE_reply_json (connection,
+                                 root,
+                                 MHD_HTTP_OK);
+  json_decref (root);
+  return ret;
 }
 
 
 /**
- * Parse coin melt requests from a JSON object and write them to
- * the database.
+ * Send a response for a failed "/refresh/reveal", where the
+ * revealed value(s) do not match the original commitment.
  *
- * @param connection the connection to send errors to
- * @param session the database connection
- * @param key_state the exchange's key state
- * @param session_hash hash identifying the refresh session
- * @param coin_details details about the coin being melted
- * @param[out] meltp on success, set to melt details
- * @return #GNUNET_OK on success,
- *         #GNUNET_NO if an error message was generated,
- *         #GNUNET_SYSERR on internal errors (no response generated)
+ * @param connection the connection to send the response to
+ * @param session info about session
+ * @param commit_coins array of @a num_newcoins committed envelopes at offset 
@a gamma
+ * @param denom_pubs array of @a num_newcoins denomination keys for the new 
coins
+ * @param gamma_tp transfer public key at offset @a gamma
+ * @return a MHD result code
  */
 static int
-refresh_check_melt (struct MHD_Connection *connection,
-                    struct TALER_EXCHANGEDB_Session *session,
-                    const struct TEH_KS_StateHandle *key_state,
-                    const struct GNUNET_HashCode *session_hash,
-                    const struct TEH_DB_MeltDetails *coin_details,
-                    struct TALER_EXCHANGEDB_RefreshMelt *meltp)
+reply_refresh_reveal_missmatch (struct MHD_Connection *connection,
+                               const struct TALER_EXCHANGEDB_RefreshSession 
*session,
+                               const struct TALER_EXCHANGEDB_RefreshCommitCoin 
*commit_coins,
+                               const struct TALER_DenominationPublicKey 
*denom_pubs,
+                               const struct TALER_TransferPublicKeyP *gamma_tp)
 {
-  struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dk;
-  struct TALER_EXCHANGEDB_DenominationKeyInformationP *dki;
-  struct TALER_EXCHANGEDB_TransactionList *tl;
-  struct TALER_Amount coin_value;
-  struct TALER_Amount coin_residual;
-  struct TALER_Amount spent;
-  int res;
-  enum GNUNET_DB_QueryStatus qs;
-
-  dk = TEH_KS_denomination_key_lookup (key_state,
-                                       &coin_details->coin_info.denom_pub,
-                                       TEH_KS_DKU_DEPOSIT);
-  if (NULL == dk)
-    return (MHD_YES ==
-            TEH_RESPONSE_reply_internal_error (connection,
-                                              
TALER_EC_REFRESH_MELT_DB_DENOMINATION_KEY_NOT_FOUND,
-                                              "denomination key no longer 
available while executing transaction"))
-        ? GNUNET_NO : GNUNET_SYSERR;
-  dki = &dk->issue;
-  TALER_amount_ntoh (&coin_value,
-                     &dki->properties.value);
-  /* fee for THIS transaction; the melt amount includes the fee! */
-  spent = coin_details->melt_amount_with_fee;
-  /* add historic transaction costs of this coin */
-  qs = TEH_plugin->get_coin_transactions (TEH_plugin->cls,
-                                          session,
-                                          &coin_details->coin_info.coin_pub,
-                                         &tl);
-  (void) qs; /* FIXME #5010 */
-  if (GNUNET_OK !=
-      TEH_DB_calculate_transaction_list_totals (tl,
-                                               &spent,
-                                               &spent))
-  {
-    GNUNET_break (0);
-    TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
-                                            tl);
-    return (MHD_YES ==
-            TEH_RESPONSE_reply_internal_db_error (connection,
-                                                 
TALER_EC_REFRESH_MELT_COIN_HISTORY_COMPUTATION_FAILED))
-      ? GNUNET_NO : GNUNET_SYSERR;
-  }
-  /* Refuse to refresh when the coin's value is insufficient
-     for the cost of all transactions. */
-  if (TALER_amount_cmp (&coin_value,
-                        &spent) < 0)
-  {
-    GNUNET_assert (GNUNET_SYSERR !=
-                   TALER_amount_subtract (&coin_residual,
-                                          &spent,
-                                          
&coin_details->melt_amount_with_fee));
-    res = (MHD_YES ==
-           TEH_RESPONSE_reply_refresh_melt_insufficient_funds (connection,
-                                                               
&coin_details->coin_info.coin_pub,
-                                                               coin_value,
-                                                               tl,
-                                                               
coin_details->melt_amount_with_fee,
-                                                               coin_residual))
-        ? GNUNET_NO : GNUNET_SYSERR;
-    TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
-                                            tl);
-    return res;
-  }
-  TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
-                                          tl);
+  json_t *info_new;
+  json_t *info_commit_k;
+  unsigned int i;
 
-  meltp->coin = coin_details->coin_info;
-  meltp->coin_sig = coin_details->melt_sig;
-  meltp->session_hash = *session_hash;
-  meltp->amount_with_fee = coin_details->melt_amount_with_fee;
-  meltp->melt_fee = coin_details->melt_fee;
-  return GNUNET_OK;
+  info_new = json_array ();
+  info_commit_k = json_array ();
+  for (i=0;i<session->num_newcoins;i++)
+  {
+    const struct TALER_EXCHANGEDB_RefreshCommitCoin *cc;
+    json_t *cc_json;
+
+    GNUNET_assert (0 ==
+                   json_array_append_new (info_new,
+                                          GNUNET_JSON_from_rsa_public_key 
(denom_pubs[i].rsa_public_key)));
+
+    cc = &commit_coins[i];
+    cc_json = json_pack ("{s:o}",
+                         "coin_ev",
+                         GNUNET_JSON_from_data (cc->coin_ev,
+                                                cc->coin_ev_size));
+    GNUNET_assert (0 ==
+                   json_array_append_new (info_commit_k,
+                                          cc_json));
+  }
+  return TEH_RESPONSE_reply_json_pack (connection,
+                                       MHD_HTTP_CONFLICT,
+                                       "{s:s, s:I, s:o, s:o, s:o, s:o, s:o, 
s:o, s:o, s:i}",
+                                       "error", "commitment violation",
+                                      "code", (json_int_t) 
TALER_EC_REFRESH_REVEAL_COMMITMENT_VIOLATION,
+                                       "coin_sig", GNUNET_JSON_from_data_auto 
(&session->melt.coin_sig),
+                                       "coin_pub", GNUNET_JSON_from_data_auto 
(&session->melt.coin.coin_pub),
+                                       "melt_amount_with_fee", 
TALER_JSON_from_amount (&session->melt.amount_with_fee),
+                                       "melt_fee", TALER_JSON_from_amount 
(&session->melt.melt_fee),
+                                       "newcoin_infos", info_new,
+                                       "commit_infos", info_commit_k,
+                                       "gamma_tp", GNUNET_JSON_from_data_auto 
(gamma_tp),
+                                       "gamma", (int) session->noreveal_index);
 }
 
 
-/**
- * Execute a "/refresh/melt".  We have been given a list of valid
- * coins and a request to melt them into the given
- * @a refresh_session_pub.  Check that the coins all have the
- * required value left and if so, store that they have been
- * melted and confirm the melting operation to the client.
- *
- * @param connection the MHD connection to handle
- * @param session_hash hash code of the session the coins are melted into
- * @param num_new_denoms number of entries in @a denom_pubs, size of 
y-dimension of @a commit_coin array
- * @param denom_pubs public keys of the coins we want to withdraw in the end
- * @param coin_melt_detail signature and (residual) value of the respective 
coin should be melted
- * @param commit_coin 2d array of coin commitments (what the exchange is to 
sign
- *                    once the "/refres/reveal" of cut and choose is done),
- *                    x-dimension must be #TALER_CNC_KAPPA
- * @param transfer_pubs array of transfer public keys (what the exchange is
- *                    to return via "/refresh/link" to enable linkage in the
- *                    future) of length #TALER_CNC_KAPPA
- * @return MHD result code
- */
-int
-TEH_DB_execute_refresh_melt (struct MHD_Connection *connection,
-                             const struct GNUNET_HashCode *session_hash,
-                             unsigned int num_new_denoms,
-                             const struct TALER_DenominationPublicKey 
*denom_pubs,
-                             const struct TEH_DB_MeltDetails *coin_melt_detail,
-                             struct TALER_EXCHANGEDB_RefreshCommitCoin *const* 
commit_coin,
-                             const struct TALER_TransferPublicKeyP 
*transfer_pubs)
-{
-  struct TEH_KS_StateHandle *key_state;
-  struct TALER_EXCHANGEDB_RefreshSession refresh_session;
-  struct TALER_EXCHANGEDB_Session *session;
-  int res;
-
-  if (NULL == (session = TEH_plugin->get_session (TEH_plugin->cls)))
-  {
-    GNUNET_break (0);
-    return TEH_RESPONSE_reply_internal_db_error (connection,
-                                                TALER_EC_DB_SETUP_FAILED);
-  }
-  START_TRANSACTION (session, connection);
-  res = TEH_plugin->get_refresh_session (TEH_plugin->cls,
-                                         session,
-                                         session_hash,
-                                         &refresh_session);
-  if (GNUNET_YES == res)
-  {
-    TEH_plugin->rollback (TEH_plugin->cls,
-                          session);
-    res = TEH_RESPONSE_reply_refresh_melt_success (connection,
-                                                   session_hash,
-                                                   
refresh_session.noreveal_index);
-    return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
-  }
-  if (GNUNET_SYSERR == res)
-  {
-    TEH_plugin->rollback (TEH_plugin->cls,
-                          session);
-    return TEH_RESPONSE_reply_internal_db_error (connection,
-                                                
TALER_EC_REFRESH_MELT_DB_FETCH_ERROR);
-  }
-
-  /* store 'global' session data */
-  refresh_session.num_newcoins = num_new_denoms;
-  refresh_session.noreveal_index
-    = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_STRONG,
-                               TALER_CNC_KAPPA);
-  key_state = TEH_KS_acquire ();
-  if (GNUNET_OK !=
-      (res = refresh_check_melt (connection,
-                                 session,
-                                 key_state,
-                                 session_hash,
-                                 coin_melt_detail,
-                                 &refresh_session.melt)))
-  {
-    TEH_KS_release (key_state);
-    TEH_plugin->rollback (TEH_plugin->cls,
-                          session);
-    return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
-  }
-  TEH_KS_release (key_state);
-
-  if (GNUNET_OK !=
-      (res = TEH_plugin->create_refresh_session (TEH_plugin->cls,
-                                                 session,
-                                                 session_hash,
-                                                 &refresh_session)))
-  {
-    TEH_plugin->rollback (TEH_plugin->cls,
-                          session);
-    return TEH_RESPONSE_reply_internal_db_error (connection,
-                                                
TALER_EC_REFRESH_MELT_DB_STORE_SESSION_ERROR);
-  }
-
-  /* store requested new denominations */
-  if (GNUNET_OK !=
-      TEH_plugin->insert_refresh_order (TEH_plugin->cls,
-                                        session,
-                                        session_hash,
-                                        num_new_denoms,
-                                        denom_pubs))
-  {
-    TEH_plugin->rollback (TEH_plugin->cls,
-                          session);
-    return TEH_RESPONSE_reply_internal_db_error (connection,
-                                                
TALER_EC_REFRESH_MELT_DB_STORE_ORDER_ERROR);
-  }
-
-  if (GNUNET_OK !=
-      TEH_plugin->insert_refresh_commit_coins (TEH_plugin->cls,
-                                               session,
-                                               session_hash,
-                                               num_new_denoms,
-                                               
commit_coin[refresh_session.noreveal_index]))
-  {
-    TEH_plugin->rollback (TEH_plugin->cls,
-                          session);
-    return TEH_RESPONSE_reply_internal_db_error (connection,
-                                                
TALER_EC_REFRESH_MELT_DB_STORE_ORDER_ERROR);
-  }
-  if (GNUNET_OK !=
-      TEH_plugin->insert_refresh_transfer_public_key (TEH_plugin->cls,
-                                                      session,
-                                                      session_hash,
-                                                      
&transfer_pubs[refresh_session.noreveal_index]))
-  {
-    TEH_plugin->rollback (TEH_plugin->cls,
-                          session);
-    return TEH_RESPONSE_reply_internal_db_error (connection,
-                                                
TALER_EC_REFRESH_MELT_DB_STORE_TRANSFER_ERROR);
-  }
-
-  COMMIT_TRANSACTION (session, connection);
-  return TEH_RESPONSE_reply_refresh_melt_success (connection,
-                                                  session_hash,
-                                                  
refresh_session.noreveal_index);
-}
-
 
 /**
  * Check if the given @a transfer_privs correspond to an honest
@@ -719,9 +424,9 @@ execute_refresh_reveal_transaction (struct MHD_Connection 
*connection,
     }
   }
   COMMIT_TRANSACTION (session, connection);
-  ret = TEH_RESPONSE_reply_refresh_reveal_success (connection,
-                                                   
refresh_session->num_newcoins,
-                                                   ev_sigs);
+  ret = reply_refresh_reveal_success (connection,
+                                     refresh_session->num_newcoins,
+                                     ev_sigs);
  cleanup:
   TEH_KS_release (key_state);
   return ret;
@@ -741,10 +446,10 @@ execute_refresh_reveal_transaction (struct MHD_Connection 
*connection,
  *                      length must be #TALER_CNC_KAPPA - 1
  * @return MHD result code
  */
-int
-TEH_DB_execute_refresh_reveal (struct MHD_Connection *connection,
-                               const struct GNUNET_HashCode *session_hash,
-                               struct TALER_TransferPrivateKeyP 
*transfer_privs)
+static int
+execute_refresh_reveal (struct MHD_Connection *connection,
+                       const struct GNUNET_HashCode *session_hash,
+                       struct TALER_TransferPrivateKeyP *transfer_privs)
 {
   int res;
   struct TALER_EXCHANGEDB_Session *session;
@@ -955,11 +660,11 @@ TEH_DB_execute_refresh_reveal (struct MHD_Connection 
*connection,
                    sizeof (struct GNUNET_HashCode)))
   {
     GNUNET_break_op (0);
-    ret = TEH_RESPONSE_reply_refresh_reveal_missmatch (connection,
-                                                       &refresh_session,
-                                                       commit_coins,
-                                                       denom_pubs,
-                                                       &gamma_tp);
+    ret = reply_refresh_reveal_missmatch (connection,
+                                         &refresh_session,
+                                         commit_coins,
+                                         denom_pubs,
+                                         &gamma_tp);
     for (j=0;j<refresh_session.num_newcoins;j++)
     {
       GNUNET_free (commit_coins[j].coin_ev);
@@ -1005,144 +710,124 @@ TEH_DB_execute_refresh_reveal (struct MHD_Connection 
*connection,
 
 
 /**
- * Closure for #handle_transfer_data().
- */
-struct HTD_Context
-{
-
-  /**
-   * Session link data we collect.
-   */
-  struct TEH_RESPONSE_LinkSessionInfo *sessions;
-
-  /**
-   * Database session. Nothing to do with @a sessions.
-   */
-  struct TALER_EXCHANGEDB_Session *session;
-
-  /**
-   * MHD connection, for queueing replies.
-   */
-  struct MHD_Connection *connection;
-
-  /**
-   * Number of sessions the coin was melted into.
-   */
-  unsigned int num_sessions;
-
-  /**
-   * How are we expected to proceed. #GNUNET_SYSERR if we
-   * failed to return an error (should return #MHD_NO).
-   * #GNUNET_NO if we succeeded in queueing an MHD error
-   * (should return #MHD_YES from #TEH_execute_refresh_link),
-   * #GNUNET_OK if we should call #TEH_RESPONSE_reply_refresh_link_success().
-   */
-  int status;
-};
-
-
-/**
- * Function called with the session hashes and transfer secret
- * information for a given coin.  Gets the linkage data and
- * builds the reply for the client.
- *
+ * Handle a "/refresh/reveal" request.   Parses the given JSON
+ * transfer private keys and if successful, passes everything to
+ * #TEH_DB_execute_refresh_reveal() which will verify that the
+ * revealed information is valid then returns the signed refreshed
+ * coins.
  *
- * @param cls closure, a `struct HTD_Context`
- * @param session_hash a session the coin was melted in
- * @param transfer_pub public transfer key for the session
- */
-static void
-handle_transfer_data (void *cls,
-                      const struct GNUNET_HashCode *session_hash,
-                      const struct TALER_TransferPublicKeyP *transfer_pub)
+ * @param connection the MHD connection to handle
+ * @param session_hash hash identifying the melting session
+ * @param tp_json private transfer keys in JSON format
+ * @return MHD result code
+  */
+static int
+handle_refresh_reveal_json (struct MHD_Connection *connection,
+                            const struct GNUNET_HashCode *session_hash,
+                            const json_t *tp_json)
 {
-  struct HTD_Context *ctx = cls;
-  struct TALER_EXCHANGEDB_LinkDataList *ldl;
-  struct TEH_RESPONSE_LinkSessionInfo *lsi;
+  struct TALER_TransferPrivateKeyP transfer_privs[TALER_CNC_KAPPA - 1];
+  unsigned int i;
+  int res;
 
-  if (GNUNET_OK != ctx->status)
-    return;
-  ldl = TEH_plugin->get_link_data_list (TEH_plugin->cls,
-                                        ctx->session,
-                                        session_hash);
-  if (NULL == ldl)
+  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
+              "reveal request for session %s\n",
+              GNUNET_h2s (session_hash));
+
+  res = GNUNET_OK;
+  for (i = 0; i < TALER_CNC_KAPPA - 1; i++)
   {
-    ctx->status = GNUNET_NO;
-    if (MHD_NO ==
-        TEH_RESPONSE_reply_json_pack (ctx->connection,
-                                      MHD_HTTP_NOT_FOUND,
-                                      "{s:s}",
-                                      "error",
-                                      "link data not found (link)"))
-      ctx->status = GNUNET_SYSERR;
-    return;
-  }
-  GNUNET_array_grow (ctx->sessions,
-                     ctx->num_sessions,
-                     ctx->num_sessions + 1);
-  lsi = &ctx->sessions[ctx->num_sessions - 1];
-  lsi->transfer_pub = *transfer_pub;
-  lsi->ldl = ldl;
+    struct GNUNET_JSON_Specification tp_spec[] = {
+      GNUNET_JSON_spec_fixed_auto (NULL, &transfer_privs[i]),
+      GNUNET_JSON_spec_end ()
+    };
+
+    if (GNUNET_OK != res)
+      break;
+    res = TEH_PARSE_json_array (connection,
+                                tp_json,
+                                tp_spec,
+                                i, -1);
+    GNUNET_break_op (GNUNET_OK == res);
+  }
+  if (GNUNET_OK != res)
+    res = (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
+  else
+    res = execute_refresh_reveal (connection,
+                                 session_hash,
+                                 transfer_privs);
+  return res;
 }
 
 
 /**
- * Execute a "/refresh/link".  Returns the linkage information that
- * will allow the owner of a coin to follow the refresh trail to
- * the refreshed coin.
+ * Handle a "/refresh/reveal" request. This time, the client reveals
+ * the private transfer keys except for the cut-and-choose value
+ * returned from "/refresh/melt".  This function parses the revealed
+ * keys and secrets and ultimately passes everything to
+ * #TEH_DB_execute_refresh_reveal() which will verify that the
+ * revealed information is valid then returns the signed refreshed
+ * coins.
  *
+ * @param rh context of the handler
  * @param connection the MHD connection to handle
- * @param coin_pub public key of the coin to link
+ * @param[in,out] connection_cls the connection's closure (can be updated)
+ * @param upload_data upload data
+ * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
  * @return MHD result code
- */
+  */
 int
-TEH_DB_execute_refresh_link (struct MHD_Connection *connection,
-                             const struct TALER_CoinSpendPublicKeyP *coin_pub)
+TEH_REFRESH_handler_refresh_reveal (struct TEH_RequestHandler *rh,
+                                    struct MHD_Connection *connection,
+                                    void **connection_cls,
+                                    const char *upload_data,
+                                    size_t *upload_data_size)
 {
-  struct HTD_Context ctx;
+  struct GNUNET_HashCode session_hash;
   int res;
-  unsigned int i;
+  json_t *root;
+  json_t *transfer_privs;
+  struct GNUNET_JSON_Specification spec[] = {
+    GNUNET_JSON_spec_fixed_auto ("session_hash", &session_hash),
+    GNUNET_JSON_spec_json ("transfer_privs", &transfer_privs),
+    GNUNET_JSON_spec_end ()
+  };
+
+  res = TEH_PARSE_post_json (connection,
+                             connection_cls,
+                             upload_data,
+                             upload_data_size,
+                             &root);
+  if (GNUNET_SYSERR == res)
+    return MHD_NO;
+  if ( (GNUNET_NO == res) || (NULL == root) )
+    return MHD_YES;
 
-  if (NULL == (ctx.session = TEH_plugin->get_session (TEH_plugin->cls)))
+  res = TEH_PARSE_json_data (connection,
+                             root,
+                             spec);
+  json_decref (root);
+  if (GNUNET_OK != res)
   {
-    GNUNET_break (0);
-    return TEH_RESPONSE_reply_internal_db_error (connection,
-                                                TALER_EC_DB_SETUP_FAILED);
-  }
-  ctx.connection = connection;
-  ctx.num_sessions = 0;
-  ctx.sessions = NULL;
-  ctx.status = GNUNET_OK;
-  res = TEH_plugin->get_transfer (TEH_plugin->cls,
-                                  ctx.session,
-                                  coin_pub,
-                                  &handle_transfer_data,
-                                  &ctx);
-  if (GNUNET_SYSERR == ctx.status)
-  {
-    res = MHD_NO;
-    goto cleanup;
+    GNUNET_break_op (0);
+    return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
   }
-  if (GNUNET_NO == ctx.status)
+  /* Determine dimensionality of the request (kappa and #old coins) */
+  /* Note we do +1 as 1 row (cut-and-choose!) is missing! */
+  if (TALER_CNC_KAPPA != json_array_size (transfer_privs) + 1)
   {
-    res = MHD_YES;
-    goto cleanup;
+    GNUNET_JSON_parse_free (spec);
+    GNUNET_break_op (0);
+    return TEH_RESPONSE_reply_arg_invalid (connection,
+                                          
TALER_EC_REFRESH_REVEAL_CNC_TRANSFER_ARRAY_SIZE_INVALID,
+                                           "transfer_privs");
   }
-  GNUNET_assert (GNUNET_OK == ctx.status);
-  if (0 == ctx.num_sessions)
-    return TEH_RESPONSE_reply_arg_unknown (connection,
-                                          TALER_EC_REFRESH_LINK_COIN_UNKNOWN,
-                                           "coin_pub");
-  res = TEH_RESPONSE_reply_refresh_link_success (connection,
-                                                 ctx.num_sessions,
-                                                 ctx.sessions);
- cleanup:
-  for (i=0;i<ctx.num_sessions;i++)
-    TEH_plugin->free_link_data_list (TEH_plugin->cls,
-                                     ctx.sessions[i].ldl);
-  GNUNET_free_non_null (ctx.sessions);
+  res = handle_refresh_reveal_json (connection,
+                                    &session_hash,
+                                    transfer_privs);
+  GNUNET_JSON_parse_free (spec);
   return res;
 }
 
 
-/* end of taler-exchange-httpd_db.c */
+/* end of taler-exchange-httpd_refresh_reveal.c */
diff --git a/src/exchange/taler-exchange-httpd_refresh.h 
b/src/exchange/taler-exchange-httpd_refresh_reveal.h
similarity index 52%
rename from src/exchange/taler-exchange-httpd_refresh.h
rename to src/exchange/taler-exchange-httpd_refresh_reveal.h
index 61b3b4f..cc5ac3f 100644
--- a/src/exchange/taler-exchange-httpd_refresh.h
+++ b/src/exchange/taler-exchange-httpd_refresh_reveal.h
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  Copyright (C) 2014, 2015 GNUnet e.V.
+  Copyright (C) 2014-2017 GNUnet e.V.
 
   TALER is free software; you can redistribute it and/or modify it under the
   terms of the GNU Affero General Public License as published by the Free 
Software
@@ -14,14 +14,14 @@
   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
 */
 /**
- * @file taler-exchange-httpd_refresh.h
- * @brief Handle /refresh/ requests
+ * @file taler-exchange-httpd_refresh_reveal.h
+ * @brief Handle /refresh/reveal requests
  * @author Florian Dold
  * @author Benedikt Mueller
  * @author Christian Grothoff
  */
-#ifndef TALER_EXCHANGE_HTTPD_REFRESH_H
-#define TALER_EXCHANGE_HTTPD_REFRESH_H
+#ifndef TALER_EXCHANGE_HTTPD_REFRESH_REVEAL_H
+#define TALER_EXCHANGE_HTTPD_REFRESH_REVEAL_H
 
 #include <gnunet/gnunet_util_lib.h>
 #include <microhttpd.h>
@@ -29,27 +29,6 @@
 
 
 /**
- * Handle a "/refresh/melt" request.  Parses the request into the JSON
- * components and then hands things of to #handle_refresh_melt_json()
- * to validate the melted coins, the signature and execute the melt
- * using TEH_DB_execute_refresh_melt().
- *
- * @param rh context of the handler
- * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
- * @return MHD result code
- */
-int
-TEH_REFRESH_handler_refresh_melt (struct TEH_RequestHandler *rh,
-                                  struct MHD_Connection *connection,
-                                  void **connection_cls,
-                                  const char *upload_data,
-                                  size_t *upload_data_size);
-
-
-/**
  * Handle a "/refresh/reveal" request.  This time, the client reveals
  * the private transfer keys except for the cut-and-choose value
  * returned from "/refresh/commit".  This function parses the revealed
@@ -72,23 +51,4 @@ TEH_REFRESH_handler_refresh_reveal (struct 
TEH_RequestHandler *rh,
                                     const char *upload_data,
                                     size_t *upload_data_size);
 
-
-/**
- * Handle a "/refresh/link" request
- *
- * @param rh context of the handler
- * @param connection the MHD connection to handle
- * @param[in,out] connection_cls the connection's closure (can be updated)
- * @param upload_data upload data
- * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
- * @return MHD result code
-  */
-int
-TEH_REFRESH_handler_refresh_link (struct TEH_RequestHandler *rh,
-                                  struct MHD_Connection *connection,
-                                  void **connection_cls,
-                                  const char *upload_data,
-                                  size_t *upload_data_size);
-
-
 #endif
diff --git a/src/exchange/taler-exchange-httpd_responses.c 
b/src/exchange/taler-exchange-httpd_responses.c
index 286572f..85575b6 100644
--- a/src/exchange/taler-exchange-httpd_responses.c
+++ b/src/exchange/taler-exchange-httpd_responses.c
@@ -879,251 +879,6 @@ TEH_RESPONSE_compile_reserve_history (const struct 
TALER_EXCHANGEDB_ReserveHisto
 
 
 /**
- * Send a response for a failed "/refresh/melt" request.  The
- * transaction history of the given coin demonstrates that the
- * @a residual value of the coin is below the @a requested
- * contribution of the coin for the melt.  Thus, the exchange
- * refuses the melt operation.
- *
- * @param connection the connection to send the response to
- * @param coin_pub public key of the coin
- * @param coin_value original value of the coin
- * @param tl transaction history for the coin
- * @param requested how much this coin was supposed to contribute, including 
fee
- * @param residual remaining value of the coin (after subtracting @a tl)
- * @return a MHD result code
- */
-int
-TEH_RESPONSE_reply_refresh_melt_insufficient_funds (struct MHD_Connection 
*connection,
-                                                    const struct 
TALER_CoinSpendPublicKeyP *coin_pub,
-                                                    struct TALER_Amount 
coin_value,
-                                                    struct 
TALER_EXCHANGEDB_TransactionList *tl,
-                                                    struct TALER_Amount 
requested,
-                                                    struct TALER_Amount 
residual)
-{
-  json_t *history;
-
-  history = TEH_RESPONSE_compile_transaction_history (tl);
-  if (NULL == history)
-    return TEH_RESPONSE_reply_internal_db_error (connection,
-                                                
TALER_EC_REFRESH_MELT_HISTORY_DB_ERROR_INSUFFICIENT_FUNDS);
-  return TEH_RESPONSE_reply_json_pack (connection,
-                                       MHD_HTTP_FORBIDDEN,
-                                       "{s:s, s:I, s:o, s:o, s:o, s:o, s:o}",
-                                       "error",
-                                      "insufficient funds",
-                                      "code",
-                                      (json_int_t) 
TALER_EC_REFRESH_MELT_INSUFFICIENT_FUNDS,
-                                       "coin_pub",
-                                       GNUNET_JSON_from_data_auto (coin_pub),
-                                       "original_value",
-                                       TALER_JSON_from_amount (&coin_value),
-                                       "residual_value",
-                                       TALER_JSON_from_amount (&residual),
-                                       "requested_value",
-                                       TALER_JSON_from_amount (&requested),
-                                       "history",
-                                       history);
-}
-
-
-/**
- * Send a response to a "/refresh/melt" request.
- *
- * @param connection the connection to send the response to
- * @param session_hash hash of the refresh session
- * @param noreveal_index which index will the client not have to reveal
- * @return a MHD status code
- */
-int
-TEH_RESPONSE_reply_refresh_melt_success (struct MHD_Connection *connection,
-                                         const struct GNUNET_HashCode 
*session_hash,
-                                         uint16_t noreveal_index)
-{
-  struct TALER_RefreshMeltConfirmationPS body;
-  struct TALER_ExchangePublicKeyP pub;
-  struct TALER_ExchangeSignatureP sig;
-  json_t *sig_json;
-
-  body.purpose.size = htonl (sizeof (struct TALER_RefreshMeltConfirmationPS));
-  body.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_MELT);
-  body.session_hash = *session_hash;
-  body.noreveal_index = htons (noreveal_index);
-  body.reserved = htons (0);
-  TEH_KS_sign (&body.purpose,
-               &pub,
-               &sig);
-  sig_json = GNUNET_JSON_from_data_auto (&sig);
-  GNUNET_assert (NULL != sig_json);
-  return TEH_RESPONSE_reply_json_pack (connection,
-                                       MHD_HTTP_OK,
-                                       "{s:i, s:o, s:o}",
-                                       "noreveal_index", (int) noreveal_index,
-                                       "exchange_sig", sig_json,
-                                       "exchange_pub", 
GNUNET_JSON_from_data_auto (&pub));
-}
-
-
-/**
- * Send a response for "/refresh/reveal".
- *
- * @param connection the connection to send the response to
- * @param num_newcoins number of new coins for which we reveal data
- * @param sigs array of @a num_newcoins signatures revealed
- * @return a MHD result code
- */
-int
-TEH_RESPONSE_reply_refresh_reveal_success (struct MHD_Connection *connection,
-                                           unsigned int num_newcoins,
-                                           const struct 
TALER_DenominationSignature *sigs)
-{
-  int newcoin_index;
-  json_t *root;
-  json_t *obj;
-  json_t *list;
-  int ret;
-
-  list = json_array ();
-  for (newcoin_index = 0; newcoin_index < num_newcoins; newcoin_index++)
-  {
-    obj = json_object ();
-    json_object_set_new (obj,
-                        "ev_sig",
-                        GNUNET_JSON_from_rsa_signature 
(sigs[newcoin_index].rsa_signature));
-    GNUNET_assert (0 ==
-                   json_array_append_new (list,
-                                          obj));
-  }
-  root = json_object ();
-  json_object_set_new (root,
-                       "ev_sigs",
-                       list);
-  ret = TEH_RESPONSE_reply_json (connection,
-                                 root,
-                                 MHD_HTTP_OK);
-  json_decref (root);
-  return ret;
-}
-
-
-/**
- * Send a response for a failed "/refresh/reveal", where the
- * revealed value(s) do not match the original commitment.
- *
- * @param connection the connection to send the response to
- * @param session info about session
- * @param commit_coins array of @a num_newcoins committed envelopes at offset 
@a gamma
- * @param denom_pubs array of @a num_newcoins denomination keys for the new 
coins
- * @param gamma_tp transfer public key at offset @a gamma
- * @return a MHD result code
- */
-int
-TEH_RESPONSE_reply_refresh_reveal_missmatch (struct MHD_Connection *connection,
-                                             const struct 
TALER_EXCHANGEDB_RefreshSession *session,
-                                             const struct 
TALER_EXCHANGEDB_RefreshCommitCoin *commit_coins,
-                                             const struct 
TALER_DenominationPublicKey *denom_pubs,
-                                             const struct 
TALER_TransferPublicKeyP *gamma_tp)
-{
-  json_t *info_new;
-  json_t *info_commit_k;
-  unsigned int i;
-
-  info_new = json_array ();
-  info_commit_k = json_array ();
-  for (i=0;i<session->num_newcoins;i++)
-  {
-    const struct TALER_EXCHANGEDB_RefreshCommitCoin *cc;
-    json_t *cc_json;
-
-    GNUNET_assert (0 ==
-                   json_array_append_new (info_new,
-                                          GNUNET_JSON_from_rsa_public_key 
(denom_pubs[i].rsa_public_key)));
-
-    cc = &commit_coins[i];
-    cc_json = json_pack ("{s:o}",
-                         "coin_ev",
-                         GNUNET_JSON_from_data (cc->coin_ev,
-                                                cc->coin_ev_size));
-    GNUNET_assert (0 ==
-                   json_array_append_new (info_commit_k,
-                                          cc_json));
-  }
-  return TEH_RESPONSE_reply_json_pack (connection,
-                                       MHD_HTTP_CONFLICT,
-                                       "{s:s, s:I, s:o, s:o, s:o, s:o, s:o, 
s:o, s:o, s:i}",
-                                       "error", "commitment violation",
-                                      "code", (json_int_t) 
TALER_EC_REFRESH_REVEAL_COMMITMENT_VIOLATION,
-                                       "coin_sig", GNUNET_JSON_from_data_auto 
(&session->melt.coin_sig),
-                                       "coin_pub", GNUNET_JSON_from_data_auto 
(&session->melt.coin.coin_pub),
-                                       "melt_amount_with_fee", 
TALER_JSON_from_amount (&session->melt.amount_with_fee),
-                                       "melt_fee", TALER_JSON_from_amount 
(&session->melt.melt_fee),
-                                       "newcoin_infos", info_new,
-                                       "commit_infos", info_commit_k,
-                                       "gamma_tp", GNUNET_JSON_from_data_auto 
(gamma_tp),
-                                       "gamma", (int) session->noreveal_index);
-}
-
-
-/**
- * Send a response for "/refresh/link".
- *
- * @param connection the connection to send the response to
- * @param num_sessions number of sessions the coin was used in
- * @param sessions array of @a num_session entries with
- *                  information for each session
- * @return a MHD result code
- */
-int
-TEH_RESPONSE_reply_refresh_link_success (struct MHD_Connection *connection,
-                                         unsigned int num_sessions,
-                                         const struct 
TEH_RESPONSE_LinkSessionInfo *sessions)
-{
-  json_t *root;
-  json_t *mlist;
-  int res;
-  unsigned int i;
-
-  mlist = json_array ();
-  for (i=0;i<num_sessions;i++)
-  {
-    const struct TALER_EXCHANGEDB_LinkDataList *pos;
-    json_t *list = json_array ();
-
-    for (pos = sessions[i].ldl; NULL != pos; pos = pos->next)
-    {
-      json_t *obj;
-
-      obj = json_object ();
-      json_object_set_new (obj,
-                           "denom_pub",
-                           GNUNET_JSON_from_rsa_public_key 
(pos->denom_pub.rsa_public_key));
-      json_object_set_new (obj,
-                           "ev_sig",
-                           GNUNET_JSON_from_rsa_signature 
(pos->ev_sig.rsa_signature));
-      GNUNET_assert (0 ==
-                     json_array_append_new (list,
-                                            obj));
-    }
-    root = json_object ();
-    json_object_set_new (root,
-                         "new_coins",
-                         list);
-    json_object_set_new (root,
-                         "transfer_pub",
-                         GNUNET_JSON_from_data_auto 
(&sessions[i].transfer_pub));
-    GNUNET_assert (0 ==
-                   json_array_append_new (mlist,
-                                          root));
-  }
-  res = TEH_RESPONSE_reply_json (connection,
-                                 mlist,
-                                 MHD_HTTP_OK);
-  json_decref (mlist);
-  return res;
-}
-
-
-/**
  * A merchant asked for details about a deposit, but
  * we do not know anything about the deposit. Generate the
  * 404 reply.
diff --git a/src/exchange/taler-exchange-httpd_responses.h 
b/src/exchange/taler-exchange-httpd_responses.h
index 5a52be3..226aa8a 100644
--- a/src/exchange/taler-exchange-httpd_responses.h
+++ b/src/exchange/taler-exchange-httpd_responses.h
@@ -301,111 +301,6 @@ TEH_RESPONSE_reply_transfer_pending (struct 
MHD_Connection *connection,
 
 
 /**
- * Send a confirmation response to a "/refresh/melt" request.
- *
- * @param connection the connection to send the response to
- * @param session_hash hash of the refresh session
- * @param noreveal_index which index will the client not have to reveal
- * @return a MHD status code
- */
-int
-TEH_RESPONSE_reply_refresh_melt_success (struct MHD_Connection *connection,
-                                         const struct GNUNET_HashCode 
*session_hash,
-                                         uint16_t noreveal_index);
-
-
-/**
- * Send a response for a failed "/refresh/melt" request.  The
- * transaction history of the given coin demonstrates that the
- * @a residual value of the coin is below the @a requested
- * contribution of the coin for the melt.  Thus, the exchange
- * refuses the melt operation.
- *
- * @param connection the connection to send the response to
- * @param coin_pub public key of the coin
- * @param coin_value original value of the coin
- * @param tl transaction history for the coin
- * @param requested how much this coin was supposed to contribute
- * @param residual remaining value of the coin (after subtracting @a tl)
- * @return a MHD result code
- */
-int
-TEH_RESPONSE_reply_refresh_melt_insufficient_funds (struct MHD_Connection 
*connection,
-                                                    const struct 
TALER_CoinSpendPublicKeyP *coin_pub,
-                                                    struct TALER_Amount 
coin_value,
-                                                    struct 
TALER_EXCHANGEDB_TransactionList *tl,
-                                                    struct TALER_Amount 
requested,
-                                                    struct TALER_Amount 
residual);
-
-
-/**
- * Send a response for "/refresh/reveal".
- *
- * @param connection the connection to send the response to
- * @param num_newcoins number of new coins for which we reveal data
- * @param sigs array of @a num_newcoins signatures revealed
- * @return a MHD result code
- */
-int
-TEH_RESPONSE_reply_refresh_reveal_success (struct MHD_Connection *connection,
-                                           unsigned int num_newcoins,
-                                           const struct 
TALER_DenominationSignature *sigs);
-
-
-/**
- * Send a response for a failed "/refresh/reveal", where the
- * revealed value(s) do not match the original commitment.
- *
- * @param connection the connection to send the response to
- * @param session info about session
- * @param commit_coins array of @a num_newcoins committed envelopes at offset 
@a gamma
- * @param denom_pubs array of @a num_newcoins denomination keys for the new 
coins
- * @param gamma_tp transfer public key at offset @a gamma
- * @return a MHD result code
- */
-int
-TEH_RESPONSE_reply_refresh_reveal_missmatch (struct MHD_Connection *connection,
-                                             const struct 
TALER_EXCHANGEDB_RefreshSession *session,
-                                             const struct 
TALER_EXCHANGEDB_RefreshCommitCoin *commit_coins,
-                                             const struct 
TALER_DenominationPublicKey *denom_pubs,
-                                             const struct 
TALER_TransferPublicKeyP *gamma_tp);
-
-
-/**
- * @brief Information for each session a coin was melted into.
- */
-struct TEH_RESPONSE_LinkSessionInfo
-{
-  /**
-   * Transfer public key of the coin.
-   */
-  struct TALER_TransferPublicKeyP transfer_pub;
-
-  /**
-   * Linked data of coins being created in the session.
-   */
-  struct TALER_EXCHANGEDB_LinkDataList *ldl;
-
-};
-
-
-/**
- * Send a response for "/refresh/link".
- *
- * @param connection the connection to send the response to
- * @param num_sessions number of sessions the coin was used in
- * @param sessions array of @a num_session entries with
- *                  information for each session
- * @return a MHD result code
- */
-int
-TEH_RESPONSE_reply_refresh_link_success (struct MHD_Connection *connection,
-                                         unsigned int num_sessions,
-                                         const struct 
TEH_RESPONSE_LinkSessionInfo *sessions);
-
-
-
-/**
  * Compile the transaction history of a coin into a JSON object.
  *
  * @param tl transaction history to JSON-ify

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



reply via email to

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