summaryrefslogtreecommitdiff
path: root/src/exchange
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2017-11-27 23:42:17 +0100
committerChristian Grothoff <christian@grothoff.org>2017-11-29 20:23:08 +0100
commit499247a4805583dc67b9d6fef850ae86b4be1e32 (patch)
treeabf9bf358bc00149a78d8128101bfc43540af8f2 /src/exchange
parent9041840d6e1caa5a0a4f8222b312b547ccd2ab1b (diff)
downloadexchange-499247a4805583dc67b9d6fef850ae86b4be1e32.tar.gz
exchange-499247a4805583dc67b9d6fef850ae86b4be1e32.tar.bz2
exchange-499247a4805583dc67b9d6fef850ae86b4be1e32.zip
fixing #5178
Diffstat (limited to 'src/exchange')
-rw-r--r--src/exchange/taler-exchange-httpd_db.c2
-rw-r--r--src/exchange/taler-exchange-httpd_keystate.c40
-rw-r--r--src/exchange/taler-exchange-httpd_keystate.h16
-rw-r--r--src/exchange/taler-exchange-httpd_refresh_link.c240
-rw-r--r--src/exchange/taler-exchange-httpd_refresh_melt.c706
-rw-r--r--src/exchange/taler-exchange-httpd_refresh_reveal.c860
-rw-r--r--src/exchange/taler-exchange-httpd_responses.c20
7 files changed, 656 insertions, 1228 deletions
diff --git a/src/exchange/taler-exchange-httpd_db.c b/src/exchange/taler-exchange-httpd_db.c
index 30bc33e17..bd7777ce3 100644
--- a/src/exchange/taler-exchange-httpd_db.c
+++ b/src/exchange/taler-exchange-httpd_db.c
@@ -155,7 +155,7 @@ TEH_DB_calculate_transaction_list_totals (struct TALER_EXCHANGEDB_TransactionLis
if (GNUNET_OK !=
TALER_amount_add (&spent,
&spent,
- &pos->details.melt->amount_with_fee))
+ &pos->details.melt->session.amount_with_fee))
{
GNUNET_break (0);
return GNUNET_SYSERR;
diff --git a/src/exchange/taler-exchange-httpd_keystate.c b/src/exchange/taler-exchange-httpd_keystate.c
index be87f6d49..801d6fee6 100644
--- a/src/exchange/taler-exchange-httpd_keystate.c
+++ b/src/exchange/taler-exchange-httpd_keystate.c
@@ -39,7 +39,7 @@
* release version, and the format is NOT the same that semantic
* versioning uses either.
*/
-#define TALER_PROTOCOL_VERSION "1:0:1"
+#define TALER_PROTOCOL_VERSION "2:0:0"
/**
@@ -1499,7 +1499,7 @@ make_fresh_key_state ()
GNUNET_h2s (&dke->denom_key_hash),
dke);
}
-
+
/* Determine size of `krd_array` by counting number of discrete
denomination key starting times. */
last = GNUNET_TIME_UNIT_ZERO_ABS;
@@ -1662,15 +1662,37 @@ TEH_KS_denomination_key_lookup (const struct TEH_KS_StateHandle *key_state,
enum TEH_KS_DenominationKeyUse use)
{
struct GNUNET_HashCode hc;
+
+ GNUNET_CRYPTO_rsa_public_key_hash (denom_pub->rsa_public_key,
+ &hc);
+ return TEH_KS_denomination_key_lookup_by_hash (key_state,
+ &hc,
+ use);
+}
+
+
+/**
+ * Look up the issue for a denom public key. Note that the result
+ * is only valid while the @a key_state is not released!
+ *
+ * @param key_state state to look in
+ * @param denom_pub_hash hash of denomination public key
+ * @param use purpose for which the key is being located
+ * @return the denomination key issue,
+ * or NULL if denom_pub could not be found (or is not valid at this time for the given @a use)
+ */
+struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *
+TEH_KS_denomination_key_lookup_by_hash (const struct TEH_KS_StateHandle *key_state,
+ const struct GNUNET_HashCode *denom_pub_hash,
+ enum TEH_KS_DenominationKeyUse use)
+{
struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki;
struct GNUNET_TIME_Absolute now;
const struct GNUNET_CONTAINER_MultiHashMap *map;
- GNUNET_CRYPTO_rsa_public_key_hash (denom_pub->rsa_public_key,
- &hc);
map = (TEH_KS_DKU_PAYBACK == use) ? key_state->revoked_map : key_state->denomkey_map;
dki = GNUNET_CONTAINER_multihashmap_get (map,
- &hc);
+ denom_pub_hash);
if (NULL == dki)
return NULL;
now = GNUNET_TIME_absolute_get ();
@@ -1679,7 +1701,7 @@ TEH_KS_denomination_key_lookup (const struct TEH_KS_StateHandle *key_state,
{
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Not returning DKI for %s, as start time is in the future\n",
- GNUNET_h2s (&hc));
+ GNUNET_h2s (denom_pub_hash));
return NULL;
}
now = GNUNET_TIME_absolute_get ();
@@ -1691,7 +1713,7 @@ TEH_KS_denomination_key_lookup (const struct TEH_KS_StateHandle *key_state,
{
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Not returning DKI for %s, as time to create coins has passed\n",
- GNUNET_h2s (&hc));
+ GNUNET_h2s (denom_pub_hash));
return NULL;
}
break;
@@ -1701,7 +1723,7 @@ TEH_KS_denomination_key_lookup (const struct TEH_KS_StateHandle *key_state,
{
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Not returning DKI for %s, as time to spend coin has passed\n",
- GNUNET_h2s (&hc));
+ GNUNET_h2s (denom_pub_hash));
return NULL;
}
break;
@@ -1711,7 +1733,7 @@ TEH_KS_denomination_key_lookup (const struct TEH_KS_StateHandle *key_state,
{
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Not returning DKI for %s, as time to payback coin has passed\n",
- GNUNET_h2s (&hc));
+ GNUNET_h2s (denom_pub_hash));
return NULL;
}
break;
diff --git a/src/exchange/taler-exchange-httpd_keystate.h b/src/exchange/taler-exchange-httpd_keystate.h
index b956c6308..c332182e0 100644
--- a/src/exchange/taler-exchange-httpd_keystate.h
+++ b/src/exchange/taler-exchange-httpd_keystate.h
@@ -121,6 +121,22 @@ TEH_KS_denomination_key_lookup (const struct TEH_KS_StateHandle *key_state,
/**
+ * Look up the issue for a denom public key. Note that the result
+ * is only valid while the @a key_state is not released!
+ *
+ * @param key_state state to look in
+ * @param denom_pub_hash hash of denomination public key
+ * @param use purpose for which the key is being located
+ * @return the denomination key issue,
+ * or NULL if denom_pub could not be found (or is not valid at this time for the given @a use)
+ */
+struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *
+TEH_KS_denomination_key_lookup_by_hash (const struct TEH_KS_StateHandle *key_state,
+ const struct GNUNET_HashCode *denom_pub_hash,
+ enum TEH_KS_DenominationKeyUse use);
+
+
+/**
* Read signals from a pipe in a loop, and reload keys from disk if
* SIGUSR1 is received, terminate if SIGTERM/SIGINT is received, and
* restart if SIGHUP is received.
diff --git a/src/exchange/taler-exchange-httpd_refresh_link.c b/src/exchange/taler-exchange-httpd_refresh_link.c
index 0be699840..aee23369c 100644
--- a/src/exchange/taler-exchange-httpd_refresh_link.c
+++ b/src/exchange/taler-exchange-httpd_refresh_link.c
@@ -32,183 +32,88 @@
/**
- * @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
{
/**
- * Public key of the coin that we are tracing.
+ * Public key of the coin for which we are running /refresh/link.
*/
struct TALER_CoinSpendPublicKeyP coin_pub;
/**
- * Session link data we collect.
- */
- struct TEH_RESPONSE_LinkSessionInfo *sessions;
-
- /**
- * Database session. Nothing to do with @a sessions.
+ * Json array with transfer data we collect.
*/
- 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;
+ json_t *mlist;
/**
- * 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().
+ * Taler error code.
*/
- int status;
+ enum TALER_ErrorCode ec;
};
/**
- * 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 *mlist;
- int res;
-
- mlist = json_array ();
- for (unsigned int i=0;i<num_sessions;i++)
- {
- json_t *list = json_array ();
- json_t *root;
-
- for (const struct TALER_EXCHANGEDB_LinkDataList *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
+ * @param ldl link data related to @a transfer_pub
*/
static void
-handle_transfer_data (void *cls,
- const struct GNUNET_HashCode *session_hash,
- const struct TALER_TransferPublicKeyP *transfer_pub)
+handle_link_data (void *cls,
+ const struct TALER_TransferPublicKeyP *transfer_pub,
+ const struct TALER_EXCHANGEDB_LinkDataList *ldl)
{
struct HTD_Context *ctx = cls;
- struct TALER_EXCHANGEDB_LinkDataList *ldl;
- struct TEH_RESPONSE_LinkSessionInfo *lsi;
- enum GNUNET_DB_QueryStatus qs;
+ json_t *list;
+ json_t *root;
- if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != ctx->status)
+ if (NULL == ctx->mlist)
return;
- ldl = NULL;
- qs = TEH_plugin->get_link_data_list (TEH_plugin->cls,
- ctx->session,
- session_hash,
- &ldl);
- if (qs <= 0)
+ if (NULL == (list = json_array ()))
+ goto fail;
+
+ for (const struct TALER_EXCHANGEDB_LinkDataList *pos = ldl;
+ NULL != pos;
+ pos = pos->next)
{
- if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
- ctx->status = GNUNET_DB_STATUS_HARD_ERROR;
- else
- ctx->status = qs;
- return;
+ json_t *obj;
+
+ if (NULL == (obj = json_object ()))
+ goto fail;
+ 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));
+ if (0 !=
+ json_array_append_new (list,
+ obj))
+ goto fail;
}
- GNUNET_assert (NULL != ldl);
- 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;
-}
-
-
-/**
- * Free session data kept in @a ctx
- *
- * @param ctx context to clean up
- */
-static void
-purge_context (struct HTD_Context *ctx)
-{
- for (unsigned int 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);
- ctx->sessions = NULL;
- ctx->num_sessions = 0;
+ if (NULL == (root = json_object ()))
+ goto fail;
+ json_object_set_new (root,
+ "new_coins",
+ list);
+ json_object_set_new (root,
+ "transfer_pub",
+ GNUNET_JSON_from_data_auto (transfer_pub));
+ if (0 !=
+ json_array_append_new (ctx->mlist,
+ root))
+ goto fail;
+ return;
+ fail:
+ ctx->ec = TALER_EC_JSON_ALLOCATION_FAILURE;
+ json_decref (ctx->mlist);
+ ctx->mlist = NULL;
}
@@ -239,14 +144,18 @@ refresh_link_transaction (void *cls,
struct HTD_Context *ctx = cls;
enum GNUNET_DB_QueryStatus qs;
- ctx->session = session;
- ctx->status = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
- qs = TEH_plugin->get_transfer (TEH_plugin->cls,
- session,
- &ctx->coin_pub,
- &handle_transfer_data,
- ctx);
- ctx->session = NULL;
+ qs = TEH_plugin->get_link_data (TEH_plugin->cls,
+ session,
+ &ctx->coin_pub,
+ &handle_link_data,
+ ctx);
+ if (NULL == ctx->mlist)
+ {
+ *mhd_ret = TEH_RESPONSE_reply_internal_error (connection,
+ ctx->ec,
+ "coin_pub");
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
{
*mhd_ret = TEH_RESPONSE_reply_arg_unknown (connection,
@@ -254,21 +163,6 @@ refresh_link_transaction (void *cls,
"coin_pub");
return GNUNET_DB_STATUS_HARD_ERROR;
}
- if (0 < qs)
- {
- qs = ctx->status;
- if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
- {
- *mhd_ret = TEH_RESPONSE_reply_json_pack (ctx->connection,
- MHD_HTTP_NOT_FOUND,
- "{s:s}",
- "error",
- "link data not found (link)");
- return GNUNET_DB_STATUS_HARD_ERROR;
- }
- return qs;
- }
- purge_context (ctx);
return qs;
}
@@ -306,19 +200,21 @@ TEH_REFRESH_handler_refresh_link (struct TEH_RequestHandler *rh,
return MHD_NO;
if (GNUNET_OK != res)
return MHD_YES;
+ ctx.mlist = json_array ();
if (GNUNET_OK !=
TEH_DB_run_transaction (connection,
&mhd_ret,
&refresh_link_transaction,
&ctx))
{
- purge_context (&ctx);
+ if (NULL != ctx.mlist)
+ json_decref (ctx.mlist);
return mhd_ret;
}
- mhd_ret = reply_refresh_link_success (connection,
- ctx.num_sessions,
- ctx.sessions);
- purge_context (&ctx);
+ mhd_ret = TEH_RESPONSE_reply_json (connection,
+ ctx.mlist,
+ MHD_HTTP_OK);
+ json_decref (ctx.mlist);
return mhd_ret;
}
diff --git a/src/exchange/taler-exchange-httpd_refresh_melt.c b/src/exchange/taler-exchange-httpd_refresh_melt.c
index 320ec9d8b..400d2bb8c 100644
--- a/src/exchange/taler-exchange-httpd_refresh_melt.c
+++ b/src/exchange/taler-exchange-httpd_refresh_melt.c
@@ -32,38 +32,6 @@
/**
- * @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
@@ -116,14 +84,14 @@ reply_refresh_melt_insufficient_funds (struct MHD_Connection *connection,
* 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 rc value the client commited to
* @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)
+ const struct TALER_RefreshCommitmentP *rc,
+ uint32_t noreveal_index)
{
struct TALER_RefreshMeltConfirmationPS body;
struct TALER_ExchangePublicKeyP pub;
@@ -132,9 +100,8 @@ reply_refresh_melt_success (struct MHD_Connection *connection,
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);
+ body.rc = *rc;
+ body.noreveal_index = htonl (noreveal_index);
if (GNUNET_OK !=
TEH_KS_sign (&body.purpose,
&pub,
@@ -162,63 +129,22 @@ struct RefreshMeltContext
{
/**
- * Key state that can be used to lookup keys.
+ * noreveal_index is only initialized during
+ * #refresh_melt_transaction().
*/
- struct TEH_KS_StateHandle *key_state;
+ struct TALER_EXCHANGEDB_RefreshSession refresh_session;
/**
- * Information about the denomination key of the coin being
- * melted.
+ * Information about the @e coin's denomination.
*/
struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki;
- /**
- * Array of denominations of the fresh coins.
- */
- struct TALER_DenominationPublicKey *denom_pubs;
-
- /**
- * Number of new coins to be generated in the melt.
- * Size of the @e denom_pubs array.
- */
- unsigned int num_newcoins;
-
- /**
- * Details about the coin to be melted.
- */
- struct TEH_DB_MeltDetails coin_melt_details;
-
- /**
- * Set to the session hash once the @e hash_context has finished.
- */
- struct GNUNET_HashCode session_hash;
-
- /**
- * Hash operation used to calculate the session hash.
- */
- struct GNUNET_HashContext *hash_context;
-
- /**
- * Committments to the blinded envelopes for the fresh coins.
- */
- struct TALER_EXCHANGEDB_RefreshCommitCoin *commit_coin[TALER_CNC_KAPPA];
-
- /**
- * Commmittments to the transfer public keys.
- */
- struct TALER_TransferPublicKeyP transfer_pub[TALER_CNC_KAPPA];
-
- /**
- * Initialized during #refresh_melt_transaction().
- */
- struct TALER_EXCHANGEDB_RefreshSession refresh_session;
-
};
/**
- * Parse coin melt requests from a JSON object and write them to
- * the database.
+ * Check that the coin has sufficient funds left for the selected
+ * melt operation.
*
* @param connection the connection to send errors to
* @param session the database connection
@@ -233,20 +159,19 @@ refresh_check_melt (struct MHD_Connection *connection,
int *mhd_ret)
{
struct TALER_EXCHANGEDB_TransactionList *tl;
- struct TALER_EXCHANGEDB_RefreshMelt *meltp = &rmc->refresh_session.melt;
struct TALER_Amount coin_value;
- struct TALER_Amount coin_residual;
struct TALER_Amount spent;
enum GNUNET_DB_QueryStatus qs;
TALER_amount_ntoh (&coin_value,
&rmc->dki->issue.properties.value);
- /* fee for THIS transaction; the melt amount includes the fee! */
- spent = rmc->coin_melt_details.melt_amount_with_fee;
+ /* Start with cost of this melt transaction */
+ spent = rmc->refresh_session.amount_with_fee;
+
/* add historic transaction costs of this coin */
qs = TEH_plugin->get_coin_transactions (TEH_plugin->cls,
session,
- &rmc->coin_melt_details.coin_info.coin_pub,
+ &rmc->refresh_session.coin.coin_pub,
&tl);
if (0 > qs)
{
@@ -267,33 +192,32 @@ refresh_check_melt (struct MHD_Connection *connection,
TALER_EC_REFRESH_MELT_COIN_HISTORY_COMPUTATION_FAILED);
return GNUNET_DB_STATUS_HARD_ERROR;
}
+
/* Refuse to refresh when the coin's value is insufficient
for the cost of all transactions. */
if (TALER_amount_cmp (&coin_value,
&spent) < 0)
{
+ struct TALER_Amount coin_residual;
+
GNUNET_assert (GNUNET_SYSERR !=
TALER_amount_subtract (&coin_residual,
&spent,
- &rmc->coin_melt_details.melt_amount_with_fee));
+ &rmc->refresh_session.amount_with_fee));
*mhd_ret = reply_refresh_melt_insufficient_funds (connection,
- &rmc->coin_melt_details.coin_info.coin_pub,
+ &rmc->refresh_session.coin.coin_pub,
coin_value,
tl,
- &rmc->coin_melt_details.melt_amount_with_fee,
+ &rmc->refresh_session.amount_with_fee,
&coin_residual);
TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
tl);
return GNUNET_DB_STATUS_HARD_ERROR;
}
+
+ /* we're good, coin has sufficient funds to be melted */
TEH_plugin->free_coin_transaction_list (TEH_plugin->cls,
tl);
-
- meltp->coin = rmc->coin_melt_details.coin_info;
- meltp->coin_sig = rmc->coin_melt_details.melt_sig;
- meltp->session_hash = rmc->session_hash;
- meltp->amount_with_fee = rmc->coin_melt_details.melt_amount_with_fee;
- meltp->melt_fee = rmc->coin_melt_details.melt_fee;
return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
}
@@ -325,17 +249,19 @@ refresh_melt_transaction (void *cls,
int *mhd_ret)
{
struct RefreshMeltContext *rmc = cls;
+ struct TALER_EXCHANGEDB_RefreshMelt rm;
enum GNUNET_DB_QueryStatus qs;
- qs = TEH_plugin->get_refresh_session (TEH_plugin->cls,
- session,
- &rmc->session_hash,
- &rmc->refresh_session);
+ /* Check if we already created such a session */
+ qs = TEH_plugin->get_melt (TEH_plugin->cls,
+ session,
+ &rmc->refresh_session.rc,
+ &rm);
if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
{
*mhd_ret = reply_refresh_melt_success (connection,
- &rmc->session_hash,
- rmc->refresh_session.noreveal_index);
+ &rmc->refresh_session.rc,
+ rm.session.noreveal_index);
return GNUNET_DB_STATUS_HARD_ERROR;
}
if (0 > qs)
@@ -346,12 +272,7 @@ refresh_melt_transaction (void *cls,
return qs;
}
- /* store 'global' session data */
- rmc->refresh_session.num_newcoins = rmc->num_newcoins;
- rmc->refresh_session.noreveal_index
- = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_STRONG,
- TALER_CNC_KAPPA);
-
+ /* check coin has enough funds remaining on it to cover melt cost */
qs = refresh_check_melt (connection,
session,
rmc,
@@ -359,28 +280,15 @@ refresh_melt_transaction (void *cls,
if (0 > qs)
return qs;
- if ( (0 >=
- (qs = TEH_plugin->create_refresh_session (TEH_plugin->cls,
- session,
- &rmc->session_hash,
- &rmc->refresh_session))) ||
- (0 >=
- (qs = TEH_plugin->insert_refresh_order (TEH_plugin->cls,
- session,
- &rmc->session_hash,
- rmc->num_newcoins,
- rmc->denom_pubs))) ||
- (0 >=
- (qs = TEH_plugin->insert_refresh_commit_coins (TEH_plugin->cls,
- session,
- &rmc->session_hash,
- rmc->num_newcoins,
- rmc->commit_coin[rmc->refresh_session.noreveal_index]))) ||
- (0 >=
- (qs = TEH_plugin->insert_refresh_transfer_public_key (TEH_plugin->cls,
- session,
- &rmc->session_hash,
- &rmc->transfer_pub[rmc->refresh_session.noreveal_index]))) )
+ /* pick challenge and persist it */
+ rmc->refresh_session.noreveal_index
+ = GNUNET_CRYPTO_random_u32 (GNUNET_CRYPTO_QUALITY_STRONG,
+ TALER_CNC_KAPPA);
+
+ if (0 >=
+ (qs = TEH_plugin->insert_melt (TEH_plugin->cls,
+ session,
+ &rmc->refresh_session)))
{
if (GNUNET_DB_STATUS_SOFT_ERROR != qs)
{
@@ -395,233 +303,6 @@ refresh_melt_transaction (void *cls,
/**
- * Handle a "/refresh/melt" request after the main JSON parsing has
- * happened. We now need to validate the coins being melted and the
- * session signature and then hand things of to execute the melt
- * operation.
- *
- * @param connection the MHD connection to handle
- * @param[out] mhd_ret set on failure to return value for MHD
- * @param rmc information about the melt to process
- * @return MHD result code
- */
-static int
-refresh_melt_prepare (struct MHD_Connection *connection,
- int *mhd_ret,
- struct RefreshMeltContext *rmc)
-{
- struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dk;
- struct TALER_EXCHANGEDB_DenominationKeyInformationP *dki;
- struct TALER_Amount cost;
- struct TALER_Amount total_cost;
- struct TALER_Amount value;
- struct TALER_Amount fee_withdraw;
- struct TALER_Amount fee_melt;
- struct TALER_Amount total_melt;
-
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "/refresh/melt request for session %s\n",
- GNUNET_h2s (&rmc->session_hash));
-
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_get_zero (TEH_exchange_currency_string,
- &total_cost));
- for (unsigned int i=0;i<rmc->num_newcoins;i++)
- {
- dk = TEH_KS_denomination_key_lookup (rmc->key_state,
- &rmc->denom_pubs[i],
- TEH_KS_DKU_WITHDRAW);
- if (NULL == dk)
- {
- GNUNET_break_op (0);
- *mhd_ret = TEH_RESPONSE_reply_arg_invalid (connection,
- TALER_EC_REFRESH_MELT_FRESH_DENOMINATION_KEY_NOT_FOUND,
- "new_denoms");
- return GNUNET_SYSERR;
- }
- dki = &dk->issue;
- TALER_amount_ntoh (&value,
- &dki->properties.value);
- TALER_amount_ntoh (&fee_withdraw,
- &dki->properties.fee_withdraw);
- if ( (GNUNET_OK !=
- TALER_amount_add (&cost,
- &value,
- &fee_withdraw)) ||
- (GNUNET_OK !=
- TALER_amount_add (&total_cost,
- &cost,
- &total_cost)) )
- {
- GNUNET_break_op (0);
- *mhd_ret = TEH_RESPONSE_reply_internal_error (connection,
- TALER_EC_REFRESH_MELT_COST_CALCULATION_OVERFLOW,
- "cost calculation failure");
- return GNUNET_SYSERR;
- }
- }
-
- dki = &rmc->dki->issue;
- TALER_amount_ntoh (&fee_melt,
- &dki->properties.fee_refresh);
- if (GNUNET_OK !=
- TALER_amount_subtract (&total_melt,
- &rmc->coin_melt_details.melt_amount_with_fee,
- &fee_melt))
- {
- GNUNET_break_op (0);
- *mhd_ret = TEH_RESPONSE_reply_external_error (connection,
- TALER_EC_REFRESH_MELT_FEES_EXCEED_CONTRIBUTION,
- "Melt contribution below melting fee");
- return GNUNET_SYSERR;
- }
- if (0 !=
- TALER_amount_cmp (&total_cost,
- &total_melt))
- {
- GNUNET_break_op (0);
- /* We require total value of coins being melted and
- total value of coins being generated to match! */
- *mhd_ret = TEH_RESPONSE_reply_json_pack (connection,
- MHD_HTTP_BAD_REQUEST,
- "{s:s, s:I}",
- "error", "value mismatch",
- "code", (json_int_t) TALER_EC_REFRESH_MELT_FEES_MISSMATCH);
- return GNUNET_SYSERR;
- }
- return TEH_DB_run_transaction (connection,
- mhd_ret,
- &refresh_melt_transaction,
- rmc);
-}
-
-
-/**
- * Extract public coin information from a JSON object.
- *
- * @param connection the connection to send error responses to
- * @param coin_info the JSON object to extract the coin info from
- * @param[out] r_melt_detail set to details about the coin's melting permission (if valid)
- * @return #GNUNET_YES if coin public info in JSON was valid
- * #GNUNET_NO JSON was invalid, response was generated
- * #GNUNET_SYSERR on internal error
- */
-static int
-get_coin_public_info (struct MHD_Connection *connection,
- const json_t *coin_info,
- struct TEH_DB_MeltDetails *r_melt_detail)
-{
- int ret;
- struct TALER_CoinSpendSignatureP melt_sig;
- struct TALER_DenominationSignature sig;
- struct TALER_DenominationPublicKey pk;
- struct TALER_Amount amount;
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_fixed_auto ("coin_pub", &r_melt_detail->coin_info.coin_pub),
- TALER_JSON_spec_denomination_signature ("denom_sig", &sig),
- TALER_JSON_spec_denomination_public_key ("denom_pub", &pk),
- GNUNET_JSON_spec_fixed_auto ("confirm_sig", &melt_sig),
- TALER_JSON_spec_amount ("value_with_fee", &amount),
- GNUNET_JSON_spec_end ()
- };
-
- ret = TEH_PARSE_json_data (connection,
- coin_info,
- spec);
- if (GNUNET_OK != ret)
- {
- GNUNET_break_op (0);
- return ret;
- }
- /* check exchange signature on the coin */
- r_melt_detail->coin_info.denom_sig = sig;
- r_melt_detail->coin_info.denom_pub = pk;
- if (GNUNET_OK !=
- TALER_test_coin_valid (&r_melt_detail->coin_info))
- {
- GNUNET_break_op (0);
- GNUNET_JSON_parse_free (spec);
- r_melt_detail->coin_info.denom_sig.rsa_signature = NULL;
- r_melt_detail->coin_info.denom_pub.rsa_public_key = NULL;
- return (MHD_YES ==
- TEH_RESPONSE_reply_signature_invalid (connection,
- TALER_EC_REFRESH_MELT_DENOMINATION_SIGNATURE_INVALID,
- "denom_sig"))
- ? GNUNET_NO : GNUNET_SYSERR;
- }
- r_melt_detail->melt_sig = melt_sig;
- r_melt_detail->melt_amount_with_fee = amount;
- return GNUNET_OK;
-}
-
-
-/**
- * Release memory from the @a commit_coin array.
- *
- * @param commit_coin array to release
- * @param kappa size of 1st dimension
- * @param num_new_coins size of 2nd dimension
- */
-static void
-free_commit_coins (struct TALER_EXCHANGEDB_RefreshCommitCoin **commit_coin,
- unsigned int kappa,
- unsigned int num_new_coins)
-{
- for (unsigned int i=0;i<kappa;i++)
- {
- if (NULL == commit_coin[i])
- break;
- for (unsigned int j=0;j<num_new_coins;j++)
- GNUNET_free_non_null (commit_coin[i][j].coin_ev);
- GNUNET_free (commit_coin[i]);
- commit_coin[i] = NULL;
- }
-}
-
-
-/**
- * Cleanup state kept in the @a rmc.
- *
- * @param rmc state to clean up; does not free @a rmc itself
- */
-static void
-cleanup_rmc (struct RefreshMeltContext *rmc)
-{
- free_commit_coins (rmc->commit_coin,
- TALER_CNC_KAPPA,
- rmc->num_newcoins);
- if (NULL != rmc->coin_melt_details.coin_info.denom_pub.rsa_public_key)
- {
- GNUNET_CRYPTO_rsa_public_key_free (rmc->coin_melt_details.coin_info.denom_pub.rsa_public_key);
- rmc->coin_melt_details.coin_info.denom_pub.rsa_public_key = NULL;
- }
- if (NULL != rmc->coin_melt_details.coin_info.denom_sig.rsa_signature)
- {
- GNUNET_CRYPTO_rsa_signature_free (rmc->coin_melt_details.coin_info.denom_sig.rsa_signature);
- rmc->coin_melt_details.coin_info.denom_sig.rsa_signature = NULL;
- }
- if (NULL != rmc->denom_pubs)
- {
- for (unsigned int j=0;j<rmc->num_newcoins;j++)
- if (NULL != rmc->denom_pubs[j].rsa_public_key)
- GNUNET_CRYPTO_rsa_public_key_free (rmc->denom_pubs[j].rsa_public_key);
- GNUNET_free (rmc->denom_pubs);
- rmc->denom_pubs = NULL;
- }
- if (NULL != rmc->hash_context)
- {
- GNUNET_CRYPTO_hash_context_abort (rmc->hash_context);
- rmc->hash_context = NULL;
- }
- if (NULL != rmc->key_state)
- {
- TEH_KS_release (rmc->key_state);
- rmc->key_state = NULL;
- }
-}
-
-
-/**
* Handle a "/refresh/melt" request after the first parsing has
* happened. We now need to validate the coins being melted and the
* session signature and then hand things of to execute the melt
@@ -629,223 +310,70 @@ cleanup_rmc (struct RefreshMeltContext *rmc)
* processing on to #handle_refresh_melt_binary().
*
* @param connection the MHD connection to handle
- * @param new_denoms array of denomination keys
- * @param melt_coin coin to melt
- * @param transfer_pubs #TALER_CNC_KAPPA-dimensional array of transfer keys
- * @param coin_evs #TALER_CNC_KAPPA-dimensional array of envelopes to sign
+ * @param[in,out] rmc details about the melt request
* @return MHD result code
*/
static int
-handle_refresh_melt_json (struct MHD_Connection *connection,
- const json_t *new_denoms,
- const json_t *melt_coin,
- const json_t *transfer_pubs,
- const json_t *coin_evs)
+handle_refresh_melt (struct MHD_Connection *connection,
+ struct RefreshMeltContext *rmc)
{
- int res;
- int mhd_ret;
- struct RefreshMeltContext rmc;
-
- memset (&rmc,
- 0,
- sizeof (rmc));
- /* For the signature check, we hash most of the inputs together
- (except for the signatures on the coins). */
- rmc.hash_context = GNUNET_CRYPTO_hash_context_start ();
- for (unsigned int i = 0; i < TALER_CNC_KAPPA; i++)
- {
- struct GNUNET_JSON_Specification trans_spec[] = {
- GNUNET_JSON_spec_fixed_auto (NULL, &rmc.transfer_pub[i]),
- GNUNET_JSON_spec_end ()
- };
-
- res = TEH_PARSE_json_array (connection,
- transfer_pubs,
- trans_spec,
- i, -1);
- if (GNUNET_OK != res)
- {
- GNUNET_break_op (0);
- mhd_ret = (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
- cleanup_rmc (&rmc);
- return mhd_ret;
- }
- GNUNET_CRYPTO_hash_context_read (rmc.hash_context,
- &rmc.transfer_pub[i],
- sizeof (struct TALER_TransferPublicKeyP));
- }
-
- rmc.num_newcoins = json_array_size (new_denoms);
- rmc.denom_pubs = GNUNET_new_array (rmc.num_newcoins,
- struct TALER_DenominationPublicKey);
- for (unsigned int i=0;i<rmc.num_newcoins;i++)
- {
- char *buf;
- size_t buf_size;
- struct GNUNET_JSON_Specification spec[] = {
- TALER_JSON_spec_denomination_public_key (NULL,
- &rmc.denom_pubs[i]),
- GNUNET_JSON_spec_end ()
- };
-
- res = TEH_PARSE_json_array (connection,
- new_denoms,
- spec,
- i,
- -1);
- if (GNUNET_OK != res)
- {
- mhd_ret = (GNUNET_NO == res) ? MHD_YES : MHD_NO;
- cleanup_rmc (&rmc);
- return mhd_ret;
- }
- buf_size = GNUNET_CRYPTO_rsa_public_key_encode (rmc.denom_pubs[i].rsa_public_key,
- &buf);
- GNUNET_CRYPTO_hash_context_read (rmc.hash_context,
- buf,
- buf_size);
- GNUNET_free (buf);
- }
-
- /* decode JSON data on coin to melt and check that this is a
- valid coin */
+ /* sanity-check that "total melt amount > melt fee" */
{
- struct TALER_AmountNBO melt_amount;
+ struct TALER_Amount fee_refresh;
- res = get_coin_public_info (connection,
- melt_coin,
- &rmc.coin_melt_details);
- if (GNUNET_OK != res)
+ TALER_amount_ntoh (&fee_refresh,
+ &rmc->dki->issue.properties.fee_refresh);
+ if (TALER_amount_cmp (&fee_refresh,
+ &rmc->refresh_session.amount_with_fee) > 0)
{
GNUNET_break_op (0);
- mhd_ret = (GNUNET_NO == res) ? MHD_YES : MHD_NO;
- cleanup_rmc (&rmc);
- return mhd_ret;
- }
- TALER_amount_hton (&melt_amount,
- &rmc.coin_melt_details.melt_amount_with_fee);
- GNUNET_CRYPTO_hash_context_read (rmc.hash_context,
- &rmc.coin_melt_details.coin_info.coin_pub,
- sizeof (struct TALER_CoinSpendPublicKeyP));
- GNUNET_CRYPTO_hash_context_read (rmc.hash_context,
- &melt_amount,
- sizeof (struct TALER_AmountNBO));
- }
-
- /* parse JSON arrays into binary arrays and hash everything
- together for the signature check */
- for (unsigned int i = 0; i < TALER_CNC_KAPPA; i++)
- {
- rmc.commit_coin[i] = GNUNET_new_array (rmc.num_newcoins,
- struct TALER_EXCHANGEDB_RefreshCommitCoin);
- for (unsigned int j = 0; j < rmc.num_newcoins; j++)
- {
- struct TALER_EXCHANGEDB_RefreshCommitCoin *rcc = &rmc.commit_coin[i][j];
- struct GNUNET_JSON_Specification coin_spec[] = {
- GNUNET_JSON_spec_varsize (NULL,
- (void **) &rcc->coin_ev,
- &rcc->coin_ev_size),
- GNUNET_JSON_spec_end ()
- };
-
- res = TEH_PARSE_json_array (connection,
- coin_evs,
- coin_spec,
- i,
- j,
- -1);
- if (GNUNET_OK != res)
- {
- GNUNET_break_op (0);
- mhd_ret = (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
- cleanup_rmc (&rmc);
- return mhd_ret;
- }
-
- GNUNET_CRYPTO_hash_context_read (rmc.hash_context,
- rcc->coin_ev,
- rcc->coin_ev_size);
+ return TEH_RESPONSE_reply_external_error (connection,
+ TALER_EC_REFRESH_MELT_FEES_EXCEED_CONTRIBUTION,
+ "melt amount smaller than melting fee");
}
}
- GNUNET_CRYPTO_hash_context_finish (rmc.hash_context,
- &rmc.session_hash);
- rmc.hash_context = NULL;
-
- rmc.key_state = TEH_KS_acquire ();
- if (NULL == rmc.key_state)
- {
- TALER_LOG_ERROR ("Lacking keys to operate\n");
- return TEH_RESPONSE_reply_internal_error (connection,
- TALER_EC_EXCHANGE_BAD_CONFIGURATION,
- "no keys");
- }
- rmc.dki = TEH_KS_denomination_key_lookup (rmc.key_state,
- &rmc.coin_melt_details.coin_info.denom_pub,
- TEH_KS_DKU_DEPOSIT);
- if (NULL == rmc.dki)
- {
- TEH_KS_release (rmc.key_state);
- TALER_LOG_WARNING ("Unknown denomination key in /refresh/melt request\n");
- return TEH_RESPONSE_reply_arg_unknown (connection,
- TALER_EC_REFRESH_MELT_DENOMINATION_KEY_NOT_FOUND,
- "denom_pub");
- }
-
/* verify signature of coin for melt operation */
{
struct TALER_RefreshMeltCoinAffirmationPS body;
- struct TALER_Amount fee_refresh;
- TALER_amount_ntoh (&fee_refresh,
- &rmc.dki->issue.properties.fee_refresh);
- rmc.coin_melt_details.melt_fee = fee_refresh;
body.purpose.size = htonl (sizeof (struct TALER_RefreshMeltCoinAffirmationPS));
body.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_MELT);
- body.session_hash = rmc.session_hash;
+ body.rc = rmc->refresh_session.rc;
TALER_amount_hton (&body.amount_with_fee,
- &rmc.coin_melt_details.melt_amount_with_fee);
- TALER_amount_hton (&body.melt_fee,
- &fee_refresh);
- body.coin_pub = rmc.coin_melt_details.coin_info.coin_pub;
- if (TALER_amount_cmp (&fee_refresh,
- &rmc.coin_melt_details.melt_amount_with_fee) > 0)
- {
- GNUNET_break_op (0);
- cleanup_rmc (&rmc);
- return TEH_RESPONSE_reply_external_error (connection,
- TALER_EC_REFRESH_MELT_AMOUNT_INSUFFICIENT,
- "melt amount smaller than melting fee");
- }
+ &rmc->refresh_session.amount_with_fee);
+ body.melt_fee = rmc->dki->issue.properties.fee_refresh;
+ body.coin_pub = rmc->refresh_session.coin.coin_pub;
if (GNUNET_OK !=
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_MELT,
&body.purpose,
- &rmc.coin_melt_details.melt_sig.eddsa_signature,
- &rmc.coin_melt_details.coin_info.coin_pub.eddsa_pub))
+ &rmc->refresh_session.coin_sig.eddsa_signature,
+ &rmc->refresh_session.coin.coin_pub.eddsa_pub))
{
GNUNET_break_op (0);
- cleanup_rmc (&rmc);
return TEH_RESPONSE_reply_signature_invalid (connection,
TALER_EC_REFRESH_MELT_COIN_SIGNATURE_INVALID,
"confirm_sig");
}
}
- /* prepare commit */
- if (GNUNET_OK !=
- refresh_melt_prepare (connection,
- &mhd_ret,
- &rmc))
+ /* run transaction */
{
- cleanup_rmc (&rmc);
- return mhd_ret;
+ int mhd_ret;
+
+ if (GNUNET_OK !=
+ TEH_DB_run_transaction (connection,
+ &mhd_ret,
+ &refresh_melt_transaction,
+ rmc))
+ return mhd_ret;
}
- mhd_ret = reply_refresh_melt_success (connection,
- &rmc.session_hash,
- rmc.refresh_session.noreveal_index);
- cleanup_rmc (&rmc);
- return mhd_ret;
+
+ /* generate ordinary response */
+ return reply_refresh_melt_success (connection,
+ &rmc->refresh_session.rc,
+ rmc->refresh_session.noreveal_index);
}
@@ -870,16 +398,22 @@ TEH_REFRESH_handler_refresh_melt (struct TEH_RequestHandler *rh,
size_t *upload_data_size)
{
json_t *root;
- json_t *new_denoms;
- json_t *melt_coin;
- json_t *coin_evs;
- json_t *transfer_pubs;
+ struct RefreshMeltContext rmc;
int res;
+ struct TEH_KS_StateHandle *key_state;
struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_json ("new_denoms", &new_denoms),
- GNUNET_JSON_spec_json ("melt_coin", &melt_coin),
- GNUNET_JSON_spec_json ("coin_evs", &coin_evs),
- GNUNET_JSON_spec_json ("transfer_pubs", &transfer_pubs),
+ GNUNET_JSON_spec_fixed_auto ("coin_pub",
+ &rmc.refresh_session.coin.coin_pub),
+ TALER_JSON_spec_denomination_signature ("denom_sig",
+ &rmc.refresh_session.coin.denom_sig),
+ TALER_JSON_spec_denomination_public_key ("denom_pub",
+ &rmc.refresh_session.coin.denom_pub),
+ GNUNET_JSON_spec_fixed_auto ("confirm_sig",
+ &rmc.refresh_session.coin_sig),
+ TALER_JSON_spec_amount ("value_with_fee",
+ &rmc.refresh_session.amount_with_fee),
+ GNUNET_JSON_spec_fixed_auto ("rc",
+ &rmc.refresh_session.rc),
GNUNET_JSON_spec_end ()
};
@@ -894,6 +428,9 @@ TEH_REFRESH_handler_refresh_melt (struct TEH_RequestHandler *rh,
(NULL == root) )
return MHD_YES;
+ memset (&rmc,
+ 0,
+ sizeof (rmc));
res = TEH_PARSE_json_data (connection,
root,
spec);
@@ -901,29 +438,60 @@ TEH_REFRESH_handler_refresh_melt (struct TEH_RequestHandler *rh,
if (GNUNET_OK != res)
return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
- /* Determine dimensionality of the request (kappa, #old and #new coins) */
- if (TALER_CNC_KAPPA != json_array_size (coin_evs))
+ if (GNUNET_OK !=
+ TALER_test_coin_valid (&rmc.refresh_session.coin))
{
GNUNET_break_op (0);
GNUNET_JSON_parse_free (spec);
- return TEH_RESPONSE_reply_arg_invalid (connection,
- TALER_EC_REFRESH_MELT_CNC_COIN_ARRAY_SIZE_INVALID,
- "coin_evs");
+ return TEH_RESPONSE_reply_signature_invalid (connection,
+ TALER_EC_REFRESH_MELT_DENOMINATION_SIGNATURE_INVALID,
+ "denom_sig");
}
- if (TALER_CNC_KAPPA != json_array_size (transfer_pubs))
+
+ /* run actual logic, now that the request was parsed */
+ key_state = TEH_KS_acquire ();
+ if (NULL == key_state)
{
- GNUNET_break_op (0);
- GNUNET_JSON_parse_free (spec);
- return TEH_RESPONSE_reply_arg_invalid (connection,
- TALER_EC_REFRESH_MELT_CNC_TRANSFER_ARRAY_SIZE_INVALID,
- "transfer_pubs");
+ TALER_LOG_ERROR ("Lacking keys to operate\n");
+ res = TEH_RESPONSE_reply_internal_error (connection,
+ TALER_EC_EXCHANGE_BAD_CONFIGURATION,
+ "no keys");
+ goto cleanup;
+ }
+ rmc.dki = TEH_KS_denomination_key_lookup (key_state,
+ &rmc.refresh_session.coin.denom_pub,
+ TEH_KS_DKU_DEPOSIT);
+ if (NULL == rmc.dki)
+ {
+ TALER_LOG_WARNING ("Unknown denomination key in /refresh/melt request\n");
+ res = TEH_RESPONSE_reply_arg_unknown (connection,
+ TALER_EC_REFRESH_MELT_DENOMINATION_KEY_NOT_FOUND,
+ "denom_pub");
+ goto cleanup;
+ }
+
+ res = handle_refresh_melt (connection,
+ &rmc);
+
+
+ cleanup:
+ if (NULL != key_state)
+ {
+ TEH_KS_release (key_state);
+ key_state = NULL;
+ }
+ if (NULL != rmc.refresh_session.coin.denom_pub.rsa_public_key)
+ {
+ GNUNET_CRYPTO_rsa_public_key_free (rmc.refresh_session.coin.denom_pub.rsa_public_key);
+ rmc.refresh_session.coin.denom_pub.rsa_public_key = NULL;
+ }
+ if (NULL != rmc.refresh_session.coin.denom_sig.rsa_signature)
+ {
+ GNUNET_CRYPTO_rsa_signature_free (rmc.refresh_session.coin.denom_sig.rsa_signature);
+ rmc.refresh_session.coin.denom_sig.rsa_signature = NULL;
}
- res = handle_refresh_melt_json (connection,
- new_denoms,
- melt_coin,
- transfer_pubs,
- coin_evs);
GNUNET_JSON_parse_free (spec);
+
return res;
}
diff --git a/src/exchange/taler-exchange-httpd_refresh_reveal.c b/src/exchange/taler-exchange-httpd_refresh_reveal.c
index a64ec1cee..4a7cd33db 100644
--- a/src/exchange/taler-exchange-httpd_refresh_reveal.c
+++ b/src/exchange/taler-exchange-httpd_refresh_reveal.c
@@ -32,6 +32,12 @@
/**
+ * Maximum number of fresh coins we allow per refresh operation.
+ */
+#define MAX_FRESH_COINS 256
+
+
+/**
* Send a response for "/refresh/reveal".
*
* @param connection the connection to send the response to
@@ -79,133 +85,19 @@ reply_refresh_reveal_success (struct MHD_Connection *connection,
* 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
+ * @param rc commitment computed by the exchange
* @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)
+ const struct TALER_RefreshCommitmentP *rc)
{
- json_t *info_new;
- json_t *info_commit_k;
-
- info_new = json_array ();
- info_commit_k = json_array ();
- for (unsigned int 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}",
+ "{s:s, s:I, s:o}",
"error", "commitment violation",
"code", (json_int_t) TALER_EC_REFRESH_REVEAL_COMMITMENT_VIOLATION,
- "coin_sig", GNUNET_JSON_from_data_auto (&session->melt.coin_sig),
- "coin_pub", GNUNET_JSON_from_data_auto (&session->melt.coin.coin_pub),
- "melt_amount_with_fee", TALER_JSON_from_amount (&session->melt.amount_with_fee),
- "melt_fee", TALER_JSON_from_amount (&session->melt.melt_fee),
- "newcoin_infos", info_new,
- "commit_infos", info_commit_k,
- "gamma_tp", GNUNET_JSON_from_data_auto (gamma_tp),
- "gamma", (int) session->noreveal_index);
-}
-
-
-/**
- * Check if the given @a transfer_privs correspond to an honest
- * commitment for the given session.
- * Checks that the transfer private keys match their commitments.
- * Then derives the shared secret for each #TALER_CNC_KAPPA, and check that they match.
- *
- * @param connection the MHD connection to handle
- * @param session database connection to use
- * @param session_hash hash of session to query
- * @param off commitment offset to check
- * @param transfer_priv private transfer key
- * @param melt information about the melted coin
- * @param num_newcoins number of newcoins being generated
- * @param denom_pubs array of @a num_newcoins keys for the new coins
- * @param hash_context hash context to update by hashing in the data
- * from this offset
- * @return #GNUNET_OK if the committment was honest,
- * #GNUNET_NO if there was a problem and we generated an error message
- * #GNUNET_SYSERR if we could not even generate an error message
- */
-static int
-check_commitment (struct MHD_Connection *connection,
- struct TALER_EXCHANGEDB_Session *session,
- const struct GNUNET_HashCode *session_hash,
- unsigned int off,
- const struct TALER_TransferPrivateKeyP *transfer_priv,
- const struct TALER_EXCHANGEDB_RefreshMelt *melt,
- unsigned int num_newcoins,
- const struct TALER_DenominationPublicKey *denom_pubs,
- struct GNUNET_HashContext *hash_context)
-{
- struct TALER_TransferSecretP transfer_secret;
-
- TALER_link_reveal_transfer_secret (transfer_priv,
- &melt->coin.coin_pub,
- &transfer_secret);
-
- /* Check that the commitments for all new coins were correct */
- for (unsigned int j = 0; j < num_newcoins; j++)
- {
- struct TALER_PlanchetSecretsP fc;
- struct TALER_CoinSpendPublicKeyP coin_pub;
- struct GNUNET_HashCode h_msg;
- char *buf;
- size_t buf_len;
-
- TALER_planchet_setup_refresh (&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;
+ "rc_expected", GNUNET_JSON_from_data_auto (rc));
}
@@ -216,169 +108,83 @@ struct RevealContext
{
/**
- * Hash of the refresh session.
+ * Commitment of the refresh operaton.
*/
- const struct GNUNET_HashCode *session_hash;
+ struct TALER_RefreshCommitmentP rc;
/**
- * Database session used to execute the transaction.
+ * Transfer public key at gamma.
*/
- struct TALER_EXCHANGEDB_Session *session;
-
- /**
- * Session state from the database.
- */
- struct TALER_EXCHANGEDB_RefreshSession refresh_session;
+ struct TALER_TransferPublicKeyP gamma_tp;
/**
- * Array of denomination public keys used for the refresh.
+ * Transfer private keys revealed to us.
*/
- struct TALER_DenominationPublicKey *denom_pubs;
+ struct TALER_TransferPrivateKeyP transfer_privs[TALER_CNC_KAPPA - 1];
/**
- * Envelopes with the signatures to be returned.
+ * Denominations being requested.
*/
- struct TALER_DenominationSignature *ev_sigs;
+ const struct TALER_EXCHANGEDB_DenominationKeyIssueInformation **dkis;
/**
- * Commitment data from the DB giving data about original
- * commitments, in particular the blinded envelopes (for
- * index gamma).
+ * Envelopes to be signed.
*/
- struct TALER_EXCHANGEDB_RefreshCommitCoin *commit_coins;
+ const struct TALER_RefreshCoinData *rcds;
/**
- * Transfer public key associated with the gamma value
- * selected by the exchange.
+ * Envelopes with the signatures to be returned. Initially NULL.
*/
- struct TALER_TransferPublicKeyP gamma_tp;
+ struct TALER_DenominationSignature *ev_sigs;
/**
- * Transfer private keys revealed to us.
+ * Size of the @e dkis, @e rcds and @e ev_sigs arrays (if non-NULL).
*/
- struct TALER_TransferPrivateKeyP transfer_privs[TALER_CNC_KAPPA - 1];
+ unsigned int num_fresh_coins;
};
/**
- * Exchange a coin as part of a refresh operation. Obtains the
- * envelope from the database and performs the signing operation.
+ * Function called with information about a refresh order we already
+ * persisted. Stores the result in @a cls so we don't do the calculation
+ * again.
*
- * @param connection the MHD connection to handle
- * @param session database connection to use
- * @param session_hash hash of session to query
- * @param key_state key state to lookup denomination pubs
- * @param denom_pub denomination key for the coin to create
- * @param commit_coin the coin that was committed
- * @param coin_off number of the coin
- * @param[out] ev_sig set to signature over the coin upon success
- * @return database transaction status
+ * @param cls closure with a `struct RevealContext`
+ * @param rowid unique serial ID for the row in our database
+ * @param num_newcoins size of the @a rrcs array
+ * @param rrcs array of @a num_newcoins information about coins to be created
+ * @param num_tprivs number of entries in @a tprivs, should be #TALER_CNC_KAPPA - 1
+ * @param tprivs array of @e num_tprivs transfer private keys
+ * @param tp transfer public key information
*/
-static enum GNUNET_DB_QueryStatus
-refresh_exchange_coin (struct MHD_Connection *connection,
- struct TALER_EXCHANGEDB_Session *session,
- const struct GNUNET_HashCode *session_hash,
- struct TEH_KS_StateHandle *key_state,
- const struct TALER_DenominationPublicKey *denom_pub,
- const struct TALER_EXCHANGEDB_RefreshCommitCoin *commit_coin,
- unsigned int coin_off,
- struct TALER_DenominationSignature *ev_sig)
+static void
+check_exists_cb (void *cls,
+ uint32_t num_newcoins,
+ const struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrcs,
+ unsigned int num_tprivs,
+ const struct TALER_TransferPrivateKeyP *tprivs,
+ const struct TALER_TransferPublicKeyP *tp)
{
- struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dki;
- enum GNUNET_DB_QueryStatus qs;
+ struct RevealContext *rctx = cls;
- dki = TEH_KS_denomination_key_lookup (key_state,
- denom_pub,
- TEH_KS_DKU_WITHDRAW);
- if (NULL == dki)
+ if (0 == num_newcoins)
{
GNUNET_break (0);
- ev_sig->rsa_signature = NULL;
- return GNUNET_DB_STATUS_HARD_ERROR;
- }
- qs = TEH_plugin->get_refresh_out (TEH_plugin->cls,
- session,
- session_hash,
- coin_off,
- ev_sig);
- if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Returning cached reply for /refresh/reveal signature\n");
- return qs;
- }
- if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != qs)
- return qs;
-
- ev_sig->rsa_signature
- = GNUNET_CRYPTO_rsa_sign_blinded (dki->denom_priv.rsa_private_key,
- commit_coin->coin_ev,
- commit_coin->coin_ev_size);
- if (NULL == ev_sig->rsa_signature)
- {
- GNUNET_break (0);
- return GNUNET_DB_STATUS_HARD_ERROR;
- }
- qs = TEH_plugin->insert_refresh_out (TEH_plugin->cls,
- session,
- session_hash,
- coin_off,
- ev_sig);
- if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
- {
- GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
- if (NULL != ev_sig->rsa_signature)
- {
- GNUNET_CRYPTO_rsa_signature_free (ev_sig->rsa_signature);
- ev_sig->rsa_signature = NULL;
- }
- }
- return qs;
-}
-
-
-/**
- * Cleanup state of the transaction stored in @a rc.
- *
- * @param rc context to clean up
- */
-static void
-cleanup_rc (struct RevealContext *rc)
-{
- if (NULL != rc->denom_pubs)
- {
- for (unsigned int i=0;i<rc->refresh_session.num_newcoins;i++)
- if (NULL != rc->denom_pubs[i].rsa_public_key)
- GNUNET_CRYPTO_rsa_public_key_free (rc->denom_pubs[i].rsa_public_key);
- GNUNET_free (rc->denom_pubs);
- rc->denom_pubs = NULL;
- }
- if (NULL != rc->commit_coins)
- {
- for (unsigned int j=0;j<rc->refresh_session.num_newcoins;j++)
- GNUNET_free_non_null (rc->commit_coins[j].coin_ev);
- GNUNET_free (rc->commit_coins);
- rc->commit_coins = NULL;
- }
- if (NULL != rc->ev_sigs)
- {
- for (unsigned int j=0;j<rc->refresh_session.num_newcoins;j++)
- if (NULL != rc->ev_sigs[j].rsa_signature)
- GNUNET_CRYPTO_rsa_signature_free (rc->ev_sigs[j].rsa_signature);
- GNUNET_free (rc->ev_sigs);
- rc->ev_sigs = NULL;
- }
- if (NULL != rc->refresh_session.melt.coin.denom_sig.rsa_signature)
- {
- GNUNET_CRYPTO_rsa_signature_free (rc->refresh_session.melt.coin.denom_sig.rsa_signature);
- rc->refresh_session.melt.coin.denom_sig.rsa_signature = NULL;
- }
- if (NULL != rc->refresh_session.melt.coin.denom_pub.rsa_public_key)
- {
- GNUNET_CRYPTO_rsa_public_key_free (rc->refresh_session.melt.coin.denom_pub.rsa_public_key);
- rc->refresh_session.melt.coin.denom_pub.rsa_public_key = NULL;
+ return;
}
+ GNUNET_break (TALER_CNC_KAPPA - 1 == num_tprivs);
+ GNUNET_break_op (0 == memcmp (tp,
+ &rctx->gamma_tp,
+ sizeof (struct TALER_TransferPublicKeyP)));
+ GNUNET_break_op (0 == memcmp (tprivs,
+ &rctx->transfer_privs,
+ sizeof (struct TALER_TransferPrivateKeyP) * num_tprivs));
+ rctx->ev_sigs = GNUNET_new_array (num_newcoins,
+ struct TALER_DenominationSignature);
+ for (unsigned int i=0;i<num_newcoins;i++)
+ rctx->ev_sigs[i].rsa_signature
+ = GNUNET_CRYPTO_rsa_signature_dup (rrcs[i].coin_sig.rsa_signature);
}
@@ -408,233 +214,230 @@ refresh_reveal_transaction (void *cls,
struct TALER_EXCHANGEDB_Session *session,
int *mhd_ret)
{
- struct RevealContext *rc = cls;
- unsigned int off;
- struct GNUNET_HashContext *hash_context;
- struct GNUNET_HashCode sh_check;
+ struct RevealContext *rctx = cls;
+ struct TALER_EXCHANGEDB_RefreshMelt refresh_melt;
enum GNUNET_DB_QueryStatus qs;
- rc->session = session;
- qs = TEH_plugin->get_refresh_session (TEH_plugin->cls,
- session,
- rc->session_hash,
- &rc->refresh_session);
+ /* Try to see if we already have given an answer before. */
+ qs = TEH_plugin->get_refresh_reveal (TEH_plugin->cls,
+ session,
+ &rctx->rc,
+ &check_exists_cb,
+ rctx);
+ switch (qs) {
+ case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
+ /* continue normal execution */
+ break;
+ case GNUNET_DB_STATUS_SOFT_ERROR:
+ return qs;
+ case GNUNET_DB_STATUS_HARD_ERROR:
+ GNUNET_break (qs);
+ *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+ TALER_EC_REFRESH_REVEAL_DB_FETCH_REVEAL_ERROR);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
+ default:
+ /* Hossa, already found our reply! */
+ GNUNET_assert (NULL != rctx->ev_sigs);
+ return qs;
+ }
+
+ /* Obtain basic information about the refresh operation and what
+ gamma we committed to. */
+ qs = TEH_plugin->get_melt (TEH_plugin->cls,
+ session,
+ &rctx->rc,
+ &refresh_melt);
if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
{
*mhd_ret = TEH_RESPONSE_reply_arg_invalid (connection,
TALER_EC_REFRESH_REVEAL_SESSION_UNKNOWN,
- "session_hash");
+ "rc");
return GNUNET_DB_STATUS_HARD_ERROR;
}
if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
return qs;
if ( (GNUNET_DB_STATUS_HARD_ERROR == qs) ||
- (rc->refresh_session.noreveal_index >= TALER_CNC_KAPPA) )
+ (refresh_melt.session.noreveal_index >= TALER_CNC_KAPPA) )
{
GNUNET_break (0);
- cleanup_rc (rc);
*mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
TALER_EC_REFRESH_REVEAL_DB_FETCH_SESSION_ERROR);
return GNUNET_DB_STATUS_HARD_ERROR;
}
- rc->denom_pubs = GNUNET_new_array (rc->refresh_session.num_newcoins,
- struct TALER_DenominationPublicKey);
- qs = TEH_plugin->get_refresh_order (TEH_plugin->cls,
- session,
- rc->session_hash,
- rc->refresh_session.num_newcoins,
- rc->denom_pubs);
- if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
- {
- cleanup_rc (rc);
- if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
- return qs;
- GNUNET_break (0);
- *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
- TALER_EC_REFRESH_REVEAL_DB_FETCH_ORDER_ERROR);
- return GNUNET_DB_STATUS_HARD_ERROR;
- }
- hash_context = GNUNET_CRYPTO_hash_context_start ();
- /* first, iterate over transfer public keys for hash_context */
- off = 0;
- for (unsigned int i=0;i<TALER_CNC_KAPPA;i++)
+ /* Verify commitment */
{
- if (i == rc->refresh_session.noreveal_index)
+ /* Note that the contents of rcs[refresh_melt.session.noreveal_index]
+ will be aliased and are *not* allocated (or deallocated) in
+ this function -- in contrast to the other offsets! */
+ struct TALER_RefreshCommitmentEntry rcs[TALER_CNC_KAPPA];
+ struct TALER_RefreshCommitmentP rc_expected;
+ unsigned int off;
+
+ off = 0; /* did we pass session.noreveal_index yet? */
+ for (unsigned int i=0;i<TALER_CNC_KAPPA;i++)
{
- off = 1;
- /* obtain gamma_tp from db */
- qs = TEH_plugin->get_refresh_transfer_public_key (TEH_plugin->cls,
- session,
- rc->session_hash,
- &rc->gamma_tp);
- if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
+ struct TALER_RefreshCommitmentEntry *rce = &rcs[i];
+
+ if (i == refresh_melt.session.noreveal_index)
+ {
+ /* Take these coin envelopes from the client */
+ rce->transfer_pub = rctx->gamma_tp;
+ rce->new_coins = (struct TALER_RefreshCoinData *) rctx->rcds;
+ off = 1;
+ }
+ else
{
- GNUNET_CRYPTO_hash_context_abort (hash_context);
- cleanup_rc (rc);
- if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
- return qs;
- GNUNET_break (0);
- *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
- TALER_EC_REFRESH_REVEAL_DB_FETCH_TRANSFER_ERROR);
- return GNUNET_DB_STATUS_HARD_ERROR;
+ /* Reconstruct coin envelopes from transfer private key */
+ struct TALER_TransferPrivateKeyP *tpriv = &rctx->transfer_privs[i - off];
+ struct TALER_TransferSecretP ts;
+
+ GNUNET_CRYPTO_ecdhe_key_get_public (&tpriv->ecdhe_priv,
+ &rce->transfer_pub.ecdhe_pub);
+ TALER_link_reveal_transfer_secret (tpriv,
+ &refresh_melt.session.coin.coin_pub,
+ &ts);
+ rce->new_coins = GNUNET_new_array (rctx->num_fresh_coins,
+ struct TALER_RefreshCoinData);
+ for (unsigned int j=0;j<rctx->num_fresh_coins;j++)
+ {
+ struct TALER_RefreshCoinData *rcd = &rce->new_coins[j];
+ struct TALER_PlanchetSecretsP ps;
+ struct TALER_PlanchetDetail pd;
+
+ rcd->dk = &rctx->dkis[j]->denom_pub;
+ TALER_planchet_setup_refresh (&ts,
+ j,
+ &ps);
+ TALER_planchet_prepare (rcd->dk,
+ &ps,
+ &pd);
+ rcd->coin_ev = pd.coin_ev;
+ rcd->coin_ev_size = pd.coin_ev_size;
+ }
}
- GNUNET_CRYPTO_hash_context_read (hash_context,
- &rc->gamma_tp,
- sizeof (struct TALER_TransferPublicKeyP));
}
- else
+ TALER_refresh_get_commitment (&rc_expected,
+ TALER_CNC_KAPPA,
+ rctx->num_fresh_coins,
+ rcs,
+ &refresh_melt.session.coin.coin_pub,
+ &refresh_melt.session.amount_with_fee);
+
+ /* Free resources allocated above */
+ for (unsigned int i=0;i<TALER_CNC_KAPPA;i++)
{
- /* compute tp from private key */
- struct TALER_TransferPublicKeyP tp;
-
- GNUNET_CRYPTO_ecdhe_key_get_public (&rc->transfer_privs[i - off].ecdhe_priv,
- &tp.ecdhe_pub);
- GNUNET_CRYPTO_hash_context_read (hash_context,
- &tp,
- sizeof (struct TALER_TransferPublicKeyP));
- }
- }
+ struct TALER_RefreshCommitmentEntry *rce = &rcs[i];
- /* next, add all of the hashes from the denomination keys to the
- hash_context */
- for (unsigned int i=0;i<rc->refresh_session.num_newcoins;i++)
- {
- char *buf;
- size_t buf_size;
-
- buf_size = GNUNET_CRYPTO_rsa_public_key_encode (rc->denom_pubs[i].rsa_public_key,
- &buf);
- GNUNET_CRYPTO_hash_context_read (hash_context,
- buf,
- buf_size);
- GNUNET_free (buf);
- }
+ if (i == refresh_melt.session.noreveal_index)
+ continue; /* This offset is special... */
+ for (unsigned int j=0;j<rctx->num_fresh_coins;j++)
+ {
+ struct TALER_RefreshCoinData *rcd = &rce->new_coins[j];
- /* next, add public key of coin and amount being refreshed */
- {
- struct TALER_AmountNBO melt_amountn;
-
- GNUNET_CRYPTO_hash_context_read (hash_context,
- &rc->refresh_session.melt.coin.coin_pub,
- sizeof (struct TALER_CoinSpendPublicKeyP));
- TALER_amount_hton (&melt_amountn,
- &rc->refresh_session.melt.amount_with_fee);
- GNUNET_CRYPTO_hash_context_read (hash_context,
- &melt_amountn,
- sizeof (struct TALER_AmountNBO));
- }
+ GNUNET_free (rcd->coin_ev);
+ }
+ GNUNET_free (rce->new_coins);
+ }
+
+ /* Verify rc_expected matches rc */
+ if (0 != memcmp (&rctx->rc,
+ &rc_expected,
+ sizeof (struct TALER_RefreshCommitmentP)))
+ {
+ GNUNET_break_op (0);
+ *mhd_ret = reply_refresh_reveal_missmatch (connection,
+ &rc_expected);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+ } /* end of checking "rc_expected" */
- rc->commit_coins = GNUNET_new_array (rc->refresh_session.num_newcoins,
- struct TALER_EXCHANGEDB_RefreshCommitCoin);
- off = 0;
- for (unsigned int i=0;i<TALER_CNC_KAPPA;i++)
+ /* check amounts add up! */
{
- int res;
+ struct TALER_Amount refresh_cost;
- if (i == rc->refresh_session.noreveal_index)
+ refresh_cost = refresh_melt.melt_fee;
+ for (unsigned int i=0;i<rctx->num_fresh_coins;i++)
{
- off = 1;
- /* obtain commit_coins for the selected gamma value from DB */
- qs = TEH_plugin->get_refresh_commit_coins (TEH_plugin->cls,
- session,
- rc->session_hash,
- rc->refresh_session.num_newcoins,
- rc->commit_coins);
- if (0 >= qs)
- {
- cleanup_rc (rc);
- GNUNET_CRYPTO_hash_context_abort (hash_context);
- if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
- return qs;
- GNUNET_break (0);
- *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
- TALER_EC_REFRESH_REVEAL_DB_FETCH_COMMIT_ERROR);
- return GNUNET_DB_STATUS_HARD_ERROR;
- }
- /* add envelopes to hash_context */
- for (unsigned int j=0;j<rc->refresh_session.num_newcoins;j++)
+ struct TALER_Amount fee_withdraw;
+ struct TALER_Amount value;
+ struct TALER_Amount total;
+
+ TALER_amount_ntoh (&fee_withdraw,
+ &rctx->dkis[i]->issue.properties.fee_withdraw);
+ TALER_amount_ntoh (&value,
+ &rctx->dkis[i]->issue.properties.value);
+ if ( (GNUNET_OK !=
+ TALER_amount_add (&total,
+ &fee_withdraw,
+ &value)) ||
+ (GNUNET_OK !=
+ TALER_amount_add (&refresh_cost,
+ &refresh_cost,
+ &total)) )
{
- GNUNET_CRYPTO_hash_context_read (hash_context,
- rc->commit_coins[j].coin_ev,
- rc->commit_coins[j].coin_ev_size);
+ GNUNET_break_op (0);
+ *mhd_ret = TEH_RESPONSE_reply_internal_error (connection,
+ TALER_EC_REFRESH_REVEAL_COST_CALCULATION_OVERFLOW,
+ "failed to add up refresh costs");
+ return GNUNET_DB_STATUS_HARD_ERROR;
}
- continue;
}
- if (GNUNET_OK !=
- (res = check_commitment (connection,
- session,
- rc->session_hash,
- i,
- &rc->transfer_privs[i - off],
- &rc->refresh_session.melt,
- rc->refresh_session.num_newcoins,
- rc->denom_pubs,
- hash_context)))
+ if (0 < TALER_amount_cmp (&refresh_cost,
+ &refresh_melt.session.amount_with_fee))
{
GNUNET_break_op (0);
- cleanup_rc (rc);
- GNUNET_CRYPTO_hash_context_abort (hash_context);
- *mhd_ret = (GNUNET_NO == res) ? MHD_YES : MHD_NO;
+ *mhd_ret = TEH_RESPONSE_reply_external_error (connection,
+ TALER_EC_REFRESH_REVEAL_AMOUNT_INSUFFICIENT,
+ "melted coin value is insufficient to cover cost of operation");
return GNUNET_DB_STATUS_HARD_ERROR;
}
}
- /* Check session hash matches */
- GNUNET_CRYPTO_hash_context_finish (hash_context,
- &sh_check);
- if (0 != memcmp (&sh_check,
- rc->session_hash,
- sizeof (struct GNUNET_HashCode)))
- {
- GNUNET_break_op (0);
- *mhd_ret = reply_refresh_reveal_missmatch (connection,
- &rc->refresh_session,
- rc->commit_coins,
- rc->denom_pubs,
- &rc->gamma_tp);
- cleanup_rc (rc);
- return GNUNET_DB_STATUS_HARD_ERROR;
- }
-
/* Client request OK, sign coins */
- rc->ev_sigs = GNUNET_new_array (rc->refresh_session.num_newcoins,
- struct TALER_DenominationSignature);
+ rctx->ev_sigs = GNUNET_new_array (rctx->num_fresh_coins,
+ struct TALER_DenominationSignature);
+ for (unsigned int i=0;i<rctx->num_fresh_coins;i++)
{
- struct TEH_KS_StateHandle *key_state;
-
- key_state = TEH_KS_acquire ();
- if (NULL == key_state)
+ rctx->ev_sigs[i].rsa_signature
+ = GNUNET_CRYPTO_rsa_sign_blinded (rctx->dkis[i]->denom_priv.rsa_private_key,
+ rctx->rcds[i].coin_ev,
+ rctx->rcds[i].coin_ev_size);
+ if (NULL == rctx->ev_sigs[i].rsa_signature)
{
- TALER_LOG_ERROR ("Lacking keys to operate\n");
- cleanup_rc (rc);
+ *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+ TALER_EC_REFRESH_REVEAL_SIGNING_ERROR);
return GNUNET_DB_STATUS_HARD_ERROR;
}
- for (unsigned int j=0;j<rc->refresh_session.num_newcoins;j++)
+ }
+
+ /* Persist operation result in DB */
+ {
+ struct TALER_EXCHANGEDB_RefreshRevealedCoin rrcs[rctx->num_fresh_coins];
+
+ for (unsigned int i=0;i<rctx->num_fresh_coins;i++)
{
- qs = refresh_exchange_coin (connection,
- session,
- rc->session_hash,
- key_state,
- &rc->denom_pubs[j],
- &rc->commit_coins[j],
- j,
- &rc->ev_sigs[j]);
- if ( (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) ||
- (NULL == rc->ev_sigs[j].rsa_signature) )
- {
- *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
- TALER_EC_REFRESH_REVEAL_SIGNING_ERROR);
- qs = GNUNET_DB_STATUS_HARD_ERROR;
- break;
- }
+ struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrc = &rrcs[i];
+
+ rrc->denom_pub = rctx->dkis[i]->denom_pub;
+ rrc->coin_ev = rctx->rcds[i].coin_ev;
+ rrc->coin_ev_size = rctx->rcds[i].coin_ev_size;
+ rrc->coin_sig = rctx->ev_sigs[i];
}
- TEH_KS_release (key_state);
+ qs = TEH_plugin->insert_refresh_reveal (TEH_plugin->cls,
+ session,
+ &rctx->rc,
+ rctx->num_fresh_coins,
+ rrcs,
+ TALER_CNC_KAPPA - 1,
+ rctx->transfer_privs,
+ &rctx->gamma_tp);
}
- if (0 >= qs)
+ if (GNUNET_DB_STATUS_HARD_ERROR == qs)
{
- cleanup_rc (rc);
- return qs;
+ *mhd_ret = TEH_RESPONSE_reply_internal_db_error (connection,
+ TALER_EC_REFRESH_REVEAL_DB_COMMIT_ERROR);
}
return qs;
}
@@ -648,59 +451,171 @@ refresh_reveal_transaction (void *cls,
* coins.
*
* @param connection the MHD connection to handle
- * @param session_hash hash identifying the melting session
+ * @param rctx context for the operation, partially built at this time
+ * @param transfer_pub transfer public key
* @param tp_json private transfer keys in JSON format
+ * @param new_denoms_h_json requests for fresh coins to be created
+ * @param coin_evs envelopes of gamma-selected coins to be signed
* @return MHD result code
- */
+ */
static int
handle_refresh_reveal_json (struct MHD_Connection *connection,
- const struct GNUNET_HashCode *session_hash,
- const json_t *tp_json)
+ struct RevealContext *rctx,
+ const json_t *tp_json,
+ const json_t *new_denoms_h_json,
+ const json_t *coin_evs)
{
- struct RevealContext rc;
- int mhd_ret;
-
- GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "reveal request for session %s\n",
- GNUNET_h2s (session_hash));
- memset (&rc,
- 0,
- sizeof (rc));
- rc.session_hash = session_hash;
- for (unsigned int i = 0; i < TALER_CNC_KAPPA - 1; i++)
+ unsigned int num_fresh_coins = json_array_size (new_denoms_h_json);
+ unsigned int num_tprivs = json_array_size (tp_json);
+ struct TEH_KS_StateHandle *key_state;
+
+ GNUNET_assert (num_tprivs == TALER_CNC_KAPPA - 1);
+ if ( (num_fresh_coins >= MAX_FRESH_COINS) ||
+ (0 == num_fresh_coins) )
+ {
+ GNUNET_break_op (0);
+ return TEH_RESPONSE_reply_arg_invalid (connection,
+ TALER_EC_REFRESH_REVEAL_NEW_DENOMS_ARRAY_SIZE_EXCESSIVE,
+ "new_denoms");
+
+ }
+ if (json_array_size (new_denoms_h_json) !=
+ json_array_size (coin_evs))
+ {
+ GNUNET_break_op (0);
+ return TEH_RESPONSE_reply_arg_invalid (connection,
+ TALER_EC_REFRESH_REVEAL_NEW_DENOMS_ARRAY_SIZE_MISSMATCH,
+ "new_denoms/coin_evs");
+ }
+
+ /* Parse transfer private keys array */
+ for (unsigned int i=0;i<num_tprivs;i++)
{
- struct GNUNET_JSON_Specification tp_spec[] = {
- GNUNET_JSON_spec_fixed_auto (NULL, &rc.transfer_privs[i]),
+ struct GNUNET_JSON_Specification trans_spec[] = {
+ GNUNET_JSON_spec_fixed_auto (NULL, &rctx->transfer_privs[i]),
GNUNET_JSON_spec_end ()
};
int res;
res = TEH_PARSE_json_array (connection,
tp_json,
- tp_spec,
+ trans_spec,
i,
- -1);
- GNUNET_break_op (GNUNET_OK == res);
+ -1);
if (GNUNET_OK != res)
- return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
+ return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
}
- if (GNUNET_OK !=
- TEH_DB_run_transaction (connection,
- &mhd_ret,
- &refresh_reveal_transaction,
- &rc))
+
+ /* Resolve denomination hashes */
{
- cleanup_rc (&rc);
- return mhd_ret;
+ const struct TALER_EXCHANGEDB_DenominationKeyIssueInformation *dkis[num_fresh_coins];
+ struct TALER_RefreshCoinData rcds[num_fresh_coins];
+ int res;
+
+ /* Resolve denomination hashes */
+ key_state = TEH_KS_acquire ();
+ if (NULL == key_state)
+ {
+ TALER_LOG_ERROR ("Lacking keys to operate\n");
+ /* FIXME: use correct EC code! */
+ return TEH_RESPONSE_reply_internal_error (connection,
+ TALER_EC_REFRESH_REVEAL_SIGNING_ERROR,
+ "exchange lacks keys");
+ }
+
+ /* Parse denomination key hashes */
+ for (unsigned int i=0;i<num_fresh_coins;i++)
+ {
+ struct GNUNET_HashCode dpk_h;
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_fixed_auto (NULL,
+ &dpk_h),
+ GNUNET_JSON_spec_end ()
+ };
+
+ res = TEH_PARSE_json_array (connection,
+ new_denoms_h_json,
+ spec,
+ i,
+ -1);
+ if (GNUNET_OK != res)
+ {
+ TEH_KS_release (key_state);
+ return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
+ }
+ dkis[i] = TEH_KS_denomination_key_lookup_by_hash (key_state,
+ &dpk_h,
+ TEH_KS_DKU_WITHDRAW);
+ if (NULL == dkis[i])
+ {
+ TEH_KS_release (key_state);
+ return TEH_RESPONSE_reply_arg_invalid (connection,
+ TALER_EC_REFRESH_REVEAL_FRESH_DENOMINATION_KEY_NOT_FOUND,
+ "new_denoms");
+ }
+ }
+
+ /* Parse coin envelopes */
+ for (unsigned int i=0;i<num_fresh_coins;i++)
+ {
+ struct TALER_RefreshCoinData *rcd = &rcds[i];
+ struct GNUNET_JSON_Specification spec[] = {
+ GNUNET_JSON_spec_varsize (NULL,
+ (void **) &rcd->coin_ev,
+ &rcd->coin_ev_size),
+ GNUNET_JSON_spec_end ()
+ };
+
+ res = TEH_PARSE_json_array (connection,
+ coin_evs,
+ spec,
+ i,
+ -1);
+ if (GNUNET_OK != res)
+ {
+ for (unsigned int j=0;j<i;j++)
+ GNUNET_free_non_null (rcds[j].coin_ev);
+ TEH_KS_release (key_state);
+ return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
+ }
+ rcd->dk = &dkis[i]->denom_pub;
+ }
+
+ rctx->num_fresh_coins = num_fresh_coins;
+ rctx->rcds = rcds;
+ rctx->dkis = dkis;
+ /* do transactional work */
+ if (GNUNET_OK ==
+ TEH_DB_run_transaction (connection,
+ &res,
+ &refresh_reveal_transaction,
+ rctx))
+ {
+ /* Generate final (positive) response */
+ GNUNET_assert (NULL != rctx->ev_sigs);
+ res = reply_refresh_reveal_success (connection,
+ num_fresh_coins,
+ rctx->ev_sigs);
+
+ }
+
+ /* free resources */
+ if (NULL != rctx->ev_sigs)
+ {
+ for (unsigned int i=0;i<num_fresh_coins;i++)
+ if (NULL != rctx->ev_sigs[i].rsa_signature)
+ GNUNET_CRYPTO_rsa_signature_free (rctx->ev_sigs[i].rsa_signature);
+ GNUNET_free (rctx->ev_sigs);
+ }
+ for (unsigned int i=0;i<num_fresh_coins;i++)
+ GNUNET_free_non_null (rcds[i].coin_ev);
+ TEH_KS_release (key_state);
+ return res;
}
- mhd_ret = reply_refresh_reveal_success (connection,
- rc.refresh_session.num_newcoins,
- rc.ev_sigs);
- cleanup_rc (&rc);
- return mhd_ret;
}
+
/**
* Handle a "/refresh/reveal" request. This time, the client reveals
* the private transfer keys except for the cut-and-choose value
@@ -724,13 +639,18 @@ TEH_REFRESH_handler_refresh_reveal (struct TEH_RequestHandler *rh,
const char *upload_data,
size_t *upload_data_size)
{
- struct GNUNET_HashCode session_hash;
int res;
json_t *root;
+ json_t *coin_evs;
json_t *transfer_privs;
+ json_t *new_denoms_h;
+ struct RevealContext rctx;
struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_fixed_auto ("session_hash", &session_hash),
+ GNUNET_JSON_spec_fixed_auto ("rc", &rctx.rc),
+ GNUNET_JSON_spec_fixed_auto ("transfer_pub", &rctx.gamma_tp),
GNUNET_JSON_spec_json ("transfer_privs", &transfer_privs),
+ GNUNET_JSON_spec_json ("coin_evs", &coin_evs),
+ GNUNET_JSON_spec_json ("new_denoms_h", &new_denoms_h),
GNUNET_JSON_spec_end ()
};
@@ -745,6 +665,9 @@ TEH_REFRESH_handler_refresh_reveal (struct TEH_RequestHandler *rh,
(NULL == root) )
return MHD_YES;
+ memset (&rctx,
+ 0,
+ sizeof (rctx));
res = TEH_PARSE_json_data (connection,
root,
spec);
@@ -754,7 +677,8 @@ TEH_REFRESH_handler_refresh_reveal (struct TEH_RequestHandler *rh,
GNUNET_break_op (0);
return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
}
- /* Determine dimensionality of the request (kappa and #old coins) */
+
+ /* Check we got enough transfer private keys */
/* Note we do +1 as 1 row (cut-and-choose!) is missing! */
if (TALER_CNC_KAPPA != json_array_size (transfer_privs) + 1)
{
@@ -765,8 +689,10 @@ TEH_REFRESH_handler_refresh_reveal (struct TEH_RequestHandler *rh,
"transfer_privs");
}
res = handle_refresh_reveal_json (connection,
- &session_hash,
- transfer_privs);
+ &rctx,
+ transfer_privs,
+ new_denoms_h,
+ coin_evs);
GNUNET_JSON_parse_free (spec);
return res;
}
diff --git a/src/exchange/taler-exchange-httpd_responses.c b/src/exchange/taler-exchange-httpd_responses.c
index ac86416f3..64e7a9367 100644
--- a/src/exchange/taler-exchange-httpd_responses.c
+++ b/src/exchange/taler-exchange-httpd_responses.c
@@ -541,18 +541,18 @@ TEH_RESPONSE_compile_transaction_history (const struct TALER_EXCHANGEDB_Transact
ms.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_MELT);
ms.purpose.size = htonl (sizeof (struct TALER_RefreshMeltCoinAffirmationPS));
- ms.session_hash = melt->session_hash;
+ ms.rc = melt->session.rc;
TALER_amount_hton (&ms.amount_with_fee,
- &melt->amount_with_fee);
+ &melt->session.amount_with_fee);
TALER_amount_hton (&ms.melt_fee,
&melt->melt_fee);
- ms.coin_pub = melt->coin.coin_pub;
+ ms.coin_pub = melt->session.coin.coin_pub;
/* internal sanity check before we hand out a bogus sig... */
if (GNUNET_OK !=
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_MELT,
&ms.purpose,
- &melt->coin_sig.eddsa_signature,
- &melt->coin.coin_pub.eddsa_pub))
+ &melt->session.coin_sig.eddsa_signature,
+ &melt->session.coin.coin_pub.eddsa_pub))
{
GNUNET_break (0);
json_decref (history);
@@ -563,10 +563,10 @@ TEH_RESPONSE_compile_transaction_history (const struct TALER_EXCHANGEDB_Transact
json_array_append_new (history,
json_pack ("{s:s, s:o, s:o, s:o, s:o}",
"type", "MELT",
- "amount", TALER_JSON_from_amount (&melt->amount_with_fee),
+ "amount", TALER_JSON_from_amount (&melt->session.amount_with_fee),
"melt_fee", TALER_JSON_from_amount (&melt->melt_fee),
- "session_hash", GNUNET_JSON_from_data_auto (&melt->session_hash),
- "coin_sig", GNUNET_JSON_from_data_auto (&melt->coin_sig))));
+ "rc", GNUNET_JSON_from_data_auto (&melt->session.rc),
+ "coin_sig", GNUNET_JSON_from_data_auto (&melt->session.coin_sig))));
}
break;
case TALER_EXCHANGEDB_TT_REFUND:
@@ -812,7 +812,7 @@ TEH_RESPONSE_compile_reserve_history (const struct TALER_EXCHANGEDB_ReserveHisto
return NULL;
}
- GNUNET_assert (0 ==
+ GNUNET_assert (0 ==
json_array_append_new (json_history,
json_pack ("{s:s, s:o, s:o, s:o, s:o, s:o}",
"type", "PAYBACK",
@@ -847,7 +847,7 @@ TEH_RESPONSE_compile_reserve_history (const struct TALER_EXCHANGEDB_ReserveHisto
}
}
ret |= 2;
- rcc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_RESERVE_CLOSED);
+ rcc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_RESERVE_CLOSED);
rcc.purpose.size = htonl (sizeof (struct TALER_ReserveCloseConfirmationPS));
rcc.timestamp = GNUNET_TIME_absolute_hton (pos->details.closing->execution_date);
TALER_amount_hton (&rcc.closing_amount,