/*
  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 
*/
/**
 * @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 
#include 
#include 
#include "taler-exchange-httpd_parsing.h"
#include "taler-exchange-httpd_mhd.h"
#include "taler-exchange-httpd_refresh_reveal.h"
#include "taler-exchange-httpd_responses.h"
#include "taler-exchange-httpd_keystate.h"
/**
 * Send a response for "/refresh/reveal".
 *
 * @param connection the connection to send the response to
 * @param num_newcoins number of new coins for which we reveal data
 * @param sigs array of @a num_newcoins signatures revealed
 * @return a MHD result code
 */
static int
reply_refresh_reveal_success (struct MHD_Connection *connection,
			      unsigned int num_newcoins,
			      const struct TALER_DenominationSignature *sigs)
{
  json_t *root;
  json_t *obj;
  json_t *list;
  int ret;
  list = json_array ();
  for (unsigned int newcoin_index = 0;
       newcoin_index < num_newcoins;
       newcoin_index++)
  {
    obj = json_object ();
    json_object_set_new (obj,
			 "ev_sig",
			 GNUNET_JSON_from_rsa_signature (sigs[newcoin_index].rsa_signature));
    GNUNET_assert (0 ==
                   json_array_append_new (list,
                                          obj));
  }
  root = json_object ();
  json_object_set_new (root,
                       "ev_sigs",
                       list);
  ret = TEH_RESPONSE_reply_json (connection,
                                 root,
                                 MHD_HTTP_OK);
  json_decref (root);
  return ret;
}
/**
 * Send a response for a failed "/refresh/reveal", where the
 * revealed value(s) do not match the original commitment.
 *
 * @param connection the connection to send the response to
 * @param session info about session
 * @param commit_coins array of @a num_newcoins committed envelopes at offset @a gamma
 * @param denom_pubs array of @a num_newcoins denomination keys for the new coins
 * @param gamma_tp transfer public key at offset @a gamma
 * @return a MHD result code
 */
static int
reply_refresh_reveal_missmatch (struct MHD_Connection *connection,
				const struct TALER_EXCHANGEDB_RefreshSession *session,
				const struct TALER_EXCHANGEDB_RefreshCommitCoin *commit_coins,
				const struct TALER_DenominationPublicKey *denom_pubs,
				const struct TALER_TransferPublicKeyP *gamma_tp)
{
  json_t *info_new;
  json_t *info_commit_k;
  info_new = json_array ();
  info_commit_k = json_array ();
  for (unsigned int i=0;inum_newcoins;i++)
  {
    const struct TALER_EXCHANGEDB_RefreshCommitCoin *cc;
    json_t *cc_json;
    GNUNET_assert (0 ==
                   json_array_append_new (info_new,
                                          GNUNET_JSON_from_rsa_public_key (denom_pubs[i].rsa_public_key)));
    cc = &commit_coins[i];
    cc_json = json_pack ("{s:o}",
                         "coin_ev",
                         GNUNET_JSON_from_data (cc->coin_ev,
                                                cc->coin_ev_size));
    GNUNET_assert (0 ==
                   json_array_append_new (info_commit_k,
                                          cc_json));
  }
  return TEH_RESPONSE_reply_json_pack (connection,
                                       MHD_HTTP_CONFLICT,
                                       "{s:s, s:I, s:o, s:o, s:o, s:o, s:o, s:o, s:o, s:i}",
                                       "error", "commitment violation",
				       "code", (json_int_t) TALER_EC_REFRESH_REVEAL_COMMITMENT_VIOLATION,
                                       "coin_sig", GNUNET_JSON_from_data_auto (&session->melt.coin_sig),
                                       "coin_pub", GNUNET_JSON_from_data_auto (&session->melt.coin.coin_pub),
                                       "melt_amount_with_fee", TALER_JSON_from_amount (&session->melt.amount_with_fee),
                                       "melt_fee", TALER_JSON_from_amount (&session->melt.melt_fee),
                                       "newcoin_infos", info_new,
                                       "commit_infos", info_commit_k,
                                       "gamma_tp", GNUNET_JSON_from_data_auto (gamma_tp),
                                       "gamma", (int) session->noreveal_index);
}
/**
 * Check if the given @a transfer_privs correspond to an honest
 * commitment for the given session.
 * Checks that the transfer private keys match their commitments.
 * Then derives the shared secret for each #TALER_CNC_KAPPA, and check that they match.
 *
 * @param connection the MHD connection to handle
 * @param session database connection to use
 * @param session_hash hash of session to query
 * @param off commitment offset to check
 * @param transfer_priv private transfer key
 * @param melt information about the melted coin
 * @param num_newcoins number of newcoins being generated
 * @param denom_pubs array of @a num_newcoins keys for the new coins
 * @param hash_context hash context to update by hashing in the data
 *                     from this offset
 * @return #GNUNET_OK if the committment was honest,
 *         #GNUNET_NO if there was a problem and we generated an error message
 *         #GNUNET_SYSERR if we could not even generate an error message
 */
