-make batch withdraw requests idempotent
This commit is contained in:
parent
7bd1828482
commit
b9d0b1aae4
@ -273,58 +273,88 @@ batch_withdraw_transaction (void *cls,
|
|||||||
return GNUNET_DB_STATUS_HARD_ERROR;
|
return GNUNET_DB_STATUS_HARD_ERROR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
|
return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates our final (successful) response.
|
||||||
|
*
|
||||||
|
* @param rc request context
|
||||||
|
* @param wc operation context
|
||||||
|
* @return MHD queue status
|
||||||
|
*/
|
||||||
|
static MHD_RESULT
|
||||||
|
generate_reply_success (const struct TEH_RequestContext *rc,
|
||||||
|
const struct BatchWithdrawContext *wc)
|
||||||
|
{
|
||||||
|
json_t *sigs;
|
||||||
|
|
||||||
|
sigs = json_array ();
|
||||||
|
GNUNET_assert (NULL != sigs);
|
||||||
|
for (unsigned int i = 0; i<wc->planchets_length; i++)
|
||||||
|
{
|
||||||
|
struct PlanchetContext *pc = &wc->planchets[i];
|
||||||
|
|
||||||
|
GNUNET_assert (
|
||||||
|
0 ==
|
||||||
|
json_array_append_new (
|
||||||
|
sigs,
|
||||||
|
GNUNET_JSON_PACK (
|
||||||
|
TALER_JSON_pack_blinded_denom_sig (
|
||||||
|
"ev_sig",
|
||||||
|
&pc->collectable.sig))));
|
||||||
|
}
|
||||||
|
TEH_METRICS_batch_withdraw_num_coins += wc->planchets_length;
|
||||||
|
return TALER_MHD_REPLY_JSON_PACK (
|
||||||
|
rc->connection,
|
||||||
|
MHD_HTTP_OK,
|
||||||
|
GNUNET_JSON_pack_array_steal ("ev_sigs",
|
||||||
|
sigs));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if the @a rc is replayed and we already have an
|
* Check if the @a rc is replayed and we already have an
|
||||||
* answer. If so, replay the existing answer and return the
|
* answer. If so, replay the existing answer and return the
|
||||||
* HTTP response.
|
* HTTP response.
|
||||||
*
|
*
|
||||||
* @param rc request context
|
* @param rc request context
|
||||||
* @param[in,out] wc parsed request data
|
* @param wc parsed request data
|
||||||
* @param[out] mret HTTP status, set if we return true
|
* @param[out] mret HTTP status, set if we return true
|
||||||
* @return true if the request is idempotent with an existing request
|
* @return true if the request is idempotent with an existing request
|
||||||
* false if we did not find the request in the DB and did not set @a mret
|
* false if we did not find the request in the DB and did not set @a mret
|
||||||
*/
|
*/
|
||||||
static bool
|
static bool
|
||||||
check_request_idempotent (struct TEH_RequestContext *rc,
|
check_request_idempotent (const struct TEH_RequestContext *rc,
|
||||||
struct BatchWithdrawContext *wc,
|
const struct BatchWithdrawContext *wc,
|
||||||
MHD_RESULT *mret)
|
MHD_RESULT *mret)
|
||||||
{
|
{
|
||||||
/* FIXME: Not yet supported. Do we want to, or simply
|
for (unsigned int i = 0; i<wc->planchets_length; i++)
|
||||||
generate an error in this case? */
|
|
||||||
#if FIXME
|
|
||||||
enum GNUNET_DB_QueryStatus qs;
|
|
||||||
|
|
||||||
qs = TEH_plugin->get_withdraw_info (TEH_plugin->cls,
|
|
||||||
&wc->h_coin_envelope,
|
|
||||||
&wc->collectable);
|
|
||||||
if (0 > qs)
|
|
||||||
{
|
{
|
||||||
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
|
struct PlanchetContext *pc = &wc->planchets[i];
|
||||||
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
|
enum GNUNET_DB_QueryStatus qs;
|
||||||
*mret = TALER_MHD_reply_with_error (rc->connection,
|
|
||||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
qs = TEH_plugin->get_withdraw_info (TEH_plugin->cls,
|
||||||
TALER_EC_GENERIC_DB_FETCH_FAILED,
|
&pc->h_coin_envelope,
|
||||||
"get_withdraw_info");
|
&pc->collectable);
|
||||||
return true; /* well, kind-of */
|
if (0 > qs)
|
||||||
|
{
|
||||||
|
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
|
||||||
|
if (GNUNET_DB_STATUS_HARD_ERROR == qs)
|
||||||
|
*mret = TALER_MHD_reply_with_error (rc->connection,
|
||||||
|
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
||||||
|
TALER_EC_GENERIC_DB_FETCH_FAILED,
|
||||||
|
"get_withdraw_info");
|
||||||
|
return true; /* well, kind-of */
|
||||||
|
}
|
||||||
|
if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
|
|
||||||
return false;
|
|
||||||
/* generate idempotent reply */
|
/* generate idempotent reply */
|
||||||
*mret = TALER_MHD_REPLY_JSON_PACK (
|
*mret = generate_reply_success (rc,
|
||||||
rc->connection,
|
wc);
|
||||||
MHD_HTTP_OK,
|
|
||||||
TALER_JSON_pack_blinded_denom_sig ("ev_sig",
|
|
||||||
&wc->collectable.sig));
|
|
||||||
TALER_blinded_denom_sig_free (&wc->collectable.sig);
|
|
||||||
return true;
|
return true;
|
||||||
#else
|
|
||||||
return false;
|
|
||||||
#endif
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -337,7 +367,7 @@ check_request_idempotent (struct TEH_RequestContext *rc,
|
|||||||
* @return MHD result for the @a rc
|
* @return MHD result for the @a rc
|
||||||
*/
|
*/
|
||||||
static MHD_RESULT
|
static MHD_RESULT
|
||||||
prepare_transaction (struct TEH_RequestContext *rc,
|
prepare_transaction (const struct TEH_RequestContext *rc,
|
||||||
struct BatchWithdrawContext *wc)
|
struct BatchWithdrawContext *wc)
|
||||||
{
|
{
|
||||||
/* Note: We could check the reserve balance here,
|
/* Note: We could check the reserve balance here,
|
||||||
@ -379,31 +409,8 @@ prepare_transaction (struct TEH_RequestContext *rc,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* return final positive response */
|
/* return final positive response */
|
||||||
{
|
return generate_reply_success (rc,
|
||||||
json_t *sigs;
|
wc);
|
||||||
|
|
||||||
sigs = json_array ();
|
|
||||||
GNUNET_assert (NULL != sigs);
|
|
||||||
for (unsigned int i = 0; i<wc->planchets_length; i++)
|
|
||||||
{
|
|
||||||
struct PlanchetContext *pc = &wc->planchets[i];
|
|
||||||
|
|
||||||
GNUNET_assert (
|
|
||||||
0 ==
|
|
||||||
json_array_append_new (
|
|
||||||
sigs,
|
|
||||||
GNUNET_JSON_PACK (
|
|
||||||
TALER_JSON_pack_blinded_denom_sig (
|
|
||||||
"ev_sig",
|
|
||||||
&pc->collectable.sig))));
|
|
||||||
}
|
|
||||||
TEH_METRICS_batch_withdraw_num_coins += wc->planchets_length;
|
|
||||||
return TALER_MHD_REPLY_JSON_PACK (
|
|
||||||
rc->connection,
|
|
||||||
MHD_HTTP_OK,
|
|
||||||
GNUNET_JSON_pack_array_steal ("ev_sigs",
|
|
||||||
sigs));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -417,13 +424,39 @@ prepare_transaction (struct TEH_RequestContext *rc,
|
|||||||
* @return MHD result for the @a rc
|
* @return MHD result for the @a rc
|
||||||
*/
|
*/
|
||||||
static MHD_RESULT
|
static MHD_RESULT
|
||||||
parse_planchets (struct TEH_RequestContext *rc,
|
parse_planchets (const struct TEH_RequestContext *rc,
|
||||||
struct BatchWithdrawContext *wc,
|
struct BatchWithdrawContext *wc,
|
||||||
const json_t *planchets)
|
const json_t *planchets)
|
||||||
{
|
{
|
||||||
struct TEH_KeyStateHandle *ksh;
|
struct TEH_KeyStateHandle *ksh;
|
||||||
MHD_RESULT mret;
|
MHD_RESULT mret;
|
||||||
|
|
||||||
|
for (unsigned int i = 0; i<wc->planchets_length; i++)
|
||||||
|
{
|
||||||
|
struct PlanchetContext *pc = &wc->planchets[i];
|
||||||
|
struct GNUNET_JSON_Specification ispec[] = {
|
||||||
|
GNUNET_JSON_spec_fixed_auto ("reserve_sig",
|
||||||
|
&pc->collectable.reserve_sig),
|
||||||
|
GNUNET_JSON_spec_fixed_auto ("denom_pub_hash",
|
||||||
|
&pc->collectable.denom_pub_hash),
|
||||||
|
TALER_JSON_spec_blinded_planchet ("coin_ev",
|
||||||
|
&pc->blinded_planchet),
|
||||||
|
GNUNET_JSON_spec_end ()
|
||||||
|
};
|
||||||
|
|
||||||
|
{
|
||||||
|
enum GNUNET_GenericReturnValue res;
|
||||||
|
|
||||||
|
res = TALER_MHD_parse_json_data (rc->connection,
|
||||||
|
json_array_get (planchets,
|
||||||
|
i),
|
||||||
|
ispec);
|
||||||
|
if (GNUNET_OK != res)
|
||||||
|
return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
|
||||||
|
}
|
||||||
|
pc->collectable.reserve_pub = *wc->reserve_pub;
|
||||||
|
}
|
||||||
|
|
||||||
ksh = TEH_keys_get_state ();
|
ksh = TEH_keys_get_state ();
|
||||||
if (NULL == ksh)
|
if (NULL == ksh)
|
||||||
{
|
{
|
||||||
@ -441,28 +474,8 @@ parse_planchets (struct TEH_RequestContext *rc,
|
|||||||
for (unsigned int i = 0; i<wc->planchets_length; i++)
|
for (unsigned int i = 0; i<wc->planchets_length; i++)
|
||||||
{
|
{
|
||||||
struct PlanchetContext *pc = &wc->planchets[i];
|
struct PlanchetContext *pc = &wc->planchets[i];
|
||||||
struct GNUNET_JSON_Specification ispec[] = {
|
|
||||||
GNUNET_JSON_spec_fixed_auto ("reserve_sig",
|
|
||||||
&pc->collectable.reserve_sig),
|
|
||||||
GNUNET_JSON_spec_fixed_auto ("denom_pub_hash",
|
|
||||||
&pc->collectable.denom_pub_hash),
|
|
||||||
TALER_JSON_spec_blinded_planchet ("coin_ev",
|
|
||||||
&pc->blinded_planchet),
|
|
||||||
GNUNET_JSON_spec_end ()
|
|
||||||
};
|
|
||||||
struct TEH_DenominationKey *dk;
|
struct TEH_DenominationKey *dk;
|
||||||
|
|
||||||
{
|
|
||||||
enum GNUNET_GenericReturnValue res;
|
|
||||||
|
|
||||||
res = TALER_MHD_parse_json_data (rc->connection,
|
|
||||||
json_array_get (planchets,
|
|
||||||
i),
|
|
||||||
ispec);
|
|
||||||
if (GNUNET_OK != res)
|
|
||||||
return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
|
|
||||||
}
|
|
||||||
pc->collectable.reserve_pub = *wc->reserve_pub;
|
|
||||||
dk = TEH_keys_denomination_by_hash2 (ksh,
|
dk = TEH_keys_denomination_by_hash2 (ksh,
|
||||||
&pc->collectable.denom_pub_hash,
|
&pc->collectable.denom_pub_hash,
|
||||||
NULL,
|
NULL,
|
||||||
@ -522,6 +535,7 @@ parse_planchets (struct TEH_RequestContext *rc,
|
|||||||
if (dk->denom_pub.cipher != pc->blinded_planchet.cipher)
|
if (dk->denom_pub.cipher != pc->blinded_planchet.cipher)
|
||||||
{
|
{
|
||||||
/* denomination cipher and blinded planchet cipher not the same */
|
/* denomination cipher and blinded planchet cipher not the same */
|
||||||
|
GNUNET_break_op (0);
|
||||||
return TALER_MHD_reply_with_error (rc->connection,
|
return TALER_MHD_reply_with_error (rc->connection,
|
||||||
MHD_HTTP_BAD_REQUEST,
|
MHD_HTTP_BAD_REQUEST,
|
||||||
TALER_EC_EXCHANGE_GENERIC_CIPHER_MISMATCH,
|
TALER_EC_EXCHANGE_GENERIC_CIPHER_MISMATCH,
|
||||||
@ -532,6 +546,7 @@ parse_planchets (struct TEH_RequestContext *rc,
|
|||||||
&dk->meta.value,
|
&dk->meta.value,
|
||||||
&dk->meta.fees.withdraw))
|
&dk->meta.fees.withdraw))
|
||||||
{
|
{
|
||||||
|
GNUNET_break (0);
|
||||||
return TALER_MHD_reply_with_error (rc->connection,
|
return TALER_MHD_reply_with_error (rc->connection,
|
||||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
||||||
TALER_EC_EXCHANGE_WITHDRAW_AMOUNT_FEE_OVERFLOW,
|
TALER_EC_EXCHANGE_WITHDRAW_AMOUNT_FEE_OVERFLOW,
|
||||||
@ -542,6 +557,7 @@ parse_planchets (struct TEH_RequestContext *rc,
|
|||||||
&wc->batch_total,
|
&wc->batch_total,
|
||||||
&pc->collectable.amount_with_fee))
|
&pc->collectable.amount_with_fee))
|
||||||
{
|
{
|
||||||
|
GNUNET_break (0);
|
||||||
return TALER_MHD_reply_with_error (rc->connection,
|
return TALER_MHD_reply_with_error (rc->connection,
|
||||||
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
MHD_HTTP_INTERNAL_SERVER_ERROR,
|
||||||
TALER_EC_EXCHANGE_WITHDRAW_AMOUNT_FEE_OVERFLOW,
|
TALER_EC_EXCHANGE_WITHDRAW_AMOUNT_FEE_OVERFLOW,
|
||||||
|
Loading…
Reference in New Issue
Block a user