-make batch withdraw requests idempotent

This commit is contained in:
Christian Grothoff 2022-05-17 12:29:00 +02:00
parent 7bd1828482
commit b9d0b1aae4
No known key found for this signature in database
GPG Key ID: 939E6BE1E29FC3CC

View File

@ -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,