static int
check_commitment (struct MHD_Connection *connection,
                  struct TALER_EXCHANGEDB_Session *session,
                  const struct GNUNET_HashCode *session_hash,
                  unsigned int off,
                  const struct TALER_TransferPrivateKeyP *transfer_priv,
                  const struct TALER_EXCHANGEDB_RefreshMelt *melt,
                  unsigned int num_newcoins,
                  const struct TALER_DenominationPublicKey *denom_pubs,
                  struct GNUNET_HashContext *hash_context)
{
  struct TALER_TransferSecretP transfer_secret;
  TALER_link_reveal_transfer_secret (transfer_priv,
                                     &melt->coin.coin_pub,
                                     &transfer_secret);
  /* Check that the commitments for all new coins were correct */
  for (unsigned int j = 0; j < num_newcoins; j++)
  {
    struct TALER_FreshCoinP fc;
    struct TALER_CoinSpendPublicKeyP coin_pub;
    struct GNUNET_HashCode h_msg;
    char *buf;
    size_t buf_len;
    TALER_setup_fresh_coin (&transfer_secret,
                            j,
                            &fc);
    GNUNET_CRYPTO_eddsa_key_get_public (&fc.coin_priv.eddsa_priv,
                                        &coin_pub.eddsa_pub);
    GNUNET_CRYPTO_hash (&coin_pub,
                        sizeof (struct TALER_CoinSpendPublicKeyP),
                        &h_msg);
    if (GNUNET_YES !=
        GNUNET_CRYPTO_rsa_blind (&h_msg,
                                 &fc.blinding_key.bks,
                                 denom_pubs[j].rsa_public_key,
                                 &buf,
                                 &buf_len))
    {
      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                  "Blind failed (bad denomination key!?)\n");
      return (MHD_YES ==
	      TEH_RESPONSE_reply_internal_error (connection,
						 TALER_EC_REFRESH_REVEAL_BLINDING_ERROR,
						 "Blinding error"))
        ? GNUNET_NO : GNUNET_SYSERR;
    }
    GNUNET_CRYPTO_hash_context_read (hash_context,
                                     buf,
                                     buf_len);
    GNUNET_free (buf);
  }
  return GNUNET_OK;
}
/**
 * State for a /refresh/reveal operation.
 */
struct RevealContext
{
  /**
   * Hash of the refresh session.
   */
  const struct GNUNET_HashCode *session_hash;
  /**
   * Database session used to execute the transaction.
   */
  struct TALER_EXCHANGEDB_Session *session;
  /**
   * Session state from the database.
   */
  struct TALER_EXCHANGEDB_RefreshSession refresh_session;
  /**
   * Array of denomination public keys used for the refresh.
   */
  struct TALER_DenominationPublicKey *denom_pubs;
  /**
   * Envelopes with the signatures to be returned.
   */
  struct TALER_DenominationSignature *ev_sigs;
  /**
   * Commitment data from the DB giving data about original
   * commitments, in particular the blinded envelopes (for
   * index gamma).
   */
  struct TALER_EXCHANGEDB_RefreshCommitCoin *commit_coins;
  /**
   * Transfer public key associated with the gamma value
   * selected by the exchange.
   */
  struct TALER_TransferPublicKeyP gamma_tp;
  /**
   * Transfer private keys revealed to us.
   */
  struct TALER_TransferPrivateKeyP transfer_privs[TALER_CNC_KAPPA - 1];
};
/**
 * Exchange a coin as part of a refresh operation.  Obtains the
 * envelope from the database and performs the signing operation.
 *
 * @param connection the MHD connection to handle
 * @param session database connection to use
 * @param session_hash hash of session to query
 * @param key_state key state to lookup denomination pubs
 * @param denom_pub denomination key for the coin to create
 * @param commit_coin the coin that was committed
 * @param coin_off number of the coin
 * @param[out] ev_sig set to signature over the coin upon success
 * @return database transaction status
 */
