diff options
author | Christian Grothoff <christian@grothoff.org> | 2022-05-17 12:29:00 +0200 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2022-05-17 12:29:00 +0200 |
commit | b9d0b1aae47ddbc8928e8c4d5a0188da91fc7735 (patch) | |
tree | a930fb7a502c3fe1afabe4fba91f024ba6990014 /src | |
parent | 7bd1828482cd7c421bf5fcd66603ae86f956d779 (diff) |
-make batch withdraw requests idempotent
Diffstat (limited to 'src')
-rw-r--r-- | src/exchange/taler-exchange-httpd_batch-withdraw.c | 162 |
1 files changed, 89 insertions, 73 deletions
diff --git a/src/exchange/taler-exchange-httpd_batch-withdraw.c b/src/exchange/taler-exchange-httpd_batch-withdraw.c index 2b90eda4f..e58548af7 100644 --- a/src/exchange/taler-exchange-httpd_batch-withdraw.c +++ b/src/exchange/taler-exchange-httpd_batch-withdraw.c @@ -273,58 +273,88 @@ batch_withdraw_transaction (void *cls, return GNUNET_DB_STATUS_HARD_ERROR; } } - 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 * answer. If so, replay the existing answer and return the * HTTP response. * * @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 * @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 */ static bool -check_request_idempotent (struct TEH_RequestContext *rc, - struct BatchWithdrawContext *wc, +check_request_idempotent (const struct TEH_RequestContext *rc, + const struct BatchWithdrawContext *wc, MHD_RESULT *mret) { - /* FIXME: Not yet supported. Do we want to, or simply - 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) + for (unsigned int i = 0; i<wc->planchets_length; i++) { - 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 */ + struct PlanchetContext *pc = &wc->planchets[i]; + enum GNUNET_DB_QueryStatus qs; + + qs = TEH_plugin->get_withdraw_info (TEH_plugin->cls, + &pc->h_coin_envelope, + &pc->collectable); + 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 */ - *mret = TALER_MHD_REPLY_JSON_PACK ( - rc->connection, - MHD_HTTP_OK, - TALER_JSON_pack_blinded_denom_sig ("ev_sig", - &wc->collectable.sig)); - TALER_blinded_denom_sig_free (&wc->collectable.sig); + *mret = generate_reply_success (rc, + wc); return true; -#else - return false; -#endif } @@ -337,7 +367,7 @@ check_request_idempotent (struct TEH_RequestContext *rc, * @return MHD result for the @a rc */ static MHD_RESULT -prepare_transaction (struct TEH_RequestContext *rc, +prepare_transaction (const struct TEH_RequestContext *rc, struct BatchWithdrawContext *wc) { /* Note: We could check the reserve balance here, @@ -379,31 +409,8 @@ prepare_transaction (struct TEH_RequestContext *rc, } } /* return final positive response */ - { - 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)); - } + return generate_reply_success (rc, + wc); } @@ -417,27 +424,13 @@ prepare_transaction (struct TEH_RequestContext *rc, * @return MHD result for the @a rc */ static MHD_RESULT -parse_planchets (struct TEH_RequestContext *rc, +parse_planchets (const struct TEH_RequestContext *rc, struct BatchWithdrawContext *wc, const json_t *planchets) { struct TEH_KeyStateHandle *ksh; MHD_RESULT mret; - ksh = TEH_keys_get_state (); - if (NULL == ksh) - { - if (! check_request_idempotent (rc, - wc, - &mret)) - { - return TALER_MHD_reply_with_error (rc->connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING, - NULL); - } - return mret; - } for (unsigned int i = 0; i<wc->planchets_length; i++) { struct PlanchetContext *pc = &wc->planchets[i]; @@ -450,7 +443,6 @@ parse_planchets (struct TEH_RequestContext *rc, &pc->blinded_planchet), GNUNET_JSON_spec_end () }; - struct TEH_DenominationKey *dk; { enum GNUNET_GenericReturnValue res; @@ -463,6 +455,27 @@ parse_planchets (struct TEH_RequestContext *rc, return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES; } pc->collectable.reserve_pub = *wc->reserve_pub; + } + + ksh = TEH_keys_get_state (); + if (NULL == ksh) + { + if (! check_request_idempotent (rc, + wc, + &mret)) + { + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING, + NULL); + } + return mret; + } + for (unsigned int i = 0; i<wc->planchets_length; i++) + { + struct PlanchetContext *pc = &wc->planchets[i]; + struct TEH_DenominationKey *dk; + dk = TEH_keys_denomination_by_hash2 (ksh, &pc->collectable.denom_pub_hash, NULL, @@ -522,6 +535,7 @@ parse_planchets (struct TEH_RequestContext *rc, if (dk->denom_pub.cipher != pc->blinded_planchet.cipher) { /* denomination cipher and blinded planchet cipher not the same */ + GNUNET_break_op (0); return TALER_MHD_reply_with_error (rc->connection, MHD_HTTP_BAD_REQUEST, TALER_EC_EXCHANGE_GENERIC_CIPHER_MISMATCH, @@ -532,6 +546,7 @@ parse_planchets (struct TEH_RequestContext *rc, &dk->meta.value, &dk->meta.fees.withdraw)) { + GNUNET_break (0); return TALER_MHD_reply_with_error (rc->connection, MHD_HTTP_INTERNAL_SERVER_ERROR, TALER_EC_EXCHANGE_WITHDRAW_AMOUNT_FEE_OVERFLOW, @@ -542,6 +557,7 @@ parse_planchets (struct TEH_RequestContext *rc, &wc->batch_total, &pc->collectable.amount_with_fee)) { + GNUNET_break (0); return TALER_MHD_reply_with_error (rc->connection, MHD_HTTP_INTERNAL_SERVER_ERROR, TALER_EC_EXCHANGE_WITHDRAW_AMOUNT_FEE_OVERFLOW, |