split httpd_refresh.c into refresh_melt, refresh_link and refresh_reveal
This commit is contained in:
parent
703c54a279
commit
f8e62141f2
@ -50,7 +50,9 @@ taler_exchange_httpd_SOURCES = \
|
|||||||
taler-exchange-httpd_mhd.c taler-exchange-httpd_mhd.h \
|
taler-exchange-httpd_mhd.c taler-exchange-httpd_mhd.h \
|
||||||
taler-exchange-httpd_parsing.c taler-exchange-httpd_parsing.h \
|
taler-exchange-httpd_parsing.c taler-exchange-httpd_parsing.h \
|
||||||
taler-exchange-httpd_payback.c taler-exchange-httpd_payback.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_refund.c taler-exchange-httpd_refund.h \
|
||||||
taler-exchange-httpd_reserve_status.c taler-exchange-httpd_reserve_status.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 \
|
taler-exchange-httpd_reserve_withdraw.c taler-exchange-httpd_reserve_withdraw.h \
|
||||||
|
@ -34,11 +34,13 @@
|
|||||||
#include "taler-exchange-httpd_reserve_status.h"
|
#include "taler-exchange-httpd_reserve_status.h"
|
||||||
#include "taler-exchange-httpd_reserve_withdraw.h"
|
#include "taler-exchange-httpd_reserve_withdraw.h"
|
||||||
#include "taler-exchange-httpd_payback.h"
|
#include "taler-exchange-httpd_payback.h"
|
||||||
#include "taler-exchange-httpd_wire.h"
|
#include "taler-exchange-httpd_refresh_link.h"
|
||||||
#include "taler-exchange-httpd_refresh.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_transfer.h"
|
||||||
#include "taler-exchange-httpd_track_transaction.h"
|
#include "taler-exchange-httpd_track_transaction.h"
|
||||||
#include "taler-exchange-httpd_keystate.h"
|
#include "taler-exchange-httpd_keystate.h"
|
||||||
|
#include "taler-exchange-httpd_wire.h"
|
||||||
#if HAVE_DEVELOPER
|
#if HAVE_DEVELOPER
|
||||||
#include "taler-exchange-httpd_test.h"
|
#include "taler-exchange-httpd_test.h"
|
||||||
#endif
|
#endif
|
||||||
|
@ -32,85 +32,6 @@
|
|||||||
*/
|
*/
|
||||||
#define MAX_TRANSACTION_COMMIT_RETRIES 3
|
#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.
|
* 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 */
|
/* end of taler-exchange-httpd_db.c */
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
This file is part of TALER
|
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
|
TALER is free software; you can redistribute it and/or modify it under the
|
||||||
terms of the GNU General Public License as published by the Free Software
|
terms of the GNU 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);
|
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
|
#endif
|
||||||
/* TALER_EXCHANGE_HTTPD_DB_H */
|
/* TALER_EXCHANGE_HTTPD_DB_H */
|
||||||
|
286
src/exchange/taler-exchange-httpd_refresh_link.c
Normal file
286
src/exchange/taler-exchange-httpd_refresh_link.c
Normal file
@ -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 */
|
49
src/exchange/taler-exchange-httpd_refresh_link.h
Normal file
49
src/exchange/taler-exchange-httpd_refresh_link.h
Normal file
@ -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
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
This file is part of TALER
|
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
|
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
|
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/>
|
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||||
*/
|
*/
|
||||||
/**
|
/**
|
||||||
* @file taler-exchange-httpd_refresh.c
|
* @file taler-exchange-httpd_refresh_melt.c
|
||||||
* @brief Handle /refresh/ requests
|
* @brief Handle /refresh/melt requests
|
||||||
* @author Florian Dold
|
* @author Florian Dold
|
||||||
* @author Benedikt Mueller
|
* @author Benedikt Mueller
|
||||||
* @author Christian Grothoff
|
* @author Christian Grothoff
|
||||||
@ -26,11 +26,454 @@
|
|||||||
#include <microhttpd.h>
|
#include <microhttpd.h>
|
||||||
#include "taler-exchange-httpd_parsing.h"
|
#include "taler-exchange-httpd_parsing.h"
|
||||||
#include "taler-exchange-httpd_mhd.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_responses.h"
|
||||||
#include "taler-exchange-httpd_keystate.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.
|
* 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
|
* We now need to validate the coins being melted and the session signature
|
||||||
@ -149,13 +592,13 @@ handle_refresh_melt_binary (struct MHD_Connection *connection,
|
|||||||
"error", "value mismatch",
|
"error", "value mismatch",
|
||||||
"code", (json_int_t) TALER_EC_REFRESH_MELT_FEES_MISSMATCH);
|
"code", (json_int_t) TALER_EC_REFRESH_MELT_FEES_MISSMATCH);
|
||||||
}
|
}
|
||||||
return TEH_DB_execute_refresh_melt (connection,
|
return execute_refresh_melt (connection,
|
||||||
session_hash,
|
session_hash,
|
||||||
num_new_denoms,
|
num_new_denoms,
|
||||||
denom_pubs,
|
denom_pubs,
|
||||||
coin_melt_details,
|
coin_melt_details,
|
||||||
commit_coin,
|
commit_coin,
|
||||||
transfer_pubs);
|
transfer_pubs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -597,159 +1040,4 @@ TEH_REFRESH_handler_refresh_melt (struct TEH_RequestHandler *rh,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/* end of taler-exchange-httpd_refresh_melt.c */
|
||||||
* 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 */
|
|
52
src/exchange/taler-exchange-httpd_refresh_melt.h
Normal file
52
src/exchange/taler-exchange-httpd_refresh_melt.h
Normal file
@ -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
|
833
src/exchange/taler-exchange-httpd_refresh_reveal.c
Normal file
833
src/exchange/taler-exchange-httpd_refresh_reveal.c
Normal file
@ -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 */
|
@ -1,6 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
This file is part of TALER
|
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
|
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
|
terms of the GNU Affero General Public License as published by the Free Software
|
||||||
@ -14,41 +14,20 @@
|
|||||||
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||||
*/
|
*/
|
||||||
/**
|
/**
|
||||||
* @file taler-exchange-httpd_refresh.h
|
* @file taler-exchange-httpd_refresh_reveal.h
|
||||||
* @brief Handle /refresh/ requests
|
* @brief Handle /refresh/reveal requests
|
||||||
* @author Florian Dold
|
* @author Florian Dold
|
||||||
* @author Benedikt Mueller
|
* @author Benedikt Mueller
|
||||||
* @author Christian Grothoff
|
* @author Christian Grothoff
|
||||||
*/
|
*/
|
||||||
#ifndef TALER_EXCHANGE_HTTPD_REFRESH_H
|
#ifndef TALER_EXCHANGE_HTTPD_REFRESH_REVEAL_H
|
||||||
#define TALER_EXCHANGE_HTTPD_REFRESH_H
|
#define TALER_EXCHANGE_HTTPD_REFRESH_REVEAL_H
|
||||||
|
|
||||||
#include <gnunet/gnunet_util_lib.h>
|
#include <gnunet/gnunet_util_lib.h>
|
||||||
#include <microhttpd.h>
|
#include <microhttpd.h>
|
||||||
#include "taler-exchange-httpd.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);
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle a "/refresh/reveal" request. This time, the client reveals
|
* Handle a "/refresh/reveal" request. This time, the client reveals
|
||||||
* the private transfer keys except for the cut-and-choose value
|
* the private transfer keys except for the cut-and-choose value
|
||||||
@ -72,23 +51,4 @@ TEH_REFRESH_handler_refresh_reveal (struct TEH_RequestHandler *rh,
|
|||||||
const char *upload_data,
|
const char *upload_data,
|
||||||
size_t *upload_data_size);
|
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
|
#endif
|
@ -878,251 +878,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
|
* A merchant asked for details about a deposit, but
|
||||||
* we do not know anything about the deposit. Generate the
|
* we do not know anything about the deposit. Generate the
|
||||||
|
@ -300,111 +300,6 @@ TEH_RESPONSE_reply_transfer_pending (struct MHD_Connection *connection,
|
|||||||
struct GNUNET_TIME_Absolute planned_exec_time);
|
struct GNUNET_TIME_Absolute planned_exec_time);
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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.
|
* Compile the transaction history of a coin into a JSON object.
|
||||||
*
|
*
|
||||||
|
Loading…
Reference in New Issue
Block a user