static enum GNUNET_DB_QueryStatus
refresh_exchange_coin (struct MHD_Connection *connection,
                       struct TALER_EXCHANGEDB_Session *session,
                       const struct GNUNET_HashCode *session_hash,
                       struct TEH_KS_StateHandle *key_state,
                       const struct TALER_DenominationPublicKey *denom_pub,
                       const struct TALER_EXCHANGEDB_RefreshCommitCoin *commit_coin,
                       unsigned int coin_off,
		       struct TALER_DenominationSignature *ev_sig)
{
  struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki;
  enum GNUNET_DB_QueryStatus qs;
  dki = TEH_KS_denomination_key_lookup (key_state,
                                        denom_pub,
					TEH_KS_DKU_WITHDRAW);
  if (NULL == dki)
  {
    GNUNET_break (0);
    ev_sig->rsa_signature = NULL;
    return GNUNET_DB_STATUS_HARD_ERROR;
  }
  qs = TEH_plugin->get_refresh_out (TEH_plugin->cls,
				    session,
				    session_hash,
				    coin_off,
				    ev_sig);
  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
  {
    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
                "Returning cached reply for /refresh/reveal signature\n");
    return qs;
  }
  if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != qs)
    return qs;
  ev_sig->rsa_signature
    = GNUNET_CRYPTO_rsa_sign_blinded (dki->denom_priv.rsa_private_key,
                                      commit_coin->coin_ev,
                                      commit_coin->coin_ev_size);
  if (NULL == ev_sig->rsa_signature)
  {
    GNUNET_break (0);
    return GNUNET_DB_STATUS_HARD_ERROR;
  }
  qs = TEH_plugin->insert_refresh_out (TEH_plugin->cls,
				       session,
				       session_hash,
				       coin_off,
				       ev_sig);
  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
  {
    GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
    if (NULL != ev_sig->rsa_signature)
    {
      GNUNET_CRYPTO_rsa_signature_free (ev_sig->rsa_signature);
      ev_sig->rsa_signature = NULL;
    }
  }
  return qs;
}
/**
 * Cleanup state of the transaction stored in @a rc.
 *
 * @param rc context to clean up
 */
static void
cleanup_rc (struct RevealContext *rc)
{
  if (NULL != rc->denom_pubs)
  {
    for (unsigned int i=0;irefresh_session.num_newcoins;i++)
      if (NULL != rc->denom_pubs[i].rsa_public_key)
	GNUNET_CRYPTO_rsa_public_key_free (rc->denom_pubs[i].rsa_public_key);
    GNUNET_free (rc->denom_pubs);
    rc->denom_pubs = NULL;
  }
  if (NULL != rc->commit_coins)
  {
    for (unsigned int j=0;jrefresh_session.num_newcoins;j++)
      GNUNET_free_non_null (rc->commit_coins[j].coin_ev);
    GNUNET_free (rc->commit_coins);
    rc->commit_coins = NULL;
  }
  if (NULL != rc->ev_sigs)
  {
    for (unsigned int j=0;jrefresh_session.num_newcoins;j++)
      if (NULL != rc->ev_sigs[j].rsa_signature)
	GNUNET_CRYPTO_rsa_signature_free (rc->ev_sigs[j].rsa_signature);
    GNUNET_free (rc->ev_sigs);
    rc->ev_sigs = NULL;
  }
  if (NULL != rc->refresh_session.melt.coin.denom_sig.rsa_signature)
  {
    GNUNET_CRYPTO_rsa_signature_free (rc->refresh_session.melt.coin.denom_sig.rsa_signature);
    rc->refresh_session.melt.coin.denom_sig.rsa_signature = NULL;
  }
  if (NULL != rc->refresh_session.melt.coin.denom_pub.rsa_public_key)
  {
    GNUNET_CRYPTO_rsa_public_key_free (rc->refresh_session.melt.coin.denom_pub.rsa_public_key);
    rc->refresh_session.melt.coin.denom_pub.rsa_public_key = NULL;
  }
}
/**
 * Execute a "/refresh/reveal".  The client is revealing to us the
 * transfer keys for @a #TALER_CNC_KAPPA-1 sets of coins.  Verify that the
 * revealed transfer keys would allow linkage to the blinded coins,
 * and if so, return the signed coins for corresponding to the set of
 * coins that was not chosen.
 *
 * IF it returns a non-error code, the transaction logic MUST
 * NOT queue a MHD response.  IF it returns an hard error, the
 * transaction logic MUST queue a MHD response and set @a mhd_ret.  IF
 * it returns the soft error code, the function MAY be called again to
 * retry and MUST not queue a MHD response.
 *
 * @param cls closure of type `struct RevealContext`
 * @param connection MHD request which triggered the transaction
 * @param session database session to use
 * @param[out] mhd_ret set to MHD response status for @a connection,
 *             if transaction failed (!)
 * @return transaction status
 */
