summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2017-06-19 21:04:49 +0200
committerChristian Grothoff <christian@grothoff.org>2017-06-19 21:05:15 +0200
commitf8e62141f27c296b326fc810f18206d16d41b468 (patch)
tree5db669aa495fee1728b1979db34da572f8fcc9af /src
parent703c54a279af8441e1a1e686f71d26bcd69bfd7d (diff)
downloadexchange-f8e62141f27c296b326fc810f18206d16d41b468.tar.gz
exchange-f8e62141f27c296b326fc810f18206d16d41b468.tar.bz2
exchange-f8e62141f27c296b326fc810f18206d16d41b468.zip
split httpd_refresh.c into refresh_melt, refresh_link and refresh_reveal
Diffstat (limited to 'src')
-rw-r--r--src/exchange/Makefile.am4
-rw-r--r--src/exchange/taler-exchange-httpd.c6
-rw-r--r--src/exchange/taler-exchange-httpd_db.c945
-rw-r--r--src/exchange/taler-exchange-httpd_db.h94
-rw-r--r--src/exchange/taler-exchange-httpd_refresh_link.c286
-rw-r--r--src/exchange/taler-exchange-httpd_refresh_link.h49
-rw-r--r--src/exchange/taler-exchange-httpd_refresh_melt.c (renamed from src/exchange/taler-exchange-httpd_refresh.c)622
-rw-r--r--src/exchange/taler-exchange-httpd_refresh_melt.h52
-rw-r--r--src/exchange/taler-exchange-httpd_refresh_reveal.c833
-rw-r--r--src/exchange/taler-exchange-httpd_refresh_reveal.h (renamed from src/exchange/taler-exchange-httpd_refresh.h)50
-rw-r--r--src/exchange/taler-exchange-httpd_responses.c245
-rw-r--r--src/exchange/taler-exchange-httpd_responses.h105
12 files changed, 1688 insertions, 1603 deletions
diff --git a/src/exchange/Makefile.am b/src/exchange/Makefile.am
index 1d837243..9ff98d12 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 43c8a803..1a4d2863 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 9871b7f3..e7be6af0 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 85a1604c..a61f9329 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 00000000..c85431f0
--- /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 00000000..037b0d30
--- /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
index 3a8875f4..1c4e5e86 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 00000000..a938abff
--- /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_refresh_reveal.c b/src/exchange/taler-exchange-httpd_refresh_reveal.c
new file mode 100644
index 00000000..05422a85
--- /dev/null
+++ b/src/exchange/taler-exchange-httpd_refresh_reveal.c
@@ -0,0 +1,833 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014-2017 Inria & GNUnet e.V.
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU Affero General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-exchange-httpd_refresh_reveal.c
+ * @brief Handle /refresh/reveal requests
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include <jansson.h>
+#include <microhttpd.h>
+#include "taler-exchange-httpd_parsing.h"
+#include "taler-exchange-httpd_mhd.h"
+#include "taler-exchange-httpd_refresh_reveal.h"
+#include "taler-exchange-httpd_responses.h"
+#include "taler-exchange-httpd_keystate.h"
+
+
+/**
+ * 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)
+
+
+
+
+
+/**
+ * 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)
+{
+ 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
+ */
+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;
+ 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);
+}
+
+
+
+/**
+ * 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 = 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
+ */
+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;
+ 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 = 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;
+}
+
+
+/**
+ * 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 = 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;
+}
+
+
+/* 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
index 61b3b4f9..cc5ac3f8 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 286572fe..85575b67 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 5a52be38..226aa8aa 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