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_parsing.c taler-exchange-httpd_parsing.h \
|
||||
taler-exchange-httpd_payback.c taler-exchange-httpd_payback.h \
|
||||
taler-exchange-httpd_refresh.c taler-exchange-httpd_refresh.h \
|
||||
taler-exchange-httpd_refresh_link.c taler-exchange-httpd_refresh_link.h \
|
||||
taler-exchange-httpd_refresh_melt.c taler-exchange-httpd_refresh_melt.h \
|
||||
taler-exchange-httpd_refresh_reveal.c taler-exchange-httpd_refresh_reveal.h \
|
||||
taler-exchange-httpd_refund.c taler-exchange-httpd_refund.h \
|
||||
taler-exchange-httpd_reserve_status.c taler-exchange-httpd_reserve_status.h \
|
||||
taler-exchange-httpd_reserve_withdraw.c taler-exchange-httpd_reserve_withdraw.h \
|
||||
|
@ -34,11 +34,13 @@
|
||||
#include "taler-exchange-httpd_reserve_status.h"
|
||||
#include "taler-exchange-httpd_reserve_withdraw.h"
|
||||
#include "taler-exchange-httpd_payback.h"
|
||||
#include "taler-exchange-httpd_wire.h"
|
||||
#include "taler-exchange-httpd_refresh.h"
|
||||
#include "taler-exchange-httpd_refresh_link.h"
|
||||
#include "taler-exchange-httpd_refresh_melt.h"
|
||||
#include "taler-exchange-httpd_refresh_reveal.h"
|
||||
#include "taler-exchange-httpd_track_transfer.h"
|
||||
#include "taler-exchange-httpd_track_transaction.h"
|
||||
#include "taler-exchange-httpd_keystate.h"
|
||||
#include "taler-exchange-httpd_wire.h"
|
||||
#if HAVE_DEVELOPER
|
||||
#include "taler-exchange-httpd_test.h"
|
||||
#endif
|
||||
|
@ -32,85 +32,6 @@
|
||||
*/
|
||||
#define MAX_TRANSACTION_COMMIT_RETRIES 3
|
||||
|
||||
/**
|
||||
* Code to begin a transaction, must be inline as we define a block
|
||||
* that ends with #COMMIT_TRANSACTION() within which we perform a number
|
||||
* of retries. Note that this code may call "return" internally, so
|
||||
* it must be called within a function where any cleanup will be done
|
||||
* by the caller. Furthermore, the function's return value must
|
||||
* match that of a #TEH_RESPONSE_reply_internal_db_error() status code.
|
||||
*
|
||||
* @param session session handle
|
||||
* @param connection connection handle
|
||||
*/
|
||||
#define START_TRANSACTION(session,connection) \
|
||||
{ /* start new scope, will be ended by COMMIT_TRANSACTION() */\
|
||||
unsigned int transaction_retries = 0; \
|
||||
enum GNUNET_DB_QueryStatus transaction_commit_result; \
|
||||
transaction_start_label: /* we will use goto for retries */ \
|
||||
if (GNUNET_OK != \
|
||||
TEH_plugin->start (TEH_plugin->cls, \
|
||||
session)) \
|
||||
{ \
|
||||
GNUNET_break (0); \
|
||||
return TEH_RESPONSE_reply_internal_db_error (connection, \
|
||||
TALER_EC_DB_START_FAILED); \
|
||||
}
|
||||
|
||||
/**
|
||||
* Code to conclude a transaction, dual to #START_TRANSACTION(). Note
|
||||
* that this code may call "return" internally, so it must be called
|
||||
* within a function where any cleanup will be done by the caller.
|
||||
* Furthermore, the function's return value must match that of a
|
||||
* #TEH_RESPONSE_reply_internal_db_error() status code.
|
||||
*
|
||||
* @param session session handle
|
||||
* @param connection connection handle
|
||||
*/
|
||||
#define COMMIT_TRANSACTION(session,connection) \
|
||||
transaction_commit_result = \
|
||||
TEH_plugin->commit (TEH_plugin->cls, \
|
||||
session); \
|
||||
if (GNUNET_DB_STATUS_HARD_ERROR == transaction_commit_result) \
|
||||
{ \
|
||||
TALER_LOG_WARNING ("Transaction commit failed in %s\n", __FUNCTION__); \
|
||||
return TEH_RESPONSE_reply_commit_error (connection, \
|
||||
TALER_EC_DB_COMMIT_FAILED_HARD); \
|
||||
} \
|
||||
if (GNUNET_DB_STATUS_SOFT_ERROR == transaction_commit_result) \
|
||||
{ \
|
||||
TALER_LOG_WARNING ("Transaction commit failed in %s\n", __FUNCTION__); \
|
||||
if (transaction_retries++ <= MAX_TRANSACTION_COMMIT_RETRIES) \
|
||||
goto transaction_start_label; \
|
||||
TALER_LOG_WARNING ("Transaction commit failed %u times in %s\n", \
|
||||
transaction_retries, \
|
||||
__FUNCTION__); \
|
||||
return TEH_RESPONSE_reply_commit_error (connection, \
|
||||
TALER_EC_DB_COMMIT_FAILED_ON_RETRY); \
|
||||
} \
|
||||
} /* end of scope opened by BEGIN_TRANSACTION */
|
||||
|
||||
|
||||
/**
|
||||
* Code to include to retry a transaction, must only be used in between
|
||||
* #START_TRANSACTION and #COMMIT_TRANSACTION.
|
||||
*
|
||||
* @param session session handle
|
||||
* @param connection connection handle
|
||||
*/
|
||||
#define RETRY_TRANSACTION(session,connection) \
|
||||
do { \
|
||||
TEH_plugin->rollback (TEH_plugin->cls, \
|
||||
session); \
|
||||
if (transaction_retries++ <= MAX_TRANSACTION_COMMIT_RETRIES) \
|
||||
goto transaction_start_label; \
|
||||
TALER_LOG_WARNING ("Transaction commit failed %u times in %s\n", \
|
||||
transaction_retries, \
|
||||
__FUNCTION__); \
|
||||
return TEH_RESPONSE_reply_commit_error (connection, \
|
||||
TALER_EC_DB_COMMIT_FAILED_ON_RETRY); \
|
||||
} while (0)
|
||||
|
||||
|
||||
/**
|
||||
* Run a database transaction for @a connection.
|
||||
@ -279,870 +200,4 @@ TEH_DB_calculate_transaction_list_totals (struct TALER_EXCHANGEDB_TransactionLis
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parse coin melt requests from a JSON object and write them to
|
||||
* the database.
|
||||
*
|
||||
* @param connection the connection to send errors to
|
||||
* @param session the database connection
|
||||
* @param key_state the exchange's key state
|
||||
* @param session_hash hash identifying the refresh session
|
||||
* @param coin_details details about the coin being melted
|
||||
* @param[out] meltp on success, set to melt details
|
||||
* @return #GNUNET_OK on success,
|
||||
* #GNUNET_NO if an error message was generated,
|
||||
* #GNUNET_SYSERR on internal errors (no response generated)
|
||||
*/
|
||||
static int
|
||||
refresh_check_melt (struct MHD_Connection *connection,
|
||||
struct TALER_EXCHANGEDB_Session *session,
|
||||
const struct TEH_KS_StateHandle *key_state,
|
||||
const struct GNUNET_HashCode *session_hash,
|
||||
const struct TEH_DB_MeltDetails *coin_details,
|
||||
struct TALER_EXCHANGEDB_RefreshMelt *meltp)
|
||||
{
|
||||
struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dk;
|
||||
struct TALER_EXCHANGEDB_DenominationKeyInformationP *dki;
|
||||
struct TALER_EXCHANGEDB_TransactionList *tl;
|
||||
struct TALER_Amount coin_value;
|
||||
struct TALER_Amount coin_residual;
|
||||
struct TALER_Amount spent;
|
||||
int res;
|
||||
enum GNUNET_DB_QueryStatus qs;
|
||||
|
||||
dk = TEH_KS_denomination_key_lookup (key_state,
|
||||
&coin_details->coin_info.denom_pub,
|
||||
TEH_KS_DKU_DEPOSIT);
|
||||
if (NULL == dk)
|
||||
return (MHD_YES ==
|
||||
TEH_RESPONSE_reply_internal_error (connection,
|
||||
TALER_EC_REFRESH_MELT_DB_DENOMINATION_KEY_NOT_FOUND,
|
||||
"denomination key no longer available while executing transaction"))
|
||||
? GNUNET_NO : GNUNET_SYSERR;
|
||||
dki = &dk->issue;
|
||||
TALER_amount_ntoh (&coin_value,
|
||||
&dki->properties.value);
|
||||
/* fee for THIS transaction; the melt amount includes the fee! */
|
||||
spent = coin_details->melt_amount_with_fee;
|
||||
/* add historic transaction costs of this coin */
|
||||
qs = TEH_plugin->get_coin_transactions (TEH_plugin->cls,
|
||||
session,
|
||||
&coin_details->coin_info.coin_pub,
|
||||
&tl);
|
||||
(void) qs; /* FIXME #5010 */
|
||||
if (GNUNET_OK !=
|
||||
TEH_DB_calculate_transaction_list_totals (tl,
|
||||
&spent,
|
||||
&spent))
|
||||
{
|
||||
GNUNET_break (0);
|
||||
TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
|
||||
tl);
|
||||
return (MHD_YES ==
|
||||
TEH_RESPONSE_reply_internal_db_error (connection,
|
||||
TALER_EC_REFRESH_MELT_COIN_HISTORY_COMPUTATION_FAILED))
|
||||
? GNUNET_NO : GNUNET_SYSERR;
|
||||
}
|
||||
/* Refuse to refresh when the coin's value is insufficient
|
||||
for the cost of all transactions. */
|
||||
if (TALER_amount_cmp (&coin_value,
|
||||
&spent) < 0)
|
||||
{
|
||||
GNUNET_assert (GNUNET_SYSERR !=
|
||||
TALER_amount_subtract (&coin_residual,
|
||||
&spent,
|
||||
&coin_details->melt_amount_with_fee));
|
||||
res = (MHD_YES ==
|
||||
TEH_RESPONSE_reply_refresh_melt_insufficient_funds (connection,
|
||||
&coin_details->coin_info.coin_pub,
|
||||
coin_value,
|
||||
tl,
|
||||
coin_details->melt_amount_with_fee,
|
||||
coin_residual))
|
||||
? GNUNET_NO : GNUNET_SYSERR;
|
||||
TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
|
||||
tl);
|
||||
return res;
|
||||
}
|
||||
TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
|
||||
tl);
|
||||
|
||||
meltp->coin = coin_details->coin_info;
|
||||
meltp->coin_sig = coin_details->melt_sig;
|
||||
meltp->session_hash = *session_hash;
|
||||
meltp->amount_with_fee = coin_details->melt_amount_with_fee;
|
||||
meltp->melt_fee = coin_details->melt_fee;
|
||||
return GNUNET_OK;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Execute a "/refresh/melt". We have been given a list of valid
|
||||
* coins and a request to melt them into the given
|
||||
* @a refresh_session_pub. Check that the coins all have the
|
||||
* required value left and if so, store that they have been
|
||||
* melted and confirm the melting operation to the client.
|
||||
*
|
||||
* @param connection the MHD connection to handle
|
||||
* @param session_hash hash code of the session the coins are melted into
|
||||
* @param num_new_denoms number of entries in @a denom_pubs, size of y-dimension of @a commit_coin array
|
||||
* @param denom_pubs public keys of the coins we want to withdraw in the end
|
||||
* @param coin_melt_detail signature and (residual) value of the respective coin should be melted
|
||||
* @param commit_coin 2d array of coin commitments (what the exchange is to sign
|
||||
* once the "/refres/reveal" of cut and choose is done),
|
||||
* x-dimension must be #TALER_CNC_KAPPA
|
||||
* @param transfer_pubs array of transfer public keys (what the exchange is
|
||||
* to return via "/refresh/link" to enable linkage in the
|
||||
* future) of length #TALER_CNC_KAPPA
|
||||
* @return MHD result code
|
||||
*/
|
||||
int
|
||||
TEH_DB_execute_refresh_melt (struct MHD_Connection *connection,
|
||||
const struct GNUNET_HashCode *session_hash,
|
||||
unsigned int num_new_denoms,
|
||||
const struct TALER_DenominationPublicKey *denom_pubs,
|
||||
const struct TEH_DB_MeltDetails *coin_melt_detail,
|
||||
struct TALER_EXCHANGEDB_RefreshCommitCoin *const* commit_coin,
|
||||
const struct TALER_TransferPublicKeyP *transfer_pubs)
|
||||
{
|
||||
struct TEH_KS_StateHandle *key_state;
|
||||
struct TALER_EXCHANGEDB_RefreshSession refresh_session;
|
||||
struct TALER_EXCHANGEDB_Session *session;
|
||||
int res;
|
||||
|
||||
if (NULL == (session = TEH_plugin->get_session (TEH_plugin->cls)))
|
||||
{
|
||||
GNUNET_break (0);
|
||||
return TEH_RESPONSE_reply_internal_db_error (connection,
|
||||
TALER_EC_DB_SETUP_FAILED);
|
||||
}
|
||||
START_TRANSACTION (session, connection);
|
||||
res = TEH_plugin->get_refresh_session (TEH_plugin->cls,
|
||||
session,
|
||||
session_hash,
|
||||
&refresh_session);
|
||||
if (GNUNET_YES == res)
|
||||
{
|
||||
TEH_plugin->rollback (TEH_plugin->cls,
|
||||
session);
|
||||
res = TEH_RESPONSE_reply_refresh_melt_success (connection,
|
||||
session_hash,
|
||||
refresh_session.noreveal_index);
|
||||
return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
|
||||
}
|
||||
if (GNUNET_SYSERR == res)
|
||||
{
|
||||
TEH_plugin->rollback (TEH_plugin->cls,
|
||||
session);
|
||||
return TEH_RESPONSE_reply_internal_db_error (connection,
|
||||
TALER_EC_REFRESH_MELT_DB_FETCH_ERROR);
|
||||
}
|
||||
|
||||
/* store 'global' session data */
|
||||
refresh_session.num_newcoins = num_new_denoms;
|
||||
refresh_session.noreveal_index
|
||||
= GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_STRONG,
|
||||
TALER_CNC_KAPPA);
|
||||
key_state = TEH_KS_acquire ();
|
||||
if (GNUNET_OK !=
|
||||
(res = refresh_check_melt (connection,
|
||||
session,
|
||||
key_state,
|
||||
session_hash,
|
||||
coin_melt_detail,
|
||||
&refresh_session.melt)))
|
||||
{
|
||||
TEH_KS_release (key_state);
|
||||
TEH_plugin->rollback (TEH_plugin->cls,
|
||||
session);
|
||||
return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
|
||||
}
|
||||
TEH_KS_release (key_state);
|
||||
|
||||
if (GNUNET_OK !=
|
||||
(res = TEH_plugin->create_refresh_session (TEH_plugin->cls,
|
||||
session,
|
||||
session_hash,
|
||||
&refresh_session)))
|
||||
{
|
||||
TEH_plugin->rollback (TEH_plugin->cls,
|
||||
session);
|
||||
return TEH_RESPONSE_reply_internal_db_error (connection,
|
||||
TALER_EC_REFRESH_MELT_DB_STORE_SESSION_ERROR);
|
||||
}
|
||||
|
||||
/* store requested new denominations */
|
||||
if (GNUNET_OK !=
|
||||
TEH_plugin->insert_refresh_order (TEH_plugin->cls,
|
||||
session,
|
||||
session_hash,
|
||||
num_new_denoms,
|
||||
denom_pubs))
|
||||
{
|
||||
TEH_plugin->rollback (TEH_plugin->cls,
|
||||
session);
|
||||
return TEH_RESPONSE_reply_internal_db_error (connection,
|
||||
TALER_EC_REFRESH_MELT_DB_STORE_ORDER_ERROR);
|
||||
}
|
||||
|
||||
if (GNUNET_OK !=
|
||||
TEH_plugin->insert_refresh_commit_coins (TEH_plugin->cls,
|
||||
session,
|
||||
session_hash,
|
||||
num_new_denoms,
|
||||
commit_coin[refresh_session.noreveal_index]))
|
||||
{
|
||||
TEH_plugin->rollback (TEH_plugin->cls,
|
||||
session);
|
||||
return TEH_RESPONSE_reply_internal_db_error (connection,
|
||||
TALER_EC_REFRESH_MELT_DB_STORE_ORDER_ERROR);
|
||||
}
|
||||
if (GNUNET_OK !=
|
||||
TEH_plugin->insert_refresh_transfer_public_key (TEH_plugin->cls,
|
||||
session,
|
||||
session_hash,
|
||||
&transfer_pubs[refresh_session.noreveal_index]))
|
||||
{
|
||||
TEH_plugin->rollback (TEH_plugin->cls,
|
||||
session);
|
||||
return TEH_RESPONSE_reply_internal_db_error (connection,
|
||||
TALER_EC_REFRESH_MELT_DB_STORE_TRANSFER_ERROR);
|
||||
}
|
||||
|
||||
COMMIT_TRANSACTION (session, connection);
|
||||
return TEH_RESPONSE_reply_refresh_melt_success (connection,
|
||||
session_hash,
|
||||
refresh_session.noreveal_index);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if the given @a transfer_privs correspond to an honest
|
||||
* commitment for the given session.
|
||||
* Checks that the transfer private keys match their commitments.
|
||||
* Then derives the shared secret for each #TALER_CNC_KAPPA, and check that they match.
|
||||
*
|
||||
* @param connection the MHD connection to handle
|
||||
* @param session database connection to use
|
||||
* @param session_hash hash of session to query
|
||||
* @param off commitment offset to check
|
||||
* @param transfer_priv private transfer key
|
||||
* @param melt information about the melted coin
|
||||
* @param num_newcoins number of newcoins being generated
|
||||
* @param denom_pubs array of @a num_newcoins keys for the new coins
|
||||
* @param hash_context hash context to update by hashing in the data
|
||||
* from this offset
|
||||
* @return #GNUNET_OK if the committment was honest,
|
||||
* #GNUNET_NO if there was a problem and we generated an error message
|
||||
* #GNUNET_SYSERR if we could not even generate an error message
|
||||
*/
|
||||
static int
|
||||
check_commitment (struct MHD_Connection *connection,
|
||||
struct TALER_EXCHANGEDB_Session *session,
|
||||
const struct GNUNET_HashCode *session_hash,
|
||||
unsigned int off,
|
||||
const struct TALER_TransferPrivateKeyP *transfer_priv,
|
||||
const struct TALER_EXCHANGEDB_RefreshMelt *melt,
|
||||
unsigned int num_newcoins,
|
||||
const struct TALER_DenominationPublicKey *denom_pubs,
|
||||
struct GNUNET_HashContext *hash_context)
|
||||
{
|
||||
struct TALER_TransferSecretP transfer_secret;
|
||||
unsigned int j;
|
||||
|
||||
TALER_link_reveal_transfer_secret (transfer_priv,
|
||||
&melt->coin.coin_pub,
|
||||
&transfer_secret);
|
||||
|
||||
/* Check that the commitments for all new coins were correct */
|
||||
for (j = 0; j < num_newcoins; j++)
|
||||
{
|
||||
struct TALER_FreshCoinP fc;
|
||||
struct TALER_CoinSpendPublicKeyP coin_pub;
|
||||
struct GNUNET_HashCode h_msg;
|
||||
char *buf;
|
||||
size_t buf_len;
|
||||
|
||||
TALER_setup_fresh_coin (&transfer_secret,
|
||||
j,
|
||||
&fc);
|
||||
GNUNET_CRYPTO_eddsa_key_get_public (&fc.coin_priv.eddsa_priv,
|
||||
&coin_pub.eddsa_pub);
|
||||
GNUNET_CRYPTO_hash (&coin_pub,
|
||||
sizeof (struct TALER_CoinSpendPublicKeyP),
|
||||
&h_msg);
|
||||
if (GNUNET_YES !=
|
||||
GNUNET_CRYPTO_rsa_blind (&h_msg,
|
||||
&fc.blinding_key.bks,
|
||||
denom_pubs[j].rsa_public_key,
|
||||
&buf,
|
||||
&buf_len))
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"Blind failed (bad denomination key!?)\n");
|
||||
return (MHD_YES ==
|
||||
TEH_RESPONSE_reply_internal_error (connection,
|
||||
TALER_EC_REFRESH_REVEAL_BLINDING_ERROR,
|
||||
"Blinding error"))
|
||||
? GNUNET_NO : GNUNET_SYSERR;
|
||||
}
|
||||
GNUNET_CRYPTO_hash_context_read (hash_context,
|
||||
buf,
|
||||
buf_len);
|
||||
GNUNET_free (buf);
|
||||
}
|
||||
return GNUNET_OK;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Exchange a coin as part of a refresh operation. Obtains the
|
||||
* envelope from the database and performs the signing operation.
|
||||
*
|
||||
* @param connection the MHD connection to handle
|
||||
* @param session database connection to use
|
||||
* @param session_hash hash of session to query
|
||||
* @param key_state key state to lookup denomination pubs
|
||||
* @param denom_pub denomination key for the coin to create
|
||||
* @param commit_coin the coin that was committed
|
||||
* @param coin_off number of the coin
|
||||
* @return NULL on error, otherwise signature over the coin
|
||||
*/
|
||||
static struct TALER_DenominationSignature
|
||||
refresh_exchange_coin (struct MHD_Connection *connection,
|
||||
struct TALER_EXCHANGEDB_Session *session,
|
||||
const struct GNUNET_HashCode *session_hash,
|
||||
struct TEH_KS_StateHandle *key_state,
|
||||
const struct TALER_DenominationPublicKey *denom_pub,
|
||||
const struct TALER_EXCHANGEDB_RefreshCommitCoin *commit_coin,
|
||||
unsigned int coin_off)
|
||||
{
|
||||
struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki;
|
||||
struct TALER_DenominationSignature ev_sig;
|
||||
|
||||
dki = TEH_KS_denomination_key_lookup (key_state,
|
||||
denom_pub,
|
||||
TEH_KS_DKU_WITHDRAW);
|
||||
if (NULL == dki)
|
||||
{
|
||||
GNUNET_break (0);
|
||||
ev_sig.rsa_signature = NULL;
|
||||
return ev_sig;
|
||||
}
|
||||
if (GNUNET_OK ==
|
||||
TEH_plugin->get_refresh_out (TEH_plugin->cls,
|
||||
session,
|
||||
session_hash,
|
||||
coin_off,
|
||||
&ev_sig))
|
||||
{
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||
"Returning cached reply for /refresh/reveal signature\n");
|
||||
return ev_sig;
|
||||
}
|
||||
|
||||
ev_sig.rsa_signature
|
||||
= GNUNET_CRYPTO_rsa_sign_blinded (dki->denom_priv.rsa_private_key,
|
||||
commit_coin->coin_ev,
|
||||
commit_coin->coin_ev_size);
|
||||
if (NULL == ev_sig.rsa_signature)
|
||||
{
|
||||
GNUNET_break (0);
|
||||
return ev_sig;
|
||||
}
|
||||
if (GNUNET_SYSERR ==
|
||||
TEH_plugin->insert_refresh_out (TEH_plugin->cls,
|
||||
session,
|
||||
session_hash,
|
||||
coin_off,
|
||||
&ev_sig))
|
||||
{
|
||||
GNUNET_break (0);
|
||||
GNUNET_CRYPTO_rsa_signature_free (ev_sig.rsa_signature);
|
||||
ev_sig.rsa_signature = NULL;
|
||||
}
|
||||
|
||||
return ev_sig;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The client request was well-formed, now execute the DB transaction
|
||||
* of a "/refresh/reveal" operation. We use the @a ev_sigs and
|
||||
* @a commit_coins to clean up resources after this function returns
|
||||
* as we might experience retries of the database transaction.
|
||||
*
|
||||
* @param connection the MHD connection to handle
|
||||
* @param session database session
|
||||
* @param session_hash hash identifying the refresh session
|
||||
* @param refresh_session information about the refresh operation we are doing
|
||||
* @param denom_pubs array of "num_newcoins" denomination keys for the new coins
|
||||
* @param[out] ev_sigs where to store generated signatures for the new coins,
|
||||
* array of length "num_newcoins", memory released by the
|
||||
* caller
|
||||
* @param[out] commit_coins array of length "num_newcoins" to be used for
|
||||
* information about the new coins from the commitment.
|
||||
* @return MHD result code
|
||||
*/
|
||||
static int
|
||||
execute_refresh_reveal_transaction (struct MHD_Connection *connection,
|
||||
struct TALER_EXCHANGEDB_Session *session,
|
||||
const struct GNUNET_HashCode *session_hash,
|
||||
const struct TALER_EXCHANGEDB_RefreshSession *refresh_session,
|
||||
const struct TALER_DenominationPublicKey *denom_pubs,
|
||||
struct TALER_DenominationSignature *ev_sigs,
|
||||
struct TALER_EXCHANGEDB_RefreshCommitCoin *commit_coins)
|
||||
{
|
||||
unsigned int j;
|
||||
struct TEH_KS_StateHandle *key_state;
|
||||
int ret;
|
||||
|
||||
START_TRANSACTION (session, connection);
|
||||
key_state = TEH_KS_acquire ();
|
||||
for (j=0;j<refresh_session->num_newcoins;j++)
|
||||
{
|
||||
if (NULL == ev_sigs[j].rsa_signature) /* could be non-NULL during retries */
|
||||
ev_sigs[j] = refresh_exchange_coin (connection,
|
||||
session,
|
||||
session_hash,
|
||||
key_state,
|
||||
&denom_pubs[j],
|
||||
&commit_coins[j],
|
||||
j);
|
||||
if (NULL == ev_sigs[j].rsa_signature)
|
||||
{
|
||||
TEH_plugin->rollback (TEH_plugin->cls,
|
||||
session);
|
||||
ret = TEH_RESPONSE_reply_internal_db_error (connection,
|
||||
TALER_EC_REFRESH_REVEAL_SIGNING_ERROR);
|
||||
goto cleanup;
|
||||
}
|
||||
}
|
||||
COMMIT_TRANSACTION (session, connection);
|
||||
ret = TEH_RESPONSE_reply_refresh_reveal_success (connection,
|
||||
refresh_session->num_newcoins,
|
||||
ev_sigs);
|
||||
cleanup:
|
||||
TEH_KS_release (key_state);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Execute a "/refresh/reveal". The client is revealing to us the
|
||||
* transfer keys for @a #TALER_CNC_KAPPA-1 sets of coins. Verify that the
|
||||
* revealed transfer keys would allow linkage to the blinded coins,
|
||||
* and if so, return the signed coins for corresponding to the set of
|
||||
* coins that was not chosen.
|
||||
*
|
||||
* @param connection the MHD connection to handle
|
||||
* @param session_hash hash identifying the refresh session
|
||||
* @param transfer_privs array with the revealed transfer keys,
|
||||
* length must be #TALER_CNC_KAPPA - 1
|
||||
* @return MHD result code
|
||||
*/
|
||||
int
|
||||
TEH_DB_execute_refresh_reveal (struct MHD_Connection *connection,
|
||||
const struct GNUNET_HashCode *session_hash,
|
||||
struct TALER_TransferPrivateKeyP *transfer_privs)
|
||||
{
|
||||
int res;
|
||||
struct TALER_EXCHANGEDB_Session *session;
|
||||
struct TALER_EXCHANGEDB_RefreshSession refresh_session;
|
||||
struct TALER_DenominationPublicKey *denom_pubs;
|
||||
struct TALER_DenominationSignature *ev_sigs;
|
||||
struct TALER_EXCHANGEDB_RefreshCommitCoin *commit_coins;
|
||||
unsigned int i;
|
||||
unsigned int j;
|
||||
unsigned int off;
|
||||
struct GNUNET_HashContext *hash_context;
|
||||
struct GNUNET_HashCode sh_check;
|
||||
int ret;
|
||||
struct TALER_TransferPublicKeyP gamma_tp;
|
||||
|
||||
if (NULL == (session = TEH_plugin->get_session (TEH_plugin->cls)))
|
||||
{
|
||||
GNUNET_break (0);
|
||||
return TEH_RESPONSE_reply_internal_db_error (connection,
|
||||
TALER_EC_DB_SETUP_FAILED);
|
||||
}
|
||||
|
||||
res = TEH_plugin->get_refresh_session (TEH_plugin->cls,
|
||||
session,
|
||||
session_hash,
|
||||
&refresh_session);
|
||||
if (GNUNET_NO == res)
|
||||
return TEH_RESPONSE_reply_arg_invalid (connection,
|
||||
TALER_EC_REFRESH_REVEAL_SESSION_UNKNOWN,
|
||||
"session_hash");
|
||||
if ( (GNUNET_SYSERR == res) ||
|
||||
(refresh_session.noreveal_index >= TALER_CNC_KAPPA) )
|
||||
return TEH_RESPONSE_reply_internal_db_error (connection,
|
||||
TALER_EC_REFRESH_REVEAL_DB_FETCH_SESSION_ERROR);
|
||||
denom_pubs = GNUNET_new_array (refresh_session.num_newcoins,
|
||||
struct TALER_DenominationPublicKey);
|
||||
if (GNUNET_OK !=
|
||||
TEH_plugin->get_refresh_order (TEH_plugin->cls,
|
||||
session,
|
||||
session_hash,
|
||||
refresh_session.num_newcoins,
|
||||
denom_pubs))
|
||||
{
|
||||
GNUNET_break (0);
|
||||
GNUNET_free (denom_pubs);
|
||||
GNUNET_CRYPTO_rsa_signature_free (refresh_session.melt.coin.denom_sig.rsa_signature);
|
||||
GNUNET_CRYPTO_rsa_public_key_free (refresh_session.melt.coin.denom_pub.rsa_public_key);
|
||||
return (MHD_YES == TEH_RESPONSE_reply_internal_db_error (connection,
|
||||
TALER_EC_REFRESH_REVEAL_DB_FETCH_ORDER_ERROR))
|
||||
? GNUNET_NO : GNUNET_SYSERR;
|
||||
}
|
||||
|
||||
hash_context = GNUNET_CRYPTO_hash_context_start ();
|
||||
/* first, iterate over transfer public keys for hash_context */
|
||||
off = 0;
|
||||
for (i=0;i<TALER_CNC_KAPPA;i++)
|
||||
{
|
||||
if (i == refresh_session.noreveal_index)
|
||||
{
|
||||
off = 1;
|
||||
/* obtain gamma_tp from db */
|
||||
if (GNUNET_OK !=
|
||||
TEH_plugin->get_refresh_transfer_public_key (TEH_plugin->cls,
|
||||
session,
|
||||
session_hash,
|
||||
&gamma_tp))
|
||||
{
|
||||
GNUNET_break (0);
|
||||
GNUNET_free (denom_pubs);
|
||||
GNUNET_CRYPTO_rsa_signature_free (refresh_session.melt.coin.denom_sig.rsa_signature);
|
||||
GNUNET_CRYPTO_rsa_public_key_free (refresh_session.melt.coin.denom_pub.rsa_public_key);
|
||||
GNUNET_CRYPTO_hash_context_abort (hash_context);
|
||||
return (MHD_YES == TEH_RESPONSE_reply_internal_db_error (connection,
|
||||
TALER_EC_REFRESH_REVEAL_DB_FETCH_TRANSFER_ERROR))
|
||||
? GNUNET_NO : GNUNET_SYSERR;
|
||||
}
|
||||
GNUNET_CRYPTO_hash_context_read (hash_context,
|
||||
&gamma_tp,
|
||||
sizeof (struct TALER_TransferPublicKeyP));
|
||||
}
|
||||
else
|
||||
{
|
||||
/* compute tp from private key */
|
||||
struct TALER_TransferPublicKeyP tp;
|
||||
|
||||
GNUNET_CRYPTO_ecdhe_key_get_public (&transfer_privs[i - off].ecdhe_priv,
|
||||
&tp.ecdhe_pub);
|
||||
GNUNET_CRYPTO_hash_context_read (hash_context,
|
||||
&tp,
|
||||
sizeof (struct TALER_TransferPublicKeyP));
|
||||
}
|
||||
}
|
||||
|
||||
/* next, add all of the hashes from the denomination keys to the
|
||||
hash_context */
|
||||
{
|
||||
struct TALER_DenominationPublicKey denom_pubs[refresh_session.num_newcoins];
|
||||
|
||||
if (GNUNET_OK !=
|
||||
TEH_plugin->get_refresh_order (TEH_plugin->cls,
|
||||
session,
|
||||
session_hash,
|
||||
refresh_session.num_newcoins,
|
||||
denom_pubs))
|
||||
{
|
||||
GNUNET_break (0);
|
||||
GNUNET_free (denom_pubs);
|
||||
GNUNET_CRYPTO_rsa_signature_free (refresh_session.melt.coin.denom_sig.rsa_signature);
|
||||
GNUNET_CRYPTO_rsa_public_key_free (refresh_session.melt.coin.denom_pub.rsa_public_key);
|
||||
GNUNET_CRYPTO_hash_context_abort (hash_context);
|
||||
return (MHD_YES == TEH_RESPONSE_reply_internal_db_error (connection,
|
||||
TALER_EC_REFRESH_REVEAL_DB_FETCH_ORDER_ERROR))
|
||||
? GNUNET_NO : GNUNET_SYSERR;
|
||||
}
|
||||
for (i=0;i<refresh_session.num_newcoins;i++)
|
||||
{
|
||||
char *buf;
|
||||
size_t buf_size;
|
||||
|
||||
buf_size = GNUNET_CRYPTO_rsa_public_key_encode (denom_pubs[i].rsa_public_key,
|
||||
&buf);
|
||||
GNUNET_CRYPTO_hash_context_read (hash_context,
|
||||
buf,
|
||||
buf_size);
|
||||
GNUNET_free (buf);
|
||||
GNUNET_CRYPTO_rsa_public_key_free (denom_pubs[i].rsa_public_key);
|
||||
}
|
||||
}
|
||||
|
||||
/* next, add public key of coin and amount being refreshed */
|
||||
{
|
||||
struct TALER_AmountNBO melt_amountn;
|
||||
|
||||
GNUNET_CRYPTO_hash_context_read (hash_context,
|
||||
&refresh_session.melt.coin.coin_pub,
|
||||
sizeof (struct TALER_CoinSpendPublicKeyP));
|
||||
TALER_amount_hton (&melt_amountn,
|
||||
&refresh_session.melt.amount_with_fee);
|
||||
GNUNET_CRYPTO_hash_context_read (hash_context,
|
||||
&melt_amountn,
|
||||
sizeof (struct TALER_AmountNBO));
|
||||
}
|
||||
|
||||
commit_coins = GNUNET_new_array (refresh_session.num_newcoins,
|
||||
struct TALER_EXCHANGEDB_RefreshCommitCoin);
|
||||
off = 0;
|
||||
for (i=0;i<TALER_CNC_KAPPA;i++)
|
||||
{
|
||||
if (i == refresh_session.noreveal_index)
|
||||
{
|
||||
off = 1;
|
||||
/* obtain commit_coins for the selected gamma value from DB */
|
||||
if (GNUNET_OK !=
|
||||
TEH_plugin->get_refresh_commit_coins (TEH_plugin->cls,
|
||||
session,
|
||||
session_hash,
|
||||
refresh_session.num_newcoins,
|
||||
commit_coins))
|
||||
{
|
||||
GNUNET_break (0);
|
||||
GNUNET_free (denom_pubs);
|
||||
GNUNET_CRYPTO_rsa_signature_free (refresh_session.melt.coin.denom_sig.rsa_signature);
|
||||
GNUNET_CRYPTO_rsa_public_key_free (refresh_session.melt.coin.denom_pub.rsa_public_key);
|
||||
GNUNET_CRYPTO_hash_context_abort (hash_context);
|
||||
return TEH_RESPONSE_reply_internal_db_error (connection,
|
||||
TALER_EC_REFRESH_REVEAL_DB_FETCH_COMMIT_ERROR);
|
||||
}
|
||||
/* add envelopes to hash_context */
|
||||
for (j=0;j<refresh_session.num_newcoins;j++)
|
||||
{
|
||||
GNUNET_CRYPTO_hash_context_read (hash_context,
|
||||
commit_coins[j].coin_ev,
|
||||
commit_coins[j].coin_ev_size);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (GNUNET_OK !=
|
||||
(res = check_commitment (connection,
|
||||
session,
|
||||
session_hash,
|
||||
i,
|
||||
&transfer_privs[i - off],
|
||||
&refresh_session.melt,
|
||||
refresh_session.num_newcoins,
|
||||
denom_pubs,
|
||||
hash_context)))
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
for (j=0;j<refresh_session.num_newcoins;j++)
|
||||
{
|
||||
GNUNET_CRYPTO_rsa_public_key_free (denom_pubs[j].rsa_public_key);
|
||||
GNUNET_free (commit_coins[j].coin_ev);
|
||||
}
|
||||
GNUNET_free (commit_coins);
|
||||
GNUNET_free (denom_pubs);
|
||||
GNUNET_CRYPTO_rsa_signature_free (refresh_session.melt.coin.denom_sig.rsa_signature);
|
||||
GNUNET_CRYPTO_rsa_public_key_free (refresh_session.melt.coin.denom_pub.rsa_public_key);
|
||||
GNUNET_CRYPTO_hash_context_abort (hash_context);
|
||||
return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check session hash matches */
|
||||
GNUNET_CRYPTO_hash_context_finish (hash_context,
|
||||
&sh_check);
|
||||
if (0 != memcmp (&sh_check,
|
||||
session_hash,
|
||||
sizeof (struct GNUNET_HashCode)))
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
ret = TEH_RESPONSE_reply_refresh_reveal_missmatch (connection,
|
||||
&refresh_session,
|
||||
commit_coins,
|
||||
denom_pubs,
|
||||
&gamma_tp);
|
||||
for (j=0;j<refresh_session.num_newcoins;j++)
|
||||
{
|
||||
GNUNET_free (commit_coins[j].coin_ev);
|
||||
GNUNET_CRYPTO_rsa_public_key_free (denom_pubs[j].rsa_public_key);
|
||||
}
|
||||
GNUNET_free (commit_coins);
|
||||
GNUNET_free (denom_pubs);
|
||||
GNUNET_CRYPTO_rsa_signature_free (refresh_session.melt.coin.denom_sig.rsa_signature);
|
||||
GNUNET_CRYPTO_rsa_public_key_free (refresh_session.melt.coin.denom_pub.rsa_public_key);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Client request OK, start transaction */
|
||||
ev_sigs = GNUNET_new_array (refresh_session.num_newcoins,
|
||||
struct TALER_DenominationSignature);
|
||||
|
||||
/* FIXME: might need to store revealed transfer private keys for
|
||||
the auditor for later; should pass them as arguments here! #4792*/
|
||||
res = execute_refresh_reveal_transaction (connection,
|
||||
session,
|
||||
session_hash,
|
||||
&refresh_session,
|
||||
denom_pubs,
|
||||
ev_sigs,
|
||||
commit_coins);
|
||||
for (i=0;i<refresh_session.num_newcoins;i++)
|
||||
{
|
||||
if (NULL != ev_sigs[i].rsa_signature)
|
||||
GNUNET_CRYPTO_rsa_signature_free (ev_sigs[i].rsa_signature);
|
||||
GNUNET_free (commit_coins[i].coin_ev);
|
||||
}
|
||||
for (j=0;j<refresh_session.num_newcoins;j++)
|
||||
if (NULL != denom_pubs[j].rsa_public_key)
|
||||
GNUNET_CRYPTO_rsa_public_key_free (denom_pubs[j].rsa_public_key);
|
||||
GNUNET_CRYPTO_rsa_signature_free (refresh_session.melt.coin.denom_sig.rsa_signature);
|
||||
GNUNET_CRYPTO_rsa_public_key_free (refresh_session.melt.coin.denom_pub.rsa_public_key);
|
||||
GNUNET_free (ev_sigs);
|
||||
GNUNET_free (denom_pubs);
|
||||
GNUNET_free (commit_coins);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Closure for #handle_transfer_data().
|
||||
*/
|
||||
struct HTD_Context
|
||||
{
|
||||
|
||||
/**
|
||||
* Session link data we collect.
|
||||
*/
|
||||
struct TEH_RESPONSE_LinkSessionInfo *sessions;
|
||||
|
||||
/**
|
||||
* Database session. Nothing to do with @a sessions.
|
||||
*/
|
||||
struct TALER_EXCHANGEDB_Session *session;
|
||||
|
||||
/**
|
||||
* MHD connection, for queueing replies.
|
||||
*/
|
||||
struct MHD_Connection *connection;
|
||||
|
||||
/**
|
||||
* Number of sessions the coin was melted into.
|
||||
*/
|
||||
unsigned int num_sessions;
|
||||
|
||||
/**
|
||||
* How are we expected to proceed. #GNUNET_SYSERR if we
|
||||
* failed to return an error (should return #MHD_NO).
|
||||
* #GNUNET_NO if we succeeded in queueing an MHD error
|
||||
* (should return #MHD_YES from #TEH_execute_refresh_link),
|
||||
* #GNUNET_OK if we should call #TEH_RESPONSE_reply_refresh_link_success().
|
||||
*/
|
||||
int status;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Function called with the session hashes and transfer secret
|
||||
* information for a given coin. Gets the linkage data and
|
||||
* builds the reply for the client.
|
||||
*
|
||||
*
|
||||
* @param cls closure, a `struct HTD_Context`
|
||||
* @param session_hash a session the coin was melted in
|
||||
* @param transfer_pub public transfer key for the session
|
||||
*/
|
||||
static void
|
||||
handle_transfer_data (void *cls,
|
||||
const struct GNUNET_HashCode *session_hash,
|
||||
const struct TALER_TransferPublicKeyP *transfer_pub)
|
||||
{
|
||||
struct HTD_Context *ctx = cls;
|
||||
struct TALER_EXCHANGEDB_LinkDataList *ldl;
|
||||
struct TEH_RESPONSE_LinkSessionInfo *lsi;
|
||||
|
||||
if (GNUNET_OK != ctx->status)
|
||||
return;
|
||||
ldl = TEH_plugin->get_link_data_list (TEH_plugin->cls,
|
||||
ctx->session,
|
||||
session_hash);
|
||||
if (NULL == ldl)
|
||||
{
|
||||
ctx->status = GNUNET_NO;
|
||||
if (MHD_NO ==
|
||||
TEH_RESPONSE_reply_json_pack (ctx->connection,
|
||||
MHD_HTTP_NOT_FOUND,
|
||||
"{s:s}",
|
||||
"error",
|
||||
"link data not found (link)"))
|
||||
ctx->status = GNUNET_SYSERR;
|
||||
return;
|
||||
}
|
||||
GNUNET_array_grow (ctx->sessions,
|
||||
ctx->num_sessions,
|
||||
ctx->num_sessions + 1);
|
||||
lsi = &ctx->sessions[ctx->num_sessions - 1];
|
||||
lsi->transfer_pub = *transfer_pub;
|
||||
lsi->ldl = ldl;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Execute a "/refresh/link". Returns the linkage information that
|
||||
* will allow the owner of a coin to follow the refresh trail to
|
||||
* the refreshed coin.
|
||||
*
|
||||
* @param connection the MHD connection to handle
|
||||
* @param coin_pub public key of the coin to link
|
||||
* @return MHD result code
|
||||
*/
|
||||
int
|
||||
TEH_DB_execute_refresh_link (struct MHD_Connection *connection,
|
||||
const struct TALER_CoinSpendPublicKeyP *coin_pub)
|
||||
{
|
||||
struct HTD_Context ctx;
|
||||
int res;
|
||||
unsigned int i;
|
||||
|
||||
if (NULL == (ctx.session = TEH_plugin->get_session (TEH_plugin->cls)))
|
||||
{
|
||||
GNUNET_break (0);
|
||||
return TEH_RESPONSE_reply_internal_db_error (connection,
|
||||
TALER_EC_DB_SETUP_FAILED);
|
||||
}
|
||||
ctx.connection = connection;
|
||||
ctx.num_sessions = 0;
|
||||
ctx.sessions = NULL;
|
||||
ctx.status = GNUNET_OK;
|
||||
res = TEH_plugin->get_transfer (TEH_plugin->cls,
|
||||
ctx.session,
|
||||
coin_pub,
|
||||
&handle_transfer_data,
|
||||
&ctx);
|
||||
if (GNUNET_SYSERR == ctx.status)
|
||||
{
|
||||
res = MHD_NO;
|
||||
goto cleanup;
|
||||
}
|
||||
if (GNUNET_NO == ctx.status)
|
||||
{
|
||||
res = MHD_YES;
|
||||
goto cleanup;
|
||||
}
|
||||
GNUNET_assert (GNUNET_OK == ctx.status);
|
||||
if (0 == ctx.num_sessions)
|
||||
return TEH_RESPONSE_reply_arg_unknown (connection,
|
||||
TALER_EC_REFRESH_LINK_COIN_UNKNOWN,
|
||||
"coin_pub");
|
||||
res = TEH_RESPONSE_reply_refresh_link_success (connection,
|
||||
ctx.num_sessions,
|
||||
ctx.sessions);
|
||||
cleanup:
|
||||
for (i=0;i<ctx.num_sessions;i++)
|
||||
TEH_plugin->free_link_data_list (TEH_plugin->cls,
|
||||
ctx.sessions[i].ldl);
|
||||
GNUNET_free_non_null (ctx.sessions);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/* end of taler-exchange-httpd_db.c */
|
||||
|
@ -1,6 +1,6 @@
|
||||
/*
|
||||
This file is part of TALER
|
||||
Copyright (C) 2014, 2015 GNUnet e.V.
|
||||
Copyright (C) 2014-2017 GNUnet e.V.
|
||||
|
||||
TALER is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
@ -83,97 +83,5 @@ TEH_DB_calculate_transaction_list_totals (struct TALER_EXCHANGEDB_TransactionLis
|
||||
struct TALER_Amount *ret);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Details about a melt operation of an individual coin.
|
||||
*/
|
||||
struct TEH_DB_MeltDetails
|
||||
{
|
||||
|
||||
/**
|
||||
* Information about the coin being melted.
|
||||
*/
|
||||
struct TALER_CoinPublicInfo coin_info;
|
||||
|
||||
/**
|
||||
* Signature allowing the melt (using
|
||||
* a `struct TALER_EXCHANGEDB_RefreshMeltConfirmSignRequestBody`) to sign over.
|
||||
*/
|
||||
struct TALER_CoinSpendSignatureP melt_sig;
|
||||
|
||||
/**
|
||||
* How much of the coin's value did the client allow to be melted?
|
||||
* This amount includes the fees, so the final amount contributed
|
||||
* to the melt is this value minus the fee for melting the coin.
|
||||
*/
|
||||
struct TALER_Amount melt_amount_with_fee;
|
||||
|
||||
/**
|
||||
* What fee is earned by the exchange? Set delayed during
|
||||
* #verify_coin_public_info().
|
||||
*/
|
||||
struct TALER_Amount melt_fee;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Execute a "/refresh/melt". We have been given a list of valid
|
||||
* coins and a request to melt them into the given
|
||||
* @a refresh_session_pub. Check that the coins all have the
|
||||
* required value left and if so, store that they have been
|
||||
* melted and confirm the melting operation to the client.
|
||||
*
|
||||
* @param connection the MHD connection to handle
|
||||
* @param session_hash hash code of the session the coins are melted into
|
||||
* @param num_new_denoms number of entries in @a denom_pubs, size of y-dimension of @a commit_coin array
|
||||
* @param denom_pubs array of public denomination keys for the refresh (?)
|
||||
* @param coin_melt_detail signatures and (residual) value of and information about the respective coin to be melted
|
||||
* @param commit_coin 2d array of coin commitments (what the exchange is to sign
|
||||
* once the "/refres/reveal" of cut and choose is done)
|
||||
* @param transfer_pubs array of transfer public keys (what the exchange is
|
||||
* to return via "/refresh/link" to enable linkage in the
|
||||
* future) of length #TALER_CNC_KAPPA
|
||||
* @return MHD result code
|
||||
*/
|
||||
int
|
||||
TEH_DB_execute_refresh_melt (struct MHD_Connection *connection,
|
||||
const struct GNUNET_HashCode *session_hash,
|
||||
unsigned int num_new_denoms,
|
||||
const struct TALER_DenominationPublicKey *denom_pubs,
|
||||
const struct TEH_DB_MeltDetails *coin_melt_detail,
|
||||
struct TALER_EXCHANGEDB_RefreshCommitCoin *const* commit_coin,
|
||||
const struct TALER_TransferPublicKeyP *transfer_pubs);
|
||||
|
||||
|
||||
/**
|
||||
* Execute a "/refresh/reveal". The client is revealing to us the
|
||||
* transfer keys for #TALER_CNC_KAPPA-1 sets of coins. Verify that the
|
||||
* revealed transfer keys would allow linkage to the blinded coins,
|
||||
* and if so, return the signed coins for corresponding to the set of
|
||||
* coins that was not chosen.
|
||||
*
|
||||
* @param connection the MHD connection to handle
|
||||
* @param session_hash hash over the refresh session
|
||||
* @param transfer_privs array of length #TALER_CNC_KAPPA-1 with the revealed transfer keys
|
||||
* @return MHD result code
|
||||
*/
|
||||
int
|
||||
TEH_DB_execute_refresh_reveal (struct MHD_Connection *connection,
|
||||
const struct GNUNET_HashCode *session_hash,
|
||||
struct TALER_TransferPrivateKeyP *transfer_privs);
|
||||
|
||||
|
||||
/**
|
||||
* Execute a "/refresh/link". Returns the linkage information that
|
||||
* will allow the owner of a coin to follow the refresh trail to the
|
||||
* refreshed coin.
|
||||
*
|
||||
* @param connection the MHD connection to handle
|
||||
* @param coin_pub public key of the coin to link
|
||||
* @return MHD result code
|
||||
*/
|
||||
int
|
||||
TEH_DB_execute_refresh_link (struct MHD_Connection *connection,
|
||||
const struct TALER_CoinSpendPublicKeyP *coin_pub);
|
||||
|
||||
#endif
|
||||
/* TALER_EXCHANGE_HTTPD_DB_H */
|
||||
|
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
|
||||
Copyright (C) 2014, 2015, 2016 Inria & GNUnet e.V.
|
||||
Copyright (C) 2014-2017 Inria & GNUnet e.V.
|
||||
|
||||
TALER is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU Affero General Public License as published by the Free Software
|
||||
@ -14,8 +14,8 @@
|
||||
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
/**
|
||||
* @file taler-exchange-httpd_refresh.c
|
||||
* @brief Handle /refresh/ requests
|
||||
* @file taler-exchange-httpd_refresh_melt.c
|
||||
* @brief Handle /refresh/melt requests
|
||||
* @author Florian Dold
|
||||
* @author Benedikt Mueller
|
||||
* @author Christian Grothoff
|
||||
@ -26,11 +26,454 @@
|
||||
#include <microhttpd.h>
|
||||
#include "taler-exchange-httpd_parsing.h"
|
||||
#include "taler-exchange-httpd_mhd.h"
|
||||
#include "taler-exchange-httpd_refresh.h"
|
||||
#include "taler-exchange-httpd_refresh_melt.h"
|
||||
#include "taler-exchange-httpd_responses.h"
|
||||
#include "taler-exchange-httpd_keystate.h"
|
||||
|
||||
|
||||
/**
|
||||
* How often should we retry a transaction before giving up
|
||||
* (for transactions resulting in serialization/dead locks only).
|
||||
*/
|
||||
#define MAX_TRANSACTION_COMMIT_RETRIES 3
|
||||
|
||||
/**
|
||||
* Code to begin a transaction, must be inline as we define a block
|
||||
* that ends with #COMMIT_TRANSACTION() within which we perform a number
|
||||
* of retries. Note that this code may call "return" internally, so
|
||||
* it must be called within a function where any cleanup will be done
|
||||
* by the caller. Furthermore, the function's return value must
|
||||
* match that of a #TEH_RESPONSE_reply_internal_db_error() status code.
|
||||
*
|
||||
* @param session session handle
|
||||
* @param connection connection handle
|
||||
*/
|
||||
#define START_TRANSACTION(session,connection) \
|
||||
{ /* start new scope, will be ended by COMMIT_TRANSACTION() */\
|
||||
unsigned int transaction_retries = 0; \
|
||||
enum GNUNET_DB_QueryStatus transaction_commit_result; \
|
||||
transaction_start_label: /* we will use goto for retries */ \
|
||||
if (GNUNET_OK != \
|
||||
TEH_plugin->start (TEH_plugin->cls, \
|
||||
session)) \
|
||||
{ \
|
||||
GNUNET_break (0); \
|
||||
return TEH_RESPONSE_reply_internal_db_error (connection, \
|
||||
TALER_EC_DB_START_FAILED); \
|
||||
}
|
||||
|
||||
/**
|
||||
* Code to conclude a transaction, dual to #START_TRANSACTION(). Note
|
||||
* that this code may call "return" internally, so it must be called
|
||||
* within a function where any cleanup will be done by the caller.
|
||||
* Furthermore, the function's return value must match that of a
|
||||
* #TEH_RESPONSE_reply_internal_db_error() status code.
|
||||
*
|
||||
* @param session session handle
|
||||
* @param connection connection handle
|
||||
*/
|
||||
#define COMMIT_TRANSACTION(session,connection) \
|
||||
transaction_commit_result = \
|
||||
TEH_plugin->commit (TEH_plugin->cls, \
|
||||
session); \
|
||||
if (GNUNET_DB_STATUS_HARD_ERROR == transaction_commit_result) \
|
||||
{ \
|
||||
TALER_LOG_WARNING ("Transaction commit failed in %s\n", __FUNCTION__); \
|
||||
return TEH_RESPONSE_reply_commit_error (connection, \
|
||||
TALER_EC_DB_COMMIT_FAILED_HARD); \
|
||||
} \
|
||||
if (GNUNET_DB_STATUS_SOFT_ERROR == transaction_commit_result) \
|
||||
{ \
|
||||
TALER_LOG_WARNING ("Transaction commit failed in %s\n", __FUNCTION__); \
|
||||
if (transaction_retries++ <= MAX_TRANSACTION_COMMIT_RETRIES) \
|
||||
goto transaction_start_label; \
|
||||
TALER_LOG_WARNING ("Transaction commit failed %u times in %s\n", \
|
||||
transaction_retries, \
|
||||
__FUNCTION__); \
|
||||
return TEH_RESPONSE_reply_commit_error (connection, \
|
||||
TALER_EC_DB_COMMIT_FAILED_ON_RETRY); \
|
||||
} \
|
||||
} /* end of scope opened by BEGIN_TRANSACTION */
|
||||
|
||||
|
||||
/**
|
||||
* Code to include to retry a transaction, must only be used in between
|
||||
* #START_TRANSACTION and #COMMIT_TRANSACTION.
|
||||
*
|
||||
* @param session session handle
|
||||
* @param connection connection handle
|
||||
*/
|
||||
#define RETRY_TRANSACTION(session,connection) \
|
||||
do { \
|
||||
TEH_plugin->rollback (TEH_plugin->cls, \
|
||||
session); \
|
||||
if (transaction_retries++ <= MAX_TRANSACTION_COMMIT_RETRIES) \
|
||||
goto transaction_start_label; \
|
||||
TALER_LOG_WARNING ("Transaction commit failed %u times in %s\n", \
|
||||
transaction_retries, \
|
||||
__FUNCTION__); \
|
||||
return TEH_RESPONSE_reply_commit_error (connection, \
|
||||
TALER_EC_DB_COMMIT_FAILED_ON_RETRY); \
|
||||
} while (0)
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* @brief Details about a melt operation of an individual coin.
|
||||
*/
|
||||
struct TEH_DB_MeltDetails
|
||||
{
|
||||
|
||||
/**
|
||||
* Information about the coin being melted.
|
||||
*/
|
||||
struct TALER_CoinPublicInfo coin_info;
|
||||
|
||||
/**
|
||||
* Signature allowing the melt (using
|
||||
* a `struct TALER_EXCHANGEDB_RefreshMeltConfirmSignRequestBody`) to sign over.
|
||||
*/
|
||||
struct TALER_CoinSpendSignatureP melt_sig;
|
||||
|
||||
/**
|
||||
* How much of the coin's value did the client allow to be melted?
|
||||
* This amount includes the fees, so the final amount contributed
|
||||
* to the melt is this value minus the fee for melting the coin.
|
||||
*/
|
||||
struct TALER_Amount melt_amount_with_fee;
|
||||
|
||||
/**
|
||||
* What fee is earned by the exchange? Set delayed during
|
||||
* #verify_coin_public_info().
|
||||
*/
|
||||
struct TALER_Amount melt_fee;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Send a response for a failed "/refresh/melt" request. The
|
||||
* transaction history of the given coin demonstrates that the
|
||||
* @a residual value of the coin is below the @a requested
|
||||
* contribution of the coin for the melt. Thus, the exchange
|
||||
* refuses the melt operation.
|
||||
*
|
||||
* @param connection the connection to send the response to
|
||||
* @param coin_pub public key of the coin
|
||||
* @param coin_value original value of the coin
|
||||
* @param tl transaction history for the coin
|
||||
* @param requested how much this coin was supposed to contribute, including fee
|
||||
* @param residual remaining value of the coin (after subtracting @a tl)
|
||||
* @return a MHD result code
|
||||
*/
|
||||
static int
|
||||
reply_refresh_melt_insufficient_funds (struct MHD_Connection *connection,
|
||||
const struct TALER_CoinSpendPublicKeyP *coin_pub,
|
||||
struct TALER_Amount coin_value,
|
||||
struct TALER_EXCHANGEDB_TransactionList *tl,
|
||||
struct TALER_Amount requested,
|
||||
struct TALER_Amount residual)
|
||||
{
|
||||
json_t *history;
|
||||
|
||||
history = TEH_RESPONSE_compile_transaction_history (tl);
|
||||
if (NULL == history)
|
||||
return TEH_RESPONSE_reply_internal_db_error (connection,
|
||||
TALER_EC_REFRESH_MELT_HISTORY_DB_ERROR_INSUFFICIENT_FUNDS);
|
||||
return TEH_RESPONSE_reply_json_pack (connection,
|
||||
MHD_HTTP_FORBIDDEN,
|
||||
"{s:s, s:I, s:o, s:o, s:o, s:o, s:o}",
|
||||
"error",
|
||||
"insufficient funds",
|
||||
"code",
|
||||
(json_int_t) TALER_EC_REFRESH_MELT_INSUFFICIENT_FUNDS,
|
||||
"coin_pub",
|
||||
GNUNET_JSON_from_data_auto (coin_pub),
|
||||
"original_value",
|
||||
TALER_JSON_from_amount (&coin_value),
|
||||
"residual_value",
|
||||
TALER_JSON_from_amount (&residual),
|
||||
"requested_value",
|
||||
TALER_JSON_from_amount (&requested),
|
||||
"history",
|
||||
history);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Send a response to a "/refresh/melt" request.
|
||||
*
|
||||
* @param connection the connection to send the response to
|
||||
* @param session_hash hash of the refresh session
|
||||
* @param noreveal_index which index will the client not have to reveal
|
||||
* @return a MHD status code
|
||||
*/
|
||||
static int
|
||||
reply_refresh_melt_success (struct MHD_Connection *connection,
|
||||
const struct GNUNET_HashCode *session_hash,
|
||||
uint16_t noreveal_index)
|
||||
{
|
||||
struct TALER_RefreshMeltConfirmationPS body;
|
||||
struct TALER_ExchangePublicKeyP pub;
|
||||
struct TALER_ExchangeSignatureP sig;
|
||||
json_t *sig_json;
|
||||
|
||||
body.purpose.size = htonl (sizeof (struct TALER_RefreshMeltConfirmationPS));
|
||||
body.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_MELT);
|
||||
body.session_hash = *session_hash;
|
||||
body.noreveal_index = htons (noreveal_index);
|
||||
body.reserved = htons (0);
|
||||
TEH_KS_sign (&body.purpose,
|
||||
&pub,
|
||||
&sig);
|
||||
sig_json = GNUNET_JSON_from_data_auto (&sig);
|
||||
GNUNET_assert (NULL != sig_json);
|
||||
return TEH_RESPONSE_reply_json_pack (connection,
|
||||
MHD_HTTP_OK,
|
||||
"{s:i, s:o, s:o}",
|
||||
"noreveal_index", (int) noreveal_index,
|
||||
"exchange_sig", sig_json,
|
||||
"exchange_pub", GNUNET_JSON_from_data_auto (&pub));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parse coin melt requests from a JSON object and write them to
|
||||
* the database.
|
||||
*
|
||||
* @param connection the connection to send errors to
|
||||
* @param session the database connection
|
||||
* @param key_state the exchange's key state
|
||||
* @param session_hash hash identifying the refresh session
|
||||
* @param coin_details details about the coin being melted
|
||||
* @param[out] meltp on success, set to melt details
|
||||
* @return #GNUNET_OK on success,
|
||||
* #GNUNET_NO if an error message was generated,
|
||||
* #GNUNET_SYSERR on internal errors (no response generated)
|
||||
*/
|
||||
static int
|
||||
refresh_check_melt (struct MHD_Connection *connection,
|
||||
struct TALER_EXCHANGEDB_Session *session,
|
||||
const struct TEH_KS_StateHandle *key_state,
|
||||
const struct GNUNET_HashCode *session_hash,
|
||||
const struct TEH_DB_MeltDetails *coin_details,
|
||||
struct TALER_EXCHANGEDB_RefreshMelt *meltp)
|
||||
{
|
||||
struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dk;
|
||||
struct TALER_EXCHANGEDB_DenominationKeyInformationP *dki;
|
||||
struct TALER_EXCHANGEDB_TransactionList *tl;
|
||||
struct TALER_Amount coin_value;
|
||||
struct TALER_Amount coin_residual;
|
||||
struct TALER_Amount spent;
|
||||
int res;
|
||||
enum GNUNET_DB_QueryStatus qs;
|
||||
|
||||
dk = TEH_KS_denomination_key_lookup (key_state,
|
||||
&coin_details->coin_info.denom_pub,
|
||||
TEH_KS_DKU_DEPOSIT);
|
||||
if (NULL == dk)
|
||||
return (MHD_YES ==
|
||||
TEH_RESPONSE_reply_internal_error (connection,
|
||||
TALER_EC_REFRESH_MELT_DB_DENOMINATION_KEY_NOT_FOUND,
|
||||
"denomination key no longer available while executing transaction"))
|
||||
? GNUNET_NO : GNUNET_SYSERR;
|
||||
dki = &dk->issue;
|
||||
TALER_amount_ntoh (&coin_value,
|
||||
&dki->properties.value);
|
||||
/* fee for THIS transaction; the melt amount includes the fee! */
|
||||
spent = coin_details->melt_amount_with_fee;
|
||||
/* add historic transaction costs of this coin */
|
||||
qs = TEH_plugin->get_coin_transactions (TEH_plugin->cls,
|
||||
session,
|
||||
&coin_details->coin_info.coin_pub,
|
||||
&tl);
|
||||
(void) qs; /* FIXME #5010 */
|
||||
if (GNUNET_OK !=
|
||||
TEH_DB_calculate_transaction_list_totals (tl,
|
||||
&spent,
|
||||
&spent))
|
||||
{
|
||||
GNUNET_break (0);
|
||||
TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
|
||||
tl);
|
||||
return (MHD_YES ==
|
||||
TEH_RESPONSE_reply_internal_db_error (connection,
|
||||
TALER_EC_REFRESH_MELT_COIN_HISTORY_COMPUTATION_FAILED))
|
||||
? GNUNET_NO : GNUNET_SYSERR;
|
||||
}
|
||||
/* Refuse to refresh when the coin's value is insufficient
|
||||
for the cost of all transactions. */
|
||||
if (TALER_amount_cmp (&coin_value,
|
||||
&spent) < 0)
|
||||
{
|
||||
GNUNET_assert (GNUNET_SYSERR !=
|
||||
TALER_amount_subtract (&coin_residual,
|
||||
&spent,
|
||||
&coin_details->melt_amount_with_fee));
|
||||
res = (MHD_YES ==
|
||||
reply_refresh_melt_insufficient_funds (connection,
|
||||
&coin_details->coin_info.coin_pub,
|
||||
coin_value,
|
||||
tl,
|
||||
coin_details->melt_amount_with_fee,
|
||||
coin_residual))
|
||||
? GNUNET_NO : GNUNET_SYSERR;
|
||||
TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
|
||||
tl);
|
||||
return res;
|
||||
}
|
||||
TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
|
||||
tl);
|
||||
|
||||
meltp->coin = coin_details->coin_info;
|
||||
meltp->coin_sig = coin_details->melt_sig;
|
||||
meltp->session_hash = *session_hash;
|
||||
meltp->amount_with_fee = coin_details->melt_amount_with_fee;
|
||||
meltp->melt_fee = coin_details->melt_fee;
|
||||
return GNUNET_OK;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Execute a "/refresh/melt". We have been given a list of valid
|
||||
* coins and a request to melt them into the given
|
||||
* @a refresh_session_pub. Check that the coins all have the
|
||||
* required value left and if so, store that they have been
|
||||
* melted and confirm the melting operation to the client.
|
||||
*
|
||||
* @param connection the MHD connection to handle
|
||||
* @param session_hash hash code of the session the coins are melted into
|
||||
* @param num_new_denoms number of entries in @a denom_pubs, size of y-dimension of @a commit_coin array
|
||||
* @param denom_pubs public keys of the coins we want to withdraw in the end
|
||||
* @param coin_melt_detail signature and (residual) value of the respective coin should be melted
|
||||
* @param commit_coin 2d array of coin commitments (what the exchange is to sign
|
||||
* once the "/refres/reveal" of cut and choose is done),
|
||||
* x-dimension must be #TALER_CNC_KAPPA
|
||||
* @param transfer_pubs array of transfer public keys (what the exchange is
|
||||
* to return via "/refresh/link" to enable linkage in the
|
||||
* future) of length #TALER_CNC_KAPPA
|
||||
* @return MHD result code
|
||||
*/
|
||||
static int
|
||||
execute_refresh_melt (struct MHD_Connection *connection,
|
||||
const struct GNUNET_HashCode *session_hash,
|
||||
unsigned int num_new_denoms,
|
||||
const struct TALER_DenominationPublicKey *denom_pubs,
|
||||
const struct TEH_DB_MeltDetails *coin_melt_detail,
|
||||
struct TALER_EXCHANGEDB_RefreshCommitCoin *const* commit_coin,
|
||||
const struct TALER_TransferPublicKeyP *transfer_pubs)
|
||||
{
|
||||
struct TEH_KS_StateHandle *key_state;
|
||||
struct TALER_EXCHANGEDB_RefreshSession refresh_session;
|
||||
struct TALER_EXCHANGEDB_Session *session;
|
||||
int res;
|
||||
|
||||
if (NULL == (session = TEH_plugin->get_session (TEH_plugin->cls)))
|
||||
{
|
||||
GNUNET_break (0);
|
||||
return TEH_RESPONSE_reply_internal_db_error (connection,
|
||||
TALER_EC_DB_SETUP_FAILED);
|
||||
}
|
||||
START_TRANSACTION (session, connection);
|
||||
res = TEH_plugin->get_refresh_session (TEH_plugin->cls,
|
||||
session,
|
||||
session_hash,
|
||||
&refresh_session);
|
||||
if (GNUNET_YES == res)
|
||||
{
|
||||
TEH_plugin->rollback (TEH_plugin->cls,
|
||||
session);
|
||||
res = reply_refresh_melt_success (connection,
|
||||
session_hash,
|
||||
refresh_session.noreveal_index);
|
||||
return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
|
||||
}
|
||||
if (GNUNET_SYSERR == res)
|
||||
{
|
||||
TEH_plugin->rollback (TEH_plugin->cls,
|
||||
session);
|
||||
return TEH_RESPONSE_reply_internal_db_error (connection,
|
||||
TALER_EC_REFRESH_MELT_DB_FETCH_ERROR);
|
||||
}
|
||||
|
||||
/* store 'global' session data */
|
||||
refresh_session.num_newcoins = num_new_denoms;
|
||||
refresh_session.noreveal_index
|
||||
= GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_STRONG,
|
||||
TALER_CNC_KAPPA);
|
||||
key_state = TEH_KS_acquire ();
|
||||
if (GNUNET_OK !=
|
||||
(res = refresh_check_melt (connection,
|
||||
session,
|
||||
key_state,
|
||||
session_hash,
|
||||
coin_melt_detail,
|
||||
&refresh_session.melt)))
|
||||
{
|
||||
TEH_KS_release (key_state);
|
||||
TEH_plugin->rollback (TEH_plugin->cls,
|
||||
session);
|
||||
return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
|
||||
}
|
||||
TEH_KS_release (key_state);
|
||||
|
||||
if (GNUNET_OK !=
|
||||
(res = TEH_plugin->create_refresh_session (TEH_plugin->cls,
|
||||
session,
|
||||
session_hash,
|
||||
&refresh_session)))
|
||||
{
|
||||
TEH_plugin->rollback (TEH_plugin->cls,
|
||||
session);
|
||||
return TEH_RESPONSE_reply_internal_db_error (connection,
|
||||
TALER_EC_REFRESH_MELT_DB_STORE_SESSION_ERROR);
|
||||
}
|
||||
|
||||
/* store requested new denominations */
|
||||
if (GNUNET_OK !=
|
||||
TEH_plugin->insert_refresh_order (TEH_plugin->cls,
|
||||
session,
|
||||
session_hash,
|
||||
num_new_denoms,
|
||||
denom_pubs))
|
||||
{
|
||||
TEH_plugin->rollback (TEH_plugin->cls,
|
||||
session);
|
||||
return TEH_RESPONSE_reply_internal_db_error (connection,
|
||||
TALER_EC_REFRESH_MELT_DB_STORE_ORDER_ERROR);
|
||||
}
|
||||
|
||||
if (GNUNET_OK !=
|
||||
TEH_plugin->insert_refresh_commit_coins (TEH_plugin->cls,
|
||||
session,
|
||||
session_hash,
|
||||
num_new_denoms,
|
||||
commit_coin[refresh_session.noreveal_index]))
|
||||
{
|
||||
TEH_plugin->rollback (TEH_plugin->cls,
|
||||
session);
|
||||
return TEH_RESPONSE_reply_internal_db_error (connection,
|
||||
TALER_EC_REFRESH_MELT_DB_STORE_ORDER_ERROR);
|
||||
}
|
||||
if (GNUNET_OK !=
|
||||
TEH_plugin->insert_refresh_transfer_public_key (TEH_plugin->cls,
|
||||
session,
|
||||
session_hash,
|
||||
&transfer_pubs[refresh_session.noreveal_index]))
|
||||
{
|
||||
TEH_plugin->rollback (TEH_plugin->cls,
|
||||
session);
|
||||
return TEH_RESPONSE_reply_internal_db_error (connection,
|
||||
TALER_EC_REFRESH_MELT_DB_STORE_TRANSFER_ERROR);
|
||||
}
|
||||
|
||||
COMMIT_TRANSACTION (session, connection);
|
||||
return reply_refresh_melt_success (connection,
|
||||
session_hash,
|
||||
refresh_session.noreveal_index);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handle a "/refresh/melt" request after the main JSON parsing has happened.
|
||||
* We now need to validate the coins being melted and the session signature
|
||||
@ -149,7 +592,7 @@ handle_refresh_melt_binary (struct MHD_Connection *connection,
|
||||
"error", "value mismatch",
|
||||
"code", (json_int_t) TALER_EC_REFRESH_MELT_FEES_MISSMATCH);
|
||||
}
|
||||
return TEH_DB_execute_refresh_melt (connection,
|
||||
return execute_refresh_melt (connection,
|
||||
session_hash,
|
||||
num_new_denoms,
|
||||
denom_pubs,
|
||||
@ -597,159 +1040,4 @@ TEH_REFRESH_handler_refresh_melt (struct TEH_RequestHandler *rh,
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handle a "/refresh/reveal" request. Parses the given JSON
|
||||
* transfer private keys and if successful, passes everything to
|
||||
* #TEH_DB_execute_refresh_reveal() which will verify that the
|
||||
* revealed information is valid then returns the signed refreshed
|
||||
* coins.
|
||||
*
|
||||
* @param connection the MHD connection to handle
|
||||
* @param session_hash hash identifying the melting session
|
||||
* @param tp_json private transfer keys in JSON format
|
||||
* @return MHD result code
|
||||
*/
|
||||
static int
|
||||
handle_refresh_reveal_json (struct MHD_Connection *connection,
|
||||
const struct GNUNET_HashCode *session_hash,
|
||||
const json_t *tp_json)
|
||||
{
|
||||
struct TALER_TransferPrivateKeyP transfer_privs[TALER_CNC_KAPPA - 1];
|
||||
unsigned int i;
|
||||
int res;
|
||||
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
||||
"reveal request for session %s\n",
|
||||
GNUNET_h2s (session_hash));
|
||||
|
||||
res = GNUNET_OK;
|
||||
for (i = 0; i < TALER_CNC_KAPPA - 1; i++)
|
||||
{
|
||||
struct GNUNET_JSON_Specification tp_spec[] = {
|
||||
GNUNET_JSON_spec_fixed_auto (NULL, &transfer_privs[i]),
|
||||
GNUNET_JSON_spec_end ()
|
||||
};
|
||||
|
||||
if (GNUNET_OK != res)
|
||||
break;
|
||||
res = TEH_PARSE_json_array (connection,
|
||||
tp_json,
|
||||
tp_spec,
|
||||
i, -1);
|
||||
GNUNET_break_op (GNUNET_OK == res);
|
||||
}
|
||||
if (GNUNET_OK != res)
|
||||
res = (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
|
||||
else
|
||||
res = TEH_DB_execute_refresh_reveal (connection,
|
||||
session_hash,
|
||||
transfer_privs);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handle a "/refresh/reveal" request. This time, the client reveals
|
||||
* the private transfer keys except for the cut-and-choose value
|
||||
* returned from "/refresh/melt". This function parses the revealed
|
||||
* keys and secrets and ultimately passes everything to
|
||||
* #TEH_DB_execute_refresh_reveal() which will verify that the
|
||||
* revealed information is valid then returns the signed refreshed
|
||||
* coins.
|
||||
*
|
||||
* @param rh context of the handler
|
||||
* @param connection the MHD connection to handle
|
||||
* @param[in,out] connection_cls the connection's closure (can be updated)
|
||||
* @param upload_data upload data
|
||||
* @param[in,out] upload_data_size number of bytes (left) in @a upload_data
|
||||
* @return MHD result code
|
||||
*/
|
||||
int
|
||||
TEH_REFRESH_handler_refresh_reveal (struct TEH_RequestHandler *rh,
|
||||
struct MHD_Connection *connection,
|
||||
void **connection_cls,
|
||||
const char *upload_data,
|
||||
size_t *upload_data_size)
|
||||
{
|
||||
struct GNUNET_HashCode session_hash;
|
||||
int res;
|
||||
json_t *root;
|
||||
json_t *transfer_privs;
|
||||
struct GNUNET_JSON_Specification spec[] = {
|
||||
GNUNET_JSON_spec_fixed_auto ("session_hash", &session_hash),
|
||||
GNUNET_JSON_spec_json ("transfer_privs", &transfer_privs),
|
||||
GNUNET_JSON_spec_end ()
|
||||
};
|
||||
|
||||
res = TEH_PARSE_post_json (connection,
|
||||
connection_cls,
|
||||
upload_data,
|
||||
upload_data_size,
|
||||
&root);
|
||||
if (GNUNET_SYSERR == res)
|
||||
return MHD_NO;
|
||||
if ( (GNUNET_NO == res) || (NULL == root) )
|
||||
return MHD_YES;
|
||||
|
||||
res = TEH_PARSE_json_data (connection,
|
||||
root,
|
||||
spec);
|
||||
json_decref (root);
|
||||
if (GNUNET_OK != res)
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
|
||||
}
|
||||
/* Determine dimensionality of the request (kappa and #old coins) */
|
||||
/* Note we do +1 as 1 row (cut-and-choose!) is missing! */
|
||||
if (TALER_CNC_KAPPA != json_array_size (transfer_privs) + 1)
|
||||
{
|
||||
GNUNET_JSON_parse_free (spec);
|
||||
GNUNET_break_op (0);
|
||||
return TEH_RESPONSE_reply_arg_invalid (connection,
|
||||
TALER_EC_REFRESH_REVEAL_CNC_TRANSFER_ARRAY_SIZE_INVALID,
|
||||
"transfer_privs");
|
||||
}
|
||||
res = handle_refresh_reveal_json (connection,
|
||||
&session_hash,
|
||||
transfer_privs);
|
||||
GNUNET_JSON_parse_free (spec);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Handle a "/refresh/link" request. Note that for "/refresh/link"
|
||||
* we do use a simple HTTP GET, and a HTTP POST!
|
||||
*
|
||||
* @param rh context of the handler
|
||||
* @param connection the MHD connection to handle
|
||||
* @param[in,out] connection_cls the connection's closure (can be updated)
|
||||
* @param upload_data upload data
|
||||
* @param[in,out] upload_data_size number of bytes (left) in @a upload_data
|
||||
* @return MHD result code
|
||||
*/
|
||||
int
|
||||
TEH_REFRESH_handler_refresh_link (struct TEH_RequestHandler *rh,
|
||||
struct MHD_Connection *connection,
|
||||
void **connection_cls,
|
||||
const char *upload_data,
|
||||
size_t *upload_data_size)
|
||||
{
|
||||
struct TALER_CoinSpendPublicKeyP coin_pub;
|
||||
int res;
|
||||
|
||||
res = TEH_PARSE_mhd_request_arg_data (connection,
|
||||
"coin_pub",
|
||||
&coin_pub,
|
||||
sizeof (struct TALER_CoinSpendPublicKeyP));
|
||||
if (GNUNET_SYSERR == res)
|
||||
return MHD_NO;
|
||||
if (GNUNET_OK != res)
|
||||
return MHD_YES;
|
||||
return TEH_DB_execute_refresh_link (connection,
|
||||
&coin_pub);
|
||||
}
|
||||
|
||||
|
||||
/* end of taler-exchange-httpd_refresh.c */
|
||||
/* end of taler-exchange-httpd_refresh_melt.c */
|
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
|
||||
Copyright (C) 2014, 2015 GNUnet e.V.
|
||||
Copyright (C) 2014-2017 GNUnet e.V.
|
||||
|
||||
TALER is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU Affero General Public License as published by the Free Software
|
||||
@ -14,41 +14,20 @@
|
||||
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
/**
|
||||
* @file taler-exchange-httpd_refresh.h
|
||||
* @brief Handle /refresh/ requests
|
||||
* @file taler-exchange-httpd_refresh_reveal.h
|
||||
* @brief Handle /refresh/reveal requests
|
||||
* @author Florian Dold
|
||||
* @author Benedikt Mueller
|
||||
* @author Christian Grothoff
|
||||
*/
|
||||
#ifndef TALER_EXCHANGE_HTTPD_REFRESH_H
|
||||
#define TALER_EXCHANGE_HTTPD_REFRESH_H
|
||||
#ifndef TALER_EXCHANGE_HTTPD_REFRESH_REVEAL_H
|
||||
#define TALER_EXCHANGE_HTTPD_REFRESH_REVEAL_H
|
||||
|
||||
#include <gnunet/gnunet_util_lib.h>
|
||||
#include <microhttpd.h>
|
||||
#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
|
||||
* 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,
|
||||
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
|
@ -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
|
||||
* 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);
|
||||
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
|
Loading…
Reference in New Issue
Block a user