static enum GNUNET_DB_QueryStatus
refresh_reveal_transaction (void *cls,
			    struct MHD_Connection *connection,
			    struct TALER_EXCHANGEDB_Session *session,
			    int *mhd_ret)
{
  struct RevealContext *rc = cls;
  unsigned int off;
  struct GNUNET_HashContext *hash_context;
  struct GNUNET_HashCode sh_check;
  enum GNUNET_DB_QueryStatus qs;
  rc->session = session;
  qs = TEH_plugin->get_refresh_session (TEH_plugin->cls,
					session,
					rc->session_hash,
					&rc->refresh_session);
  if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
  {
    *mhd_ret = TEH_RESPONSE_reply_arg_invalid (connection,
					       TALER_EC_REFRESH_REVEAL_SESSION_UNKNOWN,
					       "session_hash");
    return GNUNET_DB_STATUS_HARD_ERROR;
  }
  if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
    return qs;
  if ( (GNUNET_DB_STATUS_HARD_ERROR == qs) ||
       (rc->refresh_session.noreveal_index >= TALER_CNC_KAPPA) )
  {
    GNUNET_break (0);
    cleanup_rc (rc);
    *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
						     TALER_EC_REFRESH_REVEAL_DB_FETCH_SESSION_ERROR);
    return GNUNET_DB_STATUS_HARD_ERROR;
  }
  rc->denom_pubs = GNUNET_new_array (rc->refresh_session.num_newcoins,
				     struct TALER_DenominationPublicKey);
  qs = TEH_plugin->get_refresh_order (TEH_plugin->cls,
				      session,
				      rc->session_hash,
				      rc->refresh_session.num_newcoins,
				      rc->denom_pubs);
  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
  {
    cleanup_rc (rc);
    if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
      return qs;
    GNUNET_break (0);
    *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
						     TALER_EC_REFRESH_REVEAL_DB_FETCH_ORDER_ERROR);
    return GNUNET_DB_STATUS_HARD_ERROR;
  }
  hash_context = GNUNET_CRYPTO_hash_context_start ();
  /* first, iterate over transfer public keys for hash_context */
  off = 0;
  for (unsigned int i=0;irefresh_session.noreveal_index)
    {
      off = 1;
      /* obtain gamma_tp from db */
      qs = TEH_plugin->get_refresh_transfer_public_key (TEH_plugin->cls,
							session,
							rc->session_hash,
							&rc->gamma_tp);
      if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
      {
        GNUNET_CRYPTO_hash_context_abort (hash_context);
	cleanup_rc (rc);
	if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
	  return qs;
        GNUNET_break (0);
        *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
							 TALER_EC_REFRESH_REVEAL_DB_FETCH_TRANSFER_ERROR);
	return GNUNET_DB_STATUS_HARD_ERROR;
      }
      GNUNET_CRYPTO_hash_context_read (hash_context,
                                       &rc->gamma_tp,
                                       sizeof (struct TALER_TransferPublicKeyP));
    }
    else
    {
      /* compute tp from private key */
      struct TALER_TransferPublicKeyP tp;
      GNUNET_CRYPTO_ecdhe_key_get_public (&rc->transfer_privs[i - off].ecdhe_priv,
                                          &tp.ecdhe_pub);
      GNUNET_CRYPTO_hash_context_read (hash_context,
                                       &tp,
                                       sizeof (struct TALER_TransferPublicKeyP));
    }
  }
  /* next, add all of the hashes from the denomination keys to the
     hash_context */
  for (unsigned int i=0;irefresh_session.num_newcoins;i++)
  {
    char *buf;
    size_t buf_size;
    buf_size = GNUNET_CRYPTO_rsa_public_key_encode (rc->denom_pubs[i].rsa_public_key,
						    &buf);
    GNUNET_CRYPTO_hash_context_read (hash_context,
				     buf,
				     buf_size);
    GNUNET_free (buf);
  }
  /* next, add public key of coin and amount being refreshed */
  {
    struct TALER_AmountNBO melt_amountn;
    GNUNET_CRYPTO_hash_context_read (hash_context,
                                     &rc->refresh_session.melt.coin.coin_pub,
                                     sizeof (struct TALER_CoinSpendPublicKeyP));
    TALER_amount_hton (&melt_amountn,
                       &rc->refresh_session.melt.amount_with_fee);
    GNUNET_CRYPTO_hash_context_read (hash_context,
                                     &melt_amountn,
                                     sizeof (struct TALER_AmountNBO));
  }
  rc->commit_coins = GNUNET_new_array (rc->refresh_session.num_newcoins,
				       struct TALER_EXCHANGEDB_RefreshCommitCoin);
  off = 0;
  for (unsigned int i=0;irefresh_session.noreveal_index)
    {
      off = 1;
      /* obtain commit_coins for the selected gamma value from DB */
      qs = TEH_plugin->get_refresh_commit_coins (TEH_plugin->cls,
						 session,
						 rc->session_hash,
						 rc->refresh_session.num_newcoins,
						 rc->commit_coins);
      if (0 >= qs)
      {	
	cleanup_rc (rc);
        GNUNET_CRYPTO_hash_context_abort (hash_context);
	if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
	  return qs;
        GNUNET_break (0);
        *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
							 TALER_EC_REFRESH_REVEAL_DB_FETCH_COMMIT_ERROR);
	return GNUNET_DB_STATUS_HARD_ERROR;
      }
      /* add envelopes to hash_context */
      for (unsigned int j=0;jrefresh_session.num_newcoins;j++)
      {
        GNUNET_CRYPTO_hash_context_read (hash_context,
                                         rc->commit_coins[j].coin_ev,
                                         rc->commit_coins[j].coin_ev_size);
      }
      continue;
    }
    if (GNUNET_OK !=
        (res = check_commitment (connection,
                                 session,
                                 rc->session_hash,
                                 i,
                                 &rc->transfer_privs[i - off],
                                 &rc->refresh_session.melt,
                                 rc->refresh_session.num_newcoins,
                                 rc->denom_pubs,
                                 hash_context)))
    {
      GNUNET_break_op (0);
      cleanup_rc (rc);
      GNUNET_CRYPTO_hash_context_abort (hash_context);
      *mhd_ret = (GNUNET_NO == res) ? MHD_YES : MHD_NO;
      return GNUNET_DB_STATUS_HARD_ERROR;
    }
  }
  /* Check session hash matches */
  GNUNET_CRYPTO_hash_context_finish (hash_context,
                                     &sh_check);
  if (0 != memcmp (&sh_check,
                   rc->session_hash,
                   sizeof (struct GNUNET_HashCode)))
  {
    GNUNET_break_op (0);
    *mhd_ret = reply_refresh_reveal_missmatch (connection,
					       &rc->refresh_session,
					       rc->commit_coins,
					       rc->denom_pubs,
					       &rc->gamma_tp);
    cleanup_rc (rc);
    return GNUNET_DB_STATUS_HARD_ERROR;
  }
  
  /* Client request OK, sign coins */
  rc->ev_sigs = GNUNET_new_array (rc->refresh_session.num_newcoins,
				  struct TALER_DenominationSignature);
  {
    struct TEH_KS_StateHandle *key_state;
    key_state = TEH_KS_acquire ();
    for (unsigned int j=0;jrefresh_session.num_newcoins;j++)
    {
      qs = refresh_exchange_coin (connection,
				  session,
				  rc->session_hash,
				  key_state,
				  &rc->denom_pubs[j],
				  &rc->commit_coins[j],
				  j,
				  &rc->ev_sigs[j]);
      if ( (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) ||
	   (NULL == rc->ev_sigs[j].rsa_signature) )
      {
	*mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
							 TALER_EC_REFRESH_REVEAL_SIGNING_ERROR);
	qs = GNUNET_DB_STATUS_HARD_ERROR;
	break;
      }
    }
    TEH_KS_release (key_state);
  }
  if (0 >= qs)
  {
    cleanup_rc (rc);
    return qs;
  }
  return qs;
}
/**
 * Handle a "/refresh/reveal" request.   Parses the given JSON
 * transfer private keys and if successful, passes everything to
 * #TEH_DB_execute_refresh_reveal() which will verify that the
 * revealed information is valid then returns the signed refreshed
 * coins.
 *
 * @param connection the MHD connection to handle
 * @param session_hash hash identifying the melting session
 * @param tp_json private transfer keys in JSON format
 * @return MHD result code
  */
