From 49157c0beaeae761cac5daa22d8734880ee726dd Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Fri, 22 May 2020 13:01:11 +0200 Subject: implement POST /tips/ID/pickup --- src/backend/Makefile.am | 4 +- .../taler-merchant-httpd_post-tips-ID-pickup.c | 166 +++++++++++++-------- src/include/taler_merchantdb_plugin.h | 47 ++++++ 3 files changed, 153 insertions(+), 64 deletions(-) (limited to 'src') diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am index 1c42b7f5..8960b15f 100644 --- a/src/backend/Makefile.am +++ b/src/backend/Makefile.am @@ -74,7 +74,9 @@ taler_merchant_httpd_SOURCES = \ taler-merchant-httpd_post-orders-ID-claim.c \ taler-merchant-httpd_post-orders-ID-claim.h \ taler-merchant-httpd_post-orders-ID-pay.c \ - taler-merchant-httpd_post-orders-ID-pay.h + taler-merchant-httpd_post-orders-ID-pay.h \ + taler-merchant-httpd_post-tips-ID-pickup.c \ + taler-merchant-httpd_post-tips-ID-pickup.h DEAD = \ taler-merchant-httpd_check-payment.c taler-merchant-httpd_check-payment.h \ diff --git a/src/backend/taler-merchant-httpd_post-tips-ID-pickup.c b/src/backend/taler-merchant-httpd_post-tips-ID-pickup.c index c24c2c05..e6437c39 100644 --- a/src/backend/taler-merchant-httpd_post-tips-ID-pickup.c +++ b/src/backend/taler-merchant-httpd_post-tips-ID-pickup.c @@ -26,7 +26,7 @@ #include "taler-merchant-httpd.h" #include "taler-merchant-httpd_mhd.h" #include "taler-merchant-httpd_exchanges.h" -#include "taler-merchant-httpd_tip-pickup.h" +#include "taler-merchant-httpd_post-tips-ID-pickup.h" /** @@ -58,6 +58,16 @@ struct PlanchetOperation */ struct PickupContext *pc; + /** + * Kept in a DLL. + */ + struct PlanchetOperation *prev; + + /** + * Kept in a DLL. + */ + struct PlanchetOperation *next; + /** * Find operation (while active), later NULL. */ @@ -128,13 +138,19 @@ struct PickupContext /** * Which reserve are we draining? */ - struct TALER_ReservePrivateKey reserve_priv; + struct TALER_ReservePrivateKeyP reserve_priv; /** * Which tip is being picked up? */ struct GNUNET_HashCode tip_id; + /** + * What is the ID of the pickup operation? (Basically a + * hash over the key inputs). + */ + struct GNUNET_HashCode pickup_id; + /** * Array of our planchets. */ @@ -264,6 +280,7 @@ withdraw_cb (void *cls, { struct PlanchetOperation *po = cls; struct PickupContext *pc = po->pc; + enum GNUNET_DB_QueryStatus qs; GNUNET_CONTAINER_DLL_remove (pc->po_head, pc->po_tail, @@ -383,9 +400,9 @@ try_withdraw (struct PickupContext *pc, &do_withdraw, po); GNUNET_assert (NULL != po->fo); - GNUNET_CONTAINER_DLL_insert (pc->fo_head, - pc->fo_tail, - fo); + GNUNET_CONTAINER_DLL_insert (pc->po_head, + pc->po_tail, + po); } @@ -460,7 +477,7 @@ compute_total_requested (void *cls, &pd->denom_pub_hash); if (NULL == dpk) { - pc->http_status = MHD_HTTP_BAD_REQUEST; + pc->http_status = MHD_HTTP_CONFLICT; pc->response = TALER_MHD_make_json_pack ( "{s:I, s:I, s:I, s:O}", @@ -489,8 +506,48 @@ compute_total_requested (void *cls, } } pc->tr_initialized = true; +} + + +/** + * The tip lookup operation failed. Generate an error response based on the @a qs. + * + * @param connection connection to generate error for + * @param qs DB status to base error creation on + * @return MHD result code + */ +static MHD_RESULT +reply_lookup_tip_failed (struct MHD_Connection *connection, + enum GNUNET_DB_QueryStatus qs) +{ + unsigned int response_code; + enum TALER_ErrorCode ec; - return; + TMH_db->rollback (TMH_db->cls); + switch (qs) + { + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + ec = TALER_EC_TIP_PICKUP_TIP_ID_UNKNOWN; + response_code = MHD_HTTP_NOT_FOUND; + break; + case GNUNET_DB_STATUS_SOFT_ERROR: + ec = TALER_EC_TIP_PICKUP_DB_ERROR_SOFT; + response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; + break; + case GNUNET_DB_STATUS_HARD_ERROR: + ec = TALER_EC_TIP_PICKUP_DB_ERROR_HARD; + response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; + break; + default: + GNUNET_break (0); + ec = TALER_EC_INTERNAL_LOGIC_ERROR; + response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; + break; + } + return TALER_MHD_reply_with_error (connection, + response_code, + ec, + "Could not process pickup"); } @@ -509,19 +566,13 @@ TMH_post_tips_ID_pickup (const struct TMH_RequestHandler *rh, struct TMH_HandlerContext *hc) { struct PickupContext *pc = hc->ctx; - - char *justification; char *exchange_url; - struct TALER_Amount total_authorized; + struct TALER_Amount total_requested; struct TALER_Amount total_picked_up; struct TALER_Amount total_remaining; struct GNUNET_TIME_Absolute expiration; - struct GNUNET_TIME_Absolute timestamp_expire; - struct TALER_ReservePrivateKey reserve_priv; - MHD_RESULT ret; enum GNUNET_DB_QueryStatus qs; - unsigned int num_coins; unsigned int num_retries; if (NULL == pc) @@ -613,6 +664,8 @@ TMH_post_tips_ID_pickup (const struct TMH_RequestHandler *rh, sizeof (pc->tip_id)); for (unsigned int i = 0; iplanchets_length; i++) { + struct TALER_PlanchetDetail *pd = &pc->planchets[index]; + GNUNET_CRYPTO_hash_context_read (hc, &pd->denom_pub_hash, sizeof (pd->denom_pub_hash)); @@ -627,7 +680,7 @@ TMH_post_tips_ID_pickup (const struct TMH_RequestHandler *rh, if (NULL != pc->response) { - MDH_RESULT ret; + MHD_RESULT ret; ret = MHD_queue_response (connection, pc->http_status, @@ -638,6 +691,17 @@ TMH_post_tips_ID_pickup (const struct TMH_RequestHandler *rh, if (! pc->tr_initialized) { + qs = TMH_db->lookup_tip (TMH_db->cls, + hc->instance->settings.id, + &pc->tip_id, + &total_authorized, + &total_picked_up, + &expiration, + &exchange_url, + &pc->reserve_priv); + if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) + return reply_lookup_tip_failed (connection, + qs); MHD_suspend_connection (connection); pc->tt = GNUNET_SCHEDULER_add_delayed (EXCHANGE_TIMEOUT, &do_timeout, @@ -674,25 +738,25 @@ RETRY: "Could not begin transaction"); } { - struct GNUNET_CRYPTO_RsaSignature *sigs[num_coins]; + struct GNUNET_CRYPTO_RsaSignature *sigs[GNUNET_NZL (pc->planchets_length)]; qs = TMH_db->lookup_pickup (TMH_db->cls, hc->instance->settings.id, - &tip_id, - &pickup_id, + &pc->tip_id, + &pc->pickup_id, &exchange_url, - &pc->reserve_priv; - num_coins, + &pc->reserve_priv, + pc->planchets_length, sigs); if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) { - for (unsigned int i = 0; iplanchets_length; i++) { if (NULL == sigs[i]) { try_withdraw (pc, exchange_url, - planchets[i], + &pc->planchets[i], i); qs = GNUNET_DB_STATUS_SUCCESS_NO_RESULTS; } @@ -703,7 +767,7 @@ RETRY: GNUNET_CONTAINER_DLL_insert (pc_head, pc_tail, pc); - pc->tt = GNUNET_SCHEDULER_add_delayed (TIMEOUT, + pc->tt = GNUNET_SCHEDULER_add_delayed (EXCHANGE_TIMEOUT, &do_timeout, pc); TMH_db->rollback (TMH_db->cls); @@ -724,7 +788,7 @@ RETRY: blind_sigs = json_array (); GNUNET_assert (NULL != blind_sigs); - for (unsigned int i = 0; iplanchets_length; i++) { GNUNET_assert (0 == json_array_append_new ( @@ -761,51 +825,27 @@ RETRY: qs = TMH_db->lookup_tip (TMH_db->cls, hc->instance->settings.id, - &tip_id, + &pc->tip_id, &total_authorized, &total_picked_up, &expiration, &exchange_url, &pc->reserve_priv); + if (GNUNET_DB_STATUS_SOFT_ERROR == qs) + goto RETRY; if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) - { - unsigned int response_code; - enum TALER_ErrorCode ec; - - TMH_db->rollback (TMH_db->cls); - switch (qs) - { - case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: - ec = TALER_EC_TIP_PICKUP_TIP_ID_UNKNOWN; - response_code = MHD_HTTP_NOT_FOUND; - break; - case GNUNET_DB_STATUS_SOFT_ERROR: - goto RETRY; - case GNUNET_DB_STATUS_HARD_ERROR: - ec = TALER_EC_TIP_PICKUP_DB_ERROR_HARD; - response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; - break; - default: - GNUNET_break (0); - ec = TALER_EC_INTERNAL_LOGIC_ERROR; - response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; - break; - } - return TALER_MHD_reply_with_error (connection, - response_code, - ec, - "Could not process pickup"); - } + return reply_lookup_tip_failed (connection, + qs); if (0 == GNUNET_TIME_absolute_get_remaining (expiration).rel_value_us) { TMH_db->rollback (TMH_db->cls); return TALER_MHD_reply_with_error (connection, - response_code, - ec, - "Could not process pickup"); + MHD_HTTP_GONE, + TALER_EC_TIP_PICKUP_HAS_EXPIRED, + "Could not process pickup: it is too late"); } if (0 > - TALER_amount_subtract (&total_left, + TALER_amount_subtract (&total_remaining, &total_authorized, &total_picked_up)) { @@ -818,14 +858,14 @@ RETRY: } if (0 > - TALER_amount_cmp (&total_left, + TALER_amount_cmp (&total_remaining, &total_requested)) { GNUNET_break (0); TMH_db->rollback (TMH_db->cls); return TALER_MHD_reply_with_error (connection, MHD_HTTP_BAD_REQUEST, - TALER_EC_PICKUP_AMOUNT_EXCEEDS_TIP_REMAINING, + TALER_EC_TIP_PICKUP_AMOUNT_EXCEEDS_TIP_REMAINING, "requested amount exceeds amount left in tip"); } @@ -835,9 +875,9 @@ RETRY: &total_requested)); qs = TMH_db->insert_pickup (TMH_db->cls, hc->instance->settings.id, - &tip_id, + &pc->tip_id, &total_picked_up, - &pickup_id, + &pc->pickup_id, &total_requested); if (qs < 0) { @@ -864,11 +904,11 @@ RETRY: pc->tt = GNUNET_SCHEDULER_add_delayed (EXCHANGE_TIMEOUT, &do_timeout, pc); - for (unsigned int i = 0; iplanchets_length; i++) { try_withdraw (pc, exchange_url, - planchets[i], + &pc->planchets[i], i); } return MHD_YES; diff --git a/src/include/taler_merchantdb_plugin.h b/src/include/taler_merchantdb_plugin.h index 2dc764e8..1324b4d9 100644 --- a/src/include/taler_merchantdb_plugin.h +++ b/src/include/taler_merchantdb_plugin.h @@ -1781,6 +1781,53 @@ struct TALER_MERCHANTDB_Plugin struct TALER_MERCHANTDB_PickupDetails **pickups); + /** + * Insert details about a tip pickup operation. The @a total_picked_up + * UPDATES the total amount under the @a tip_id, while the @a + * total_requested is the amount to be associated with this @a pickup_id. + * While there is usually only one pickup event that picks up the entire + * amount, our schema allows for wallets to pick up the amount incrementally + * over multiple pick up operations. + * + * @param cls closure, typically a connection to the db + * @param tip_id the unique ID for the tip + * @param total_picked_up how much was picked up overall at this + * point (includes @total_requested) + * @param pickup_id unique ID for the operation + * @param total_requested how much is being picked up in this operation + * @return transaction status, usually + * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT for success + * #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if @a credit_uuid already known + */ + enum GNUNET_DB_QueryStatus + (*insert_pickup)(void *cls, + const char *instance_id, + const struct GNUNET_HashCode *tip_id, + const struct TALER_Amount *total_picked_up, + const struct GNUNET_HashCode *pickup_id, + const struct TALER_Amount *total_requested); + + + /** + * Insert blind signature obtained from the exchange during a + * tip pickup operation. + * + * @param cls closure, typically a connection to the db + * @param pickup_id unique ID for the operation + * @param offset offset of the blind signature for the pickup + * @param blind_sig the blind signature + * @return transaction status, usually + * #GNUNET_DB_STATUS_SUCCESS_ONE_RESULT for success + * #GNUNET_DB_STATUS_SUCCESS_NO_RESULTS if @a credit_uuid already known + */ + enum GNUNET_DB_QueryStatus + (*insert_pickup_blind_signature)( + void *cls, + const struct GNUNET_HashCode *pickup_id, + uint32_t offset, + const struct GNUNET_CRYPTO_RsaSignature *blind_sig); + + /* ****************** OLD API ******************** */ -- cgit v1.2.3