retry transactions on serialization/dead-lock failures (#3990)
This commit is contained in:
parent
70c28e53d0
commit
93a84d5e5a
@ -629,7 +629,8 @@ struct TALER_MINTDB_Plugin
|
||||
*
|
||||
* @param cls the @e cls of this struct with the plugin-specific state
|
||||
* @param session connection to use
|
||||
* @return #GNUNET_OK on success
|
||||
* @return #GNUNET_OK on success, #GNUNET_NO if the transaction
|
||||
* can be retried, #GNUNET_SYSERR on hard failures
|
||||
*/
|
||||
int
|
||||
(*commit) (void *cls,
|
||||
|
@ -24,6 +24,67 @@
|
||||
#include "taler-mint-httpd_responses.h"
|
||||
#include "taler-mint-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 #TMH_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; \
|
||||
int transaction_commit_result; \
|
||||
transaction_start_label: /* we will use goto for retries */ \
|
||||
if (GNUNET_OK != \
|
||||
TMH_plugin->start (TMH_plugin->cls, \
|
||||
session)) \
|
||||
{ \
|
||||
GNUNET_break (0); \
|
||||
return TMH_RESPONSE_reply_internal_db_error (connection); \
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
* #TMH_RESPONSE_reply_internal_db_error() status code.
|
||||
*
|
||||
* @param session session handle
|
||||
* @param connection connection handle
|
||||
*/
|
||||
#define COMMIT_TRANSACTION(session,connection) \
|
||||
transaction_commit_result = \
|
||||
TMH_plugin->commit (TMH_plugin->cls, \
|
||||
session); \
|
||||
if (GNUNET_SYSERR == transaction_commit_result) \
|
||||
{ \
|
||||
TALER_LOG_WARNING ("Transaction commit failed in %s\n", __FUNCTION__); \
|
||||
return TMH_RESPONSE_reply_commit_error (connection); \
|
||||
} \
|
||||
if (GNUNET_NO == 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 TMH_RESPONSE_reply_commit_error (connection); \
|
||||
} \
|
||||
} /* end of scope opened by BEGIN_TRANSACTION */
|
||||
|
||||
|
||||
/**
|
||||
* Calculate the total value of all transactions performed.
|
||||
@ -130,13 +191,8 @@ TMH_DB_execute_deposit (struct MHD_Connection *connection,
|
||||
&dki->issue.properties.value);
|
||||
TMH_KS_release (mks);
|
||||
|
||||
if (GNUNET_OK !=
|
||||
TMH_plugin->start (TMH_plugin->cls,
|
||||
session))
|
||||
{
|
||||
GNUNET_break (0);
|
||||
return TMH_RESPONSE_reply_internal_db_error (connection);
|
||||
}
|
||||
START_TRANSACTION (session, connection);
|
||||
|
||||
/* fee for THIS transaction */
|
||||
spent = deposit->amount_with_fee;
|
||||
/* add cost of all previous transactions */
|
||||
@ -179,13 +235,7 @@ TMH_DB_execute_deposit (struct MHD_Connection *connection,
|
||||
return TMH_RESPONSE_reply_internal_db_error (connection);
|
||||
}
|
||||
|
||||
if (GNUNET_OK !=
|
||||
TMH_plugin->commit (TMH_plugin->cls,
|
||||
session))
|
||||
{
|
||||
TALER_LOG_WARNING ("/deposit transaction commit failed\n");
|
||||
return TMH_RESPONSE_reply_commit_error (connection);
|
||||
}
|
||||
COMMIT_TRANSACTION(session, connection);
|
||||
GNUNET_assert (GNUNET_OK ==
|
||||
TALER_amount_subtract (&amount_without_fee,
|
||||
&deposit->amount_with_fee,
|
||||
@ -241,6 +291,187 @@ TMH_DB_execute_reserve_status (struct MHD_Connection *connection,
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Try to execute /reserve/withdraw transaction.
|
||||
*
|
||||
* @param connection request we are handling
|
||||
* @param session database session we are using
|
||||
* @param reserve reserve to withdraw from
|
||||
* @param denomination_pub public key of the denomination requested
|
||||
* @param dki denomination to withdraw
|
||||
* @param blinded_msg blinded message to be signed
|
||||
* @param blinded_msg_len number of bytes in @a blinded_msg
|
||||
* @param signature signature over the withdraw request, to be stored in DB
|
||||
* @param denom_sig[out] where to write the resulting signature
|
||||
* (used to release memory in case of transaction failure
|
||||
* @return MHD result code
|
||||
*/
|
||||
static int
|
||||
execute_reserve_withdraw_transaction (struct MHD_Connection *connection,
|
||||
struct TALER_MINTDB_Session *session,
|
||||
struct TMH_KS_StateHandle *key_state,
|
||||
const struct TALER_ReservePublicKeyP *reserve,
|
||||
const struct TALER_DenominationPublicKey *denomination_pub,
|
||||
const struct TALER_MINTDB_DenominationKeyIssueInformation *dki,
|
||||
const char *blinded_msg,
|
||||
size_t blinded_msg_len,
|
||||
const struct TALER_ReserveSignatureP *signature,
|
||||
struct TALER_DenominationSignature *denom_sig)
|
||||
{
|
||||
struct TALER_MINTDB_ReserveHistory *rh;
|
||||
const struct TALER_MINTDB_ReserveHistory *pos;
|
||||
struct TALER_MINTDB_DenominationKeyIssueInformation *tdki;
|
||||
struct TALER_MINTDB_CollectableBlindcoin collectable;
|
||||
struct TALER_Amount amount_required;
|
||||
struct TALER_Amount deposit_total;
|
||||
struct TALER_Amount withdraw_total;
|
||||
struct TALER_Amount balance;
|
||||
struct TALER_Amount value;
|
||||
struct TALER_Amount fee_withdraw;
|
||||
struct GNUNET_HashCode h_blind;
|
||||
int res;
|
||||
|
||||
/* Check if balance is sufficient */
|
||||
START_TRANSACTION (session, connection);
|
||||
rh = TMH_plugin->get_reserve_history (TMH_plugin->cls,
|
||||
session,
|
||||
reserve);
|
||||
if (NULL == rh)
|
||||
{
|
||||
TMH_plugin->rollback (TMH_plugin->cls,
|
||||
session);
|
||||
return TMH_RESPONSE_reply_arg_unknown (connection,
|
||||
"reserve_pub");
|
||||
}
|
||||
|
||||
/* calculate amount required including fees */
|
||||
TALER_amount_ntoh (&value,
|
||||
&dki->issue.properties.value);
|
||||
TALER_amount_ntoh (&fee_withdraw,
|
||||
&dki->issue.properties.fee_withdraw);
|
||||
|
||||
if (GNUNET_OK !=
|
||||
TALER_amount_add (&amount_required,
|
||||
&value,
|
||||
&fee_withdraw))
|
||||
{
|
||||
TMH_plugin->rollback (TMH_plugin->cls,
|
||||
session);
|
||||
return TMH_RESPONSE_reply_internal_db_error (connection);
|
||||
}
|
||||
|
||||
/* calculate balance of the reserve */
|
||||
res = 0;
|
||||
for (pos = rh; NULL != pos; pos = pos->next)
|
||||
{
|
||||
switch (pos->type)
|
||||
{
|
||||
case TALER_MINTDB_RO_BANK_TO_MINT:
|
||||
if (0 == (res & 1))
|
||||
deposit_total = pos->details.bank->amount;
|
||||
else
|
||||
if (GNUNET_OK !=
|
||||
TALER_amount_add (&deposit_total,
|
||||
&deposit_total,
|
||||
&pos->details.bank->amount))
|
||||
{
|
||||
TMH_plugin->rollback (TMH_plugin->cls,
|
||||
session);
|
||||
return TMH_RESPONSE_reply_internal_db_error (connection);
|
||||
}
|
||||
res |= 1;
|
||||
break;
|
||||
case TALER_MINTDB_RO_WITHDRAW_COIN:
|
||||
tdki = TMH_KS_denomination_key_lookup (key_state,
|
||||
&pos->details.withdraw->denom_pub,
|
||||
TMH_KS_DKU_WITHDRAW);
|
||||
TALER_amount_ntoh (&value,
|
||||
&tdki->issue.properties.value);
|
||||
if (0 == (res & 2))
|
||||
withdraw_total = value;
|
||||
else
|
||||
if (GNUNET_OK !=
|
||||
TALER_amount_add (&withdraw_total,
|
||||
&withdraw_total,
|
||||
&value))
|
||||
{
|
||||
TMH_plugin->rollback (TMH_plugin->cls,
|
||||
session);
|
||||
return TMH_RESPONSE_reply_internal_db_error (connection);
|
||||
}
|
||||
res |= 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (0 == (res & 1))
|
||||
{
|
||||
/* did not encounter any deposit operations, how can we have a reserve? */
|
||||
GNUNET_break (0);
|
||||
return TMH_RESPONSE_reply_internal_db_error (connection);
|
||||
}
|
||||
if (0 == (res & 2))
|
||||
{
|
||||
/* did not encounter any withdraw operations, set to zero */
|
||||
TALER_amount_get_zero (deposit_total.currency,
|
||||
&withdraw_total);
|
||||
}
|
||||
/* All reserve balances should be non-negative */
|
||||
GNUNET_assert (GNUNET_SYSERR !=
|
||||
TALER_amount_subtract (&balance,
|
||||
&deposit_total,
|
||||
&withdraw_total));
|
||||
if (0 < TALER_amount_cmp (&amount_required,
|
||||
&balance))
|
||||
{
|
||||
TMH_plugin->rollback (TMH_plugin->cls,
|
||||
session);
|
||||
res = TMH_RESPONSE_reply_reserve_withdraw_insufficient_funds (connection,
|
||||
rh);
|
||||
TMH_plugin->free_reserve_history (TMH_plugin->cls,
|
||||
rh);
|
||||
return res;
|
||||
}
|
||||
TMH_plugin->free_reserve_history (TMH_plugin->cls,
|
||||
rh);
|
||||
|
||||
/* Balance is good, sign the coin! */
|
||||
denom_sig->rsa_signature
|
||||
= GNUNET_CRYPTO_rsa_sign (dki->denom_priv.rsa_private_key,
|
||||
blinded_msg,
|
||||
blinded_msg_len);
|
||||
if (NULL == denom_sig->rsa_signature)
|
||||
{
|
||||
GNUNET_break (0);
|
||||
TMH_plugin->rollback (TMH_plugin->cls,
|
||||
session);
|
||||
return TMH_RESPONSE_reply_internal_error (connection,
|
||||
"Internal error");
|
||||
}
|
||||
collectable.sig = *denom_sig;
|
||||
collectable.denom_pub = *denomination_pub;
|
||||
collectable.amount_with_fee = amount_required;
|
||||
collectable.withdraw_fee = fee_withdraw;
|
||||
collectable.reserve_pub = *reserve;
|
||||
collectable.h_coin_envelope = h_blind;
|
||||
collectable.reserve_sig = *signature;
|
||||
if (GNUNET_OK !=
|
||||
TMH_plugin->insert_withdraw_info (TMH_plugin->cls,
|
||||
session,
|
||||
&collectable))
|
||||
{
|
||||
GNUNET_break (0);
|
||||
TMH_plugin->rollback (TMH_plugin->cls,
|
||||
session);
|
||||
return TMH_RESPONSE_reply_internal_db_error (connection);
|
||||
}
|
||||
COMMIT_TRANSACTION (session, connection);
|
||||
|
||||
return TMH_RESPONSE_reply_reserve_withdraw_success (connection,
|
||||
&collectable);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Execute a "/reserve/withdraw". Given a reserve and a properly signed
|
||||
* request to withdraw a coin, check the balance of the reserve and
|
||||
@ -264,19 +495,10 @@ TMH_DB_execute_reserve_withdraw (struct MHD_Connection *connection,
|
||||
const struct TALER_ReserveSignatureP *signature)
|
||||
{
|
||||
struct TALER_MINTDB_Session *session;
|
||||
struct TALER_MINTDB_ReserveHistory *rh;
|
||||
const struct TALER_MINTDB_ReserveHistory *pos;
|
||||
struct TMH_KS_StateHandle *key_state;
|
||||
struct TALER_MINTDB_CollectableBlindcoin collectable;
|
||||
struct TALER_MINTDB_DenominationKeyIssueInformation *dki;
|
||||
struct TALER_MINTDB_DenominationKeyIssueInformation *tdki;
|
||||
struct GNUNET_CRYPTO_rsa_Signature *sig;
|
||||
struct TALER_Amount amount_required;
|
||||
struct TALER_Amount deposit_total;
|
||||
struct TALER_Amount withdraw_total;
|
||||
struct TALER_Amount balance;
|
||||
struct TALER_Amount value;
|
||||
struct TALER_Amount fee_withdraw;
|
||||
struct TALER_MINTDB_CollectableBlindcoin collectable;
|
||||
struct TALER_DenominationSignature denom_sig;
|
||||
struct GNUNET_HashCode h_blind;
|
||||
int res;
|
||||
|
||||
@ -311,7 +533,6 @@ TMH_DB_execute_reserve_withdraw (struct MHD_Connection *connection,
|
||||
}
|
||||
GNUNET_assert (GNUNET_NO == res);
|
||||
|
||||
/* Check if balance is sufficient */
|
||||
key_state = TMH_KS_acquire ();
|
||||
dki = TMH_KS_denomination_key_lookup (key_state,
|
||||
denomination_pub,
|
||||
@ -325,162 +546,19 @@ TMH_DB_execute_reserve_withdraw (struct MHD_Connection *connection,
|
||||
"error",
|
||||
"Denomination not found");
|
||||
}
|
||||
if (GNUNET_OK !=
|
||||
TMH_plugin->start (TMH_plugin->cls,
|
||||
session))
|
||||
{
|
||||
GNUNET_break (0);
|
||||
TMH_KS_release (key_state);
|
||||
return TMH_RESPONSE_reply_internal_db_error (connection);
|
||||
}
|
||||
|
||||
rh = TMH_plugin->get_reserve_history (TMH_plugin->cls,
|
||||
denom_sig.rsa_signature = NULL;
|
||||
res = execute_reserve_withdraw_transaction (connection,
|
||||
session,
|
||||
reserve);
|
||||
if (NULL == rh)
|
||||
{
|
||||
TMH_plugin->rollback (TMH_plugin->cls,
|
||||
session);
|
||||
TMH_KS_release (key_state);
|
||||
return TMH_RESPONSE_reply_arg_unknown (connection,
|
||||
"reserve_pub");
|
||||
}
|
||||
|
||||
/* calculate amount required including fees */
|
||||
TALER_amount_ntoh (&value,
|
||||
&dki->issue.properties.value);
|
||||
TALER_amount_ntoh (&fee_withdraw,
|
||||
&dki->issue.properties.fee_withdraw);
|
||||
|
||||
if (GNUNET_OK !=
|
||||
TALER_amount_add (&amount_required,
|
||||
&value,
|
||||
&fee_withdraw))
|
||||
{
|
||||
TMH_plugin->rollback (TMH_plugin->cls,
|
||||
session);
|
||||
TMH_KS_release (key_state);
|
||||
return TMH_RESPONSE_reply_internal_db_error (connection);
|
||||
}
|
||||
|
||||
/* calculate balance of the reserve */
|
||||
res = 0;
|
||||
for (pos = rh; NULL != pos; pos = pos->next)
|
||||
{
|
||||
switch (pos->type)
|
||||
{
|
||||
case TALER_MINTDB_RO_BANK_TO_MINT:
|
||||
if (0 == (res & 1))
|
||||
deposit_total = pos->details.bank->amount;
|
||||
else
|
||||
if (GNUNET_OK !=
|
||||
TALER_amount_add (&deposit_total,
|
||||
&deposit_total,
|
||||
&pos->details.bank->amount))
|
||||
{
|
||||
TMH_plugin->rollback (TMH_plugin->cls,
|
||||
session);
|
||||
TMH_KS_release (key_state);
|
||||
return TMH_RESPONSE_reply_internal_db_error (connection);
|
||||
}
|
||||
res |= 1;
|
||||
break;
|
||||
case TALER_MINTDB_RO_WITHDRAW_COIN:
|
||||
tdki = TMH_KS_denomination_key_lookup (key_state,
|
||||
&pos->details.withdraw->denom_pub,
|
||||
TMH_KS_DKU_WITHDRAW);
|
||||
TALER_amount_ntoh (&value,
|
||||
&tdki->issue.properties.value);
|
||||
if (0 == (res & 2))
|
||||
withdraw_total = value;
|
||||
else
|
||||
if (GNUNET_OK !=
|
||||
TALER_amount_add (&withdraw_total,
|
||||
&withdraw_total,
|
||||
&value))
|
||||
{
|
||||
TMH_plugin->rollback (TMH_plugin->cls,
|
||||
session);
|
||||
TMH_KS_release (key_state);
|
||||
return TMH_RESPONSE_reply_internal_db_error (connection);
|
||||
}
|
||||
res |= 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (0 == (res & 1))
|
||||
{
|
||||
/* did not encounter any deposit operations, how can we have a reserve? */
|
||||
GNUNET_break (0);
|
||||
return TMH_RESPONSE_reply_internal_db_error (connection);
|
||||
}
|
||||
if (0 == (res & 2))
|
||||
{
|
||||
/* did not encounter any withdraw operations, set to zero */
|
||||
TALER_amount_get_zero (deposit_total.currency,
|
||||
&withdraw_total);
|
||||
}
|
||||
/* All reserve balances should be non-negative */
|
||||
GNUNET_assert (GNUNET_SYSERR !=
|
||||
TALER_amount_subtract (&balance,
|
||||
&deposit_total,
|
||||
&withdraw_total));
|
||||
if (0 < TALER_amount_cmp (&amount_required,
|
||||
&balance))
|
||||
{
|
||||
TMH_KS_release (key_state);
|
||||
TMH_plugin->rollback (TMH_plugin->cls,
|
||||
session);
|
||||
res = TMH_RESPONSE_reply_reserve_withdraw_insufficient_funds (connection,
|
||||
rh);
|
||||
TMH_plugin->free_reserve_history (TMH_plugin->cls,
|
||||
rh);
|
||||
return res;
|
||||
}
|
||||
TMH_plugin->free_reserve_history (TMH_plugin->cls,
|
||||
rh);
|
||||
|
||||
/* Balance is good, sign the coin! */
|
||||
sig = GNUNET_CRYPTO_rsa_sign (dki->denom_priv.rsa_private_key,
|
||||
key_state,
|
||||
reserve,
|
||||
denomination_pub,
|
||||
dki,
|
||||
blinded_msg,
|
||||
blinded_msg_len);
|
||||
blinded_msg_len,
|
||||
signature,
|
||||
&denom_sig);
|
||||
GNUNET_CRYPTO_rsa_signature_free (denom_sig.rsa_signature);
|
||||
TMH_KS_release (key_state);
|
||||
if (NULL == sig)
|
||||
{
|
||||
GNUNET_break (0);
|
||||
TMH_plugin->rollback (TMH_plugin->cls,
|
||||
session);
|
||||
return TMH_RESPONSE_reply_internal_error (connection,
|
||||
"Internal error");
|
||||
}
|
||||
collectable.sig.rsa_signature = sig;
|
||||
collectable.denom_pub = *denomination_pub;
|
||||
collectable.amount_with_fee = amount_required;
|
||||
collectable.withdraw_fee = fee_withdraw;
|
||||
collectable.reserve_pub = *reserve;
|
||||
collectable.h_coin_envelope = h_blind;
|
||||
collectable.reserve_sig = *signature;
|
||||
if (GNUNET_OK !=
|
||||
TMH_plugin->insert_withdraw_info (TMH_plugin->cls,
|
||||
session,
|
||||
&collectable))
|
||||
{
|
||||
GNUNET_break (0);
|
||||
GNUNET_CRYPTO_rsa_signature_free (sig);
|
||||
TMH_plugin->rollback (TMH_plugin->cls,
|
||||
session);
|
||||
return TMH_RESPONSE_reply_internal_db_error (connection);
|
||||
}
|
||||
if (GNUNET_OK !=
|
||||
TMH_plugin->commit (TMH_plugin->cls,
|
||||
session))
|
||||
{
|
||||
TALER_LOG_WARNING ("/reserve/withdraw transaction commit failed\n");
|
||||
return TMH_RESPONSE_reply_commit_error (connection);
|
||||
}
|
||||
res = TMH_RESPONSE_reply_reserve_withdraw_success (connection,
|
||||
&collectable);
|
||||
GNUNET_CRYPTO_rsa_signature_free (sig);
|
||||
return res;
|
||||
}
|
||||
|
||||
@ -633,13 +711,7 @@ TMH_DB_execute_refresh_melt (struct MHD_Connection *connection,
|
||||
GNUNET_break (0);
|
||||
return TMH_RESPONSE_reply_internal_db_error (connection);
|
||||
}
|
||||
if (GNUNET_OK !=
|
||||
TMH_plugin->start (TMH_plugin->cls,
|
||||
session))
|
||||
{
|
||||
GNUNET_break (0);
|
||||
return TMH_RESPONSE_reply_internal_db_error (connection);
|
||||
}
|
||||
START_TRANSACTION (session, connection);
|
||||
res = TMH_plugin->get_refresh_session (TMH_plugin->cls,
|
||||
session,
|
||||
session_hash,
|
||||
@ -741,13 +813,7 @@ TMH_DB_execute_refresh_melt (struct MHD_Connection *connection,
|
||||
}
|
||||
}
|
||||
|
||||
if (GNUNET_OK !=
|
||||
TMH_plugin->commit (TMH_plugin->cls,
|
||||
session))
|
||||
{
|
||||
TALER_LOG_WARNING ("/refresh/melt transaction commit failed\n");
|
||||
return TMH_RESPONSE_reply_commit_error (connection);
|
||||
}
|
||||
COMMIT_TRANSACTION (session, connection);
|
||||
return TMH_RESPONSE_reply_refresh_melt_success (connection,
|
||||
session_hash,
|
||||
refresh_session.noreveal_index);
|
||||
@ -1051,6 +1117,74 @@ refresh_mint_coin (struct MHD_Connection *connection,
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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 ev_sigs[out] where to store generated signatures for the new coins,
|
||||
* array of length "num_newcoins", memory released by the
|
||||
* caller
|
||||
* @param commit_coins[out] 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_MINTDB_Session *session,
|
||||
const struct GNUNET_HashCode *session_hash,
|
||||
struct TALER_MINTDB_RefreshSession *refresh_session,
|
||||
struct TALER_MINTDB_RefreshMelt *melts,
|
||||
struct TALER_DenominationPublicKey *denom_pubs,
|
||||
struct TALER_DenominationSignature *ev_sigs,
|
||||
struct TALER_MINTDB_RefreshCommitCoin *commit_coins)
|
||||
{
|
||||
unsigned int j;
|
||||
struct TMH_KS_StateHandle *key_state;
|
||||
|
||||
START_TRANSACTION (session, connection);
|
||||
if (GNUNET_OK !=
|
||||
TMH_plugin->get_refresh_commit_coins (TMH_plugin->cls,
|
||||
session,
|
||||
session_hash,
|
||||
refresh_session->noreveal_index,
|
||||
refresh_session->num_newcoins,
|
||||
commit_coins))
|
||||
{
|
||||
GNUNET_break (0);
|
||||
return TMH_RESPONSE_reply_internal_db_error (connection);
|
||||
}
|
||||
key_state = TMH_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_mint_coin (connection,
|
||||
session,
|
||||
session_hash,
|
||||
key_state,
|
||||
&denom_pubs[j],
|
||||
&commit_coins[j],
|
||||
j);
|
||||
if (NULL == ev_sigs[j].rsa_signature)
|
||||
{
|
||||
TMH_KS_release (key_state);
|
||||
return TMH_RESPONSE_reply_internal_db_error (connection);
|
||||
}
|
||||
}
|
||||
TMH_KS_release (key_state);
|
||||
COMMIT_TRANSACTION (session, connection);
|
||||
return TMH_RESPONSE_reply_refresh_reveal_success (connection,
|
||||
refresh_session->num_newcoins,
|
||||
ev_sigs);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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
|
||||
@ -1074,7 +1208,6 @@ TMH_DB_execute_refresh_reveal (struct MHD_Connection *connection,
|
||||
int res;
|
||||
struct TALER_MINTDB_Session *session;
|
||||
struct TALER_MINTDB_RefreshSession refresh_session;
|
||||
struct TMH_KS_StateHandle *key_state;
|
||||
struct TALER_MINTDB_RefreshMelt *melts;
|
||||
struct TALER_DenominationPublicKey *denom_pubs;
|
||||
struct TALER_DenominationSignature *ev_sigs;
|
||||
@ -1164,82 +1297,27 @@ TMH_DB_execute_refresh_reveal (struct MHD_Connection *connection,
|
||||
GNUNET_free (melts);
|
||||
|
||||
/* Client request OK, start transaction */
|
||||
if (GNUNET_OK !=
|
||||
TMH_plugin->start (TMH_plugin->cls,
|
||||
session))
|
||||
{
|
||||
GNUNET_break (0);
|
||||
for (j=0;j<refresh_session.num_newcoins;j++)
|
||||
GNUNET_CRYPTO_rsa_public_key_free (denom_pubs[j].rsa_public_key);
|
||||
GNUNET_free (denom_pubs);
|
||||
return TMH_RESPONSE_reply_internal_db_error (connection);
|
||||
}
|
||||
|
||||
commit_coins = GNUNET_malloc (refresh_session.num_newcoins *
|
||||
sizeof (struct TALER_MINTDB_RefreshCommitCoin));
|
||||
if (GNUNET_OK !=
|
||||
TMH_plugin->get_refresh_commit_coins (TMH_plugin->cls,
|
||||
session,
|
||||
session_hash,
|
||||
refresh_session.noreveal_index,
|
||||
refresh_session.num_newcoins,
|
||||
commit_coins))
|
||||
{
|
||||
GNUNET_break (0);
|
||||
GNUNET_free (commit_coins);
|
||||
for (j=0;j<refresh_session.num_newcoins;j++)
|
||||
GNUNET_CRYPTO_rsa_public_key_free (denom_pubs[j].rsa_public_key);
|
||||
GNUNET_free (denom_pubs);
|
||||
return TMH_RESPONSE_reply_internal_db_error (connection);
|
||||
}
|
||||
ev_sigs = GNUNET_malloc (refresh_session.num_newcoins *
|
||||
sizeof (struct TALER_DenominationSignature));
|
||||
key_state = TMH_KS_acquire ();
|
||||
for (j=0;j<refresh_session.num_newcoins;j++)
|
||||
{
|
||||
ev_sigs[j] = refresh_mint_coin (connection,
|
||||
res = execute_refresh_reveal_transaction (connection,
|
||||
session,
|
||||
session_hash,
|
||||
key_state,
|
||||
&denom_pubs[j],
|
||||
&commit_coins[j],
|
||||
j);
|
||||
if (NULL == ev_sigs[j].rsa_signature)
|
||||
{
|
||||
TMH_KS_release (key_state);
|
||||
for (i=0;i<j;i++)
|
||||
&refresh_session,
|
||||
melts,
|
||||
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 (ev_sigs);
|
||||
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_free (ev_sigs);
|
||||
GNUNET_free (denom_pubs);
|
||||
GNUNET_free (commit_coins);
|
||||
return TMH_RESPONSE_reply_internal_db_error (connection);
|
||||
}
|
||||
}
|
||||
TMH_KS_release (key_state);
|
||||
for (j=0;j<refresh_session.num_newcoins;j++)
|
||||
GNUNET_CRYPTO_rsa_public_key_free (denom_pubs[j].rsa_public_key);
|
||||
GNUNET_free (denom_pubs);
|
||||
GNUNET_free (commit_coins);
|
||||
|
||||
if (GNUNET_OK !=
|
||||
TMH_plugin->commit (TMH_plugin->cls,
|
||||
session))
|
||||
{
|
||||
TALER_LOG_WARNING ("/refresh/reveal transaction commit failed\n");
|
||||
for (i=0;i<refresh_session.num_newcoins;i++)
|
||||
GNUNET_CRYPTO_rsa_signature_free (ev_sigs[i].rsa_signature);
|
||||
GNUNET_free (ev_sigs);
|
||||
return TMH_RESPONSE_reply_commit_error (connection);
|
||||
}
|
||||
|
||||
res = TMH_RESPONSE_reply_refresh_reveal_success (connection,
|
||||
refresh_session.num_newcoins,
|
||||
ev_sigs);
|
||||
for (i=0;i<refresh_session.num_newcoins;i++)
|
||||
GNUNET_CRYPTO_rsa_signature_free (ev_sigs[i].rsa_signature);
|
||||
GNUNET_free (ev_sigs);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
33
src/mint/test_taler_mint_httpd_afl.sh
Executable file
33
src/mint/test_taler_mint_httpd_afl.sh
Executable file
@ -0,0 +1,33 @@
|
||||
#!/bin/bash
|
||||
#
|
||||
# This file is part of TALER
|
||||
# Copyright (C) 2015 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, If not, see <http://www.gnu.org/licenses/>
|
||||
#
|
||||
#
|
||||
# This script uses 'curl' to POST various ill-formed requests to the
|
||||
# taler-mint-httpd. Basically, the goal is to make sure that the
|
||||
# HTTP server survives (and produces the 'correct' error code).
|
||||
#
|
||||
# We read the JSON snippets from afl-tests/
|
||||
#
|
||||
# Setup keys.
|
||||
taler-mint-keyup -d test-mint-home -m test-mint-home/master.priv
|
||||
# Run test...
|
||||
for n in afl-tests/*.req
|
||||
do
|
||||
echo -n "Test $n"
|
||||
taler-mint-httpd -d test-mint-home/ -t 1 -f $n -C > /dev/null || { echo "FAIL!"; exit 1; }
|
||||
echo "OK"
|
||||
done
|
||||
exit 0
|
@ -1095,10 +1095,34 @@ postgres_commit (void *cls,
|
||||
if (PGRES_COMMAND_OK !=
|
||||
PQresultStatus (result))
|
||||
{
|
||||
const char *sqlstate;
|
||||
|
||||
sqlstate = PQresultErrorField (result,
|
||||
PG_DIAG_SQLSTATE);
|
||||
if (NULL == sqlstate)
|
||||
{
|
||||
/* very unexpected... */
|
||||
GNUNET_break (0);
|
||||
PQclear (result);
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
/* 40P01: deadlock, 40001: serialization failure */
|
||||
if ( (0 == strcmp (sqlstate,
|
||||
"40P01")) ||
|
||||
(0 == strcmp (sqlstate,
|
||||
"40001")) )
|
||||
{
|
||||
/* These two can be retried and have a fair chance of working
|
||||
the next time */
|
||||
PQclear (result);
|
||||
return GNUNET_NO;
|
||||
}
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"Database commit failure: %s\n",
|
||||
sqlstate);
|
||||
PQclear (result);
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
PQclear (result);
|
||||
return GNUNET_OK;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user