static int
handle_refresh_reveal_json (struct MHD_Connection *connection,
                            const struct GNUNET_HashCode *session_hash,
                            const json_t *tp_json)
{
  struct RevealContext rc;
  int mhd_ret;
  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
              "reveal request for session %s\n",
              GNUNET_h2s (session_hash));
  memset (&rc,
	  0,
	  sizeof (rc));
  rc.session_hash = session_hash;
  for (unsigned int i = 0; i < TALER_CNC_KAPPA - 1; i++)
  {
    struct GNUNET_JSON_Specification tp_spec[] = {
      GNUNET_JSON_spec_fixed_auto (NULL, &rc.transfer_privs[i]),
      GNUNET_JSON_spec_end ()
    };
    int res;
    res = TEH_PARSE_json_array (connection,
                                tp_json,
                                tp_spec,
                                i,
				-1);
    GNUNET_break_op (GNUNET_OK == res);
    if (GNUNET_OK != res)
      return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
  }
  if (GNUNET_OK !=
      TEH_DB_run_transaction (connection,
			      &mhd_ret,
			      &refresh_reveal_transaction,
			      &rc))
  {
    cleanup_rc (&rc);
    return mhd_ret;
  }
  mhd_ret = reply_refresh_reveal_success (connection,
					  rc.refresh_session.num_newcoins,
					  rc.ev_sigs);
  cleanup_rc (&rc);
  return mhd_ret;
}
/**
 * Handle a "/refresh/reveal" request. This time, the client reveals
 * the private transfer keys except for the cut-and-choose value
 * returned from "/refresh/melt".  This function parses the revealed
 * keys and secrets and ultimately passes everything to
 * #TEH_DB_execute_refresh_reveal() which will verify that the
 * revealed information is valid then returns the signed refreshed
 * coins.
 *
 * @param rh context of the handler
 * @param connection the MHD connection to handle
 * @param[in,out] connection_cls the connection's closure (can be updated)
 * @param upload_data upload data
 * @param[in,out] upload_data_size number of bytes (left) in @a upload_data
 * @return MHD result code
  */
int
TEH_REFRESH_handler_refresh_reveal (struct TEH_RequestHandler *rh,
                                    struct MHD_Connection *connection,
                                    void **connection_cls,
                                    const char *upload_data,
                                    size_t *upload_data_size)
{
  struct GNUNET_HashCode session_hash;
  int res;
  json_t *root;
  json_t *transfer_privs;
  struct GNUNET_JSON_Specification spec[] = {
    GNUNET_JSON_spec_fixed_auto ("session_hash", &session_hash),
    GNUNET_JSON_spec_json ("transfer_privs", &transfer_privs),
    GNUNET_JSON_spec_end ()
  };
  res = TEH_PARSE_post_json (connection,
                             connection_cls,
                             upload_data,
                             upload_data_size,
                             &root);
  if (GNUNET_SYSERR == res)
    return MHD_NO;
  if ( (GNUNET_NO == res) ||
       (NULL == root) )
    return MHD_YES;
  res = TEH_PARSE_json_data (connection,
                             root,
                             spec);
  json_decref (root);
  if (GNUNET_OK != res)
  {
    GNUNET_break_op (0);
    return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
  }
  /* Determine dimensionality of the request (kappa and #old coins) */
  /* Note we do +1 as 1 row (cut-and-choose!) is missing! */
  if (TALER_CNC_KAPPA != json_array_size (transfer_privs) + 1)
  {
    GNUNET_JSON_parse_free (spec);
    GNUNET_break_op (0);
    return TEH_RESPONSE_reply_arg_invalid (connection,
					   TALER_EC_REFRESH_REVEAL_CNC_TRANSFER_ARRAY_SIZE_INVALID,
                                           "transfer_privs");
  }
  res = handle_refresh_reveal_json (connection,
                                    &session_hash,
                                    transfer_privs);
  GNUNET_JSON_parse_free (spec);
  return res;
}
/* end of taler-exchange-httpd_refresh_reveal.c */