diff options
author | Christian Grothoff <grothoff@gnunet.org> | 2023-10-13 14:43:50 +0200 |
---|---|---|
committer | Christian Grothoff <grothoff@gnunet.org> | 2023-10-13 21:16:50 +0200 |
commit | a5f50083e65a3e9a0945b150701349afa81a0e9e (patch) | |
tree | a6a89fa83c78d812a7f6a38e851659cf443269dd /src/backend | |
parent | a8b2456ecf47e2650f8bac1da3cc25b4ace54d24 (diff) | |
download | merchant-a5f50083e65a3e9a0945b150701349afa81a0e9e.tar.gz merchant-a5f50083e65a3e9a0945b150701349afa81a0e9e.tar.bz2 merchant-a5f50083e65a3e9a0945b150701349afa81a0e9e.zip |
work towards multi-currency support
Diffstat (limited to 'src/backend')
-rw-r--r-- | src/backend/kudos.conf | 4 | ||||
-rw-r--r-- | src/backend/taler-merchant-exchange.c | 29 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_exchanges.c | 32 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_post-rewards-ID-pickup.c | 19 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_private-post-orders-ID-refund.c | 112 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_private-post-orders.c | 94 |
6 files changed, 202 insertions, 88 deletions
diff --git a/src/backend/kudos.conf b/src/backend/kudos.conf index 424209d0..71ed232a 100644 --- a/src/backend/kudos.conf +++ b/src/backend/kudos.conf @@ -1,9 +1,5 @@ - - # Trust Taler project for "KUDOS" currency so that demos work out-of-the-box [merchant-exchange-kudos] EXCHANGE_BASE_URL = https://exchange.demo.taler.net/ MASTER_KEY = JFX1NE38C65A5XT8VSNQXX7R7BBG4GNZ63F5T7Y6859V4J8KBKF0 -# If currency does not match [TALER] section, the exchange -# will be ignored! CURRENCY = KUDOS diff --git a/src/backend/taler-merchant-exchange.c b/src/backend/taler-merchant-exchange.c index 4db8f92d..bc475031 100644 --- a/src/backend/taler-merchant-exchange.c +++ b/src/backend/taler-merchant-exchange.c @@ -613,14 +613,18 @@ check_wire_fee (struct Inquiry *w, case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: break; } - if (0 <= TALER_amount_cmp (&fees.wire, - wire_fee)) + if ( (GNUNET_OK != + TALER_amount_cmp_currency (&fees.wire, + wire_fee)) || + (0 > TALER_amount_cmp (&fees.wire, + wire_fee)) ) { + GNUNET_break_op (0); GNUNET_free (wire_method); - return GNUNET_OK; /* expected_fee >= wire_fee */ + return GNUNET_SYSERR; /* expected_fee >= wire_fee */ } GNUNET_free (wire_method); - return GNUNET_SYSERR; + return GNUNET_OK; } @@ -698,8 +702,14 @@ check_transfer (void *cls, GNUNET_break (0); return; /* already had a serious issue; odd that we're called more than once as well... */ } - if ( (0 != TALER_amount_cmp (amount_with_fee, + if ( (GNUNET_OK != + TALER_amount_cmp_currency (amount_with_fee, + &ttd->coin_value)) || + (0 != TALER_amount_cmp (amount_with_fee, &ttd->coin_value)) || + (GNUNET_OK != + TALER_amount_cmp_currency (deposit_fee, + &ttd->coin_fee)) || (0 != TALER_amount_cmp (deposit_fee, &ttd->coin_fee)) ) { @@ -905,9 +915,12 @@ wire_transfer_cb (void *cls, return; } - if (0 != - TALER_amount_cmp (&td->total_amount, - &w->total)) + if ( (GNUNET_OK != + TALER_amount_cmp_currency (&td->total_amount, + &w->total)) || + (0 != + TALER_amount_cmp (&td->total_amount, + &w->total)) ) { GNUNET_break_op (0); update_transaction_status (w, diff --git a/src/backend/taler-merchant-httpd_exchanges.c b/src/backend/taler-merchant-httpd_exchanges.c index 170e59f8..0ac2234c 100644 --- a/src/backend/taler-merchant-httpd_exchanges.c +++ b/src/backend/taler-merchant-httpd_exchanges.c @@ -37,6 +37,9 @@ #define RETRY_BACKOFF_THRESHOLD GNUNET_TIME_relative_multiply ( \ GNUNET_TIME_UNIT_SECONDS, 60) +#define FAST_FAIL_THRESHOLD GNUNET_TIME_relative_multiply ( \ + GNUNET_TIME_UNIT_SECONDS, 2) + /** * Information we keep for a pending #MMH_EXCHANGES_keys4exchange() operation. @@ -796,6 +799,21 @@ TMH_EXCHANGES_keys4exchange ( exchange); return fo; } + if ( (NULL == exchange->conn) && + (GNUNET_TIME_relative_cmp ( + GNUNET_TIME_absolute_get_remaining ( + exchange->first_retry), + >, + FAST_FAIL_THRESHOLD)) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Already waiting for `%skeys' for a while, failing query instantly\n", + exchange->url); + GNUNET_assert (NULL == fo->at); + fo->at = GNUNET_SCHEDULER_add_now (&return_keys, + fo); + return fo; + } GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Next /keys request scheduled for %s\n", GNUNET_TIME_absolute2s ( @@ -911,6 +929,7 @@ free_exchange_entry (struct TMH_Exchange *exchange) } GNUNET_assert (NULL == exchange->keys_head); GNUNET_assert (NULL == exchange->keys_tail); + GNUNET_free (exchange->currency); GNUNET_free (exchange->url); GNUNET_free (exchange); } @@ -1118,8 +1137,10 @@ keys_mgmt_cb (void *cls, TALER_EXCHANGE_keys_decref (keys); return; } + if (NULL == exchange->currency) + exchange->currency = GNUNET_strdup (keys->currency); if (0 != strcmp (exchange->currency, - keys->currency)) + keys->currency)) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "/keys response from `%s' is for currency `%s', but we expected `%s'\n", @@ -1286,6 +1307,8 @@ TMH_test_exchange_configured_for_currency ( { if (! exchange->trusted) continue; + if (NULL == exchange->currency) + continue; if (0 == strcmp (currency, exchange->currency)) return true; @@ -1317,6 +1340,11 @@ accept_exchanges (void *cls, "merchant-exchange-", strlen ("merchant-exchange-"))) return; + if (GNUNET_YES == + GNUNET_CONFIGURATION_get_value_yesno (cfg, + section, + "DISABLED")) + return; if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cfg, section, @@ -1435,6 +1463,8 @@ update_exchange_keys (void *cls, GNUNET_break (0); return; } + if (NULL == exchange->currency) + exchange->currency = GNUNET_strdup (keys->currency); if (0 != strcmp (keys->currency, exchange->currency)) { diff --git a/src/backend/taler-merchant-httpd_post-rewards-ID-pickup.c b/src/backend/taler-merchant-httpd_post-rewards-ID-pickup.c index 16bd5940..1a21790d 100644 --- a/src/backend/taler-merchant-httpd_post-rewards-ID-pickup.c +++ b/src/backend/taler-merchant-httpd_post-rewards-ID-pickup.c @@ -537,7 +537,7 @@ compute_total_requested (void *cls, { pc->http_status = MHD_HTTP_BAD_REQUEST; pc->response = - TALER_MHD_make_error (TALER_EC_GENERIC_CURRENCY_MISMATCH, + TALER_MHD_make_error (TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH, "Must not mix currencies when picking up rewards"); MHD_resume_connection (pc->connection); TALER_MHD_daemon_trigger (); /* we resumed, kick MHD */ @@ -920,6 +920,23 @@ RETRY: TALER_EC_MERCHANT_REWARD_PICKUP_HAS_EXPIRED, hc->infix); } + if (GNUNET_OK != + TALER_amount_cmp_currency (&total_authorized, + &total_picked_up)) + { + /* This could theoretically happen if the exchange changed + its currency between us approving the reward + and the client then picks it up with the new + exchange currency. And of course the backend + would have had to get the new /keys of the + exchange already as well. Very theoretical case. */ + GNUNET_break_op (0); + TMH_db->rollback (TMH_db->cls); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_CONFLICT, + TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH, + "picked up amount does not use same currency as authorized amount"); + } if (0 > TALER_amount_subtract (&total_remaining, &total_authorized, diff --git a/src/backend/taler-merchant-httpd_private-post-orders-ID-refund.c b/src/backend/taler-merchant-httpd_private-post-orders-ID-refund.c index 1b2aa460..58fa96f4 100644 --- a/src/backend/taler-merchant-httpd_private-post-orders-ID-refund.c +++ b/src/backend/taler-merchant-httpd_private-post-orders-ID-refund.c @@ -133,6 +133,20 @@ TMH_private_post_orders_ID_refund (const struct TMH_RequestHandler *rh, struct GNUNET_TIME_Timestamp timestamp; { + enum GNUNET_GenericReturnValue res; + + res = TALER_MHD_parse_json_data (connection, + hc->request_body, + spec); + if (GNUNET_OK != res) + { + return (GNUNET_NO == res) + ? MHD_YES + : MHD_NO; + } + } + + { enum GNUNET_DB_QueryStatus qs; uint64_t order_serial; struct GNUNET_TIME_Timestamp refund_deadline; @@ -145,9 +159,34 @@ TMH_private_post_orders_ID_refund (const struct TMH_RequestHandler *rh, &order_serial, &paid, NULL); - if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) + if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) + { + if (qs < 0) + { + GNUNET_break (0); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_FETCH_FAILED, + "lookup_contract_terms"); + } + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_MERCHANT_GENERIC_ORDER_UNKNOWN, + hc->infix); + } + if (GNUNET_OK != + TALER_JSON_contract_hash (contract_terms, + &h_contract)) + { + GNUNET_break (0); + json_decref (contract_terms); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_FAILED_COMPUTE_JSON_HASH, + "Could not hash contract terms"); + } { - struct GNUNET_JSON_Specification spec[] = { + struct GNUNET_JSON_Specification cspec[] = { GNUNET_JSON_spec_timestamp ("refund_deadline", &refund_deadline), GNUNET_JSON_spec_timestamp ("timestamp", @@ -157,11 +196,10 @@ TMH_private_post_orders_ID_refund (const struct TMH_RequestHandler *rh, if (GNUNET_YES != GNUNET_JSON_parse (contract_terms, - spec, + cspec, NULL, NULL)) { GNUNET_break (0); - GNUNET_JSON_parse_free (spec); json_decref (contract_terms); return TALER_MHD_reply_with_error ( connection, @@ -188,28 +226,6 @@ TMH_private_post_orders_ID_refund (const struct TMH_RequestHandler *rh, wire the funds, so we will try to give the refund anyway */ } } - else - { - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_NOT_FOUND, - TALER_EC_MERCHANT_GENERIC_ORDER_UNKNOWN, - hc->infix); - } - } - - { - enum GNUNET_GenericReturnValue res; - - res = TALER_MHD_parse_json_data (connection, - hc->request_body, - spec); - if (GNUNET_OK != res) - { - json_decref (contract_terms); - return (GNUNET_NO == res) - ? MHD_YES - : MHD_NO; - } } TMH_db->preflight (TMH_db->cls); @@ -293,6 +309,14 @@ TMH_private_post_orders_ID_refund (const struct TMH_RequestHandler *rh, switch (rs) { + case TALER_MERCHANTDB_RS_BAD_CURRENCY: + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Refund amount %s is not in the currency of the original payment\n", + TALER_amount2s (&refund)); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_CONFLICT, + TALER_EC_MERCHANT_GENERIC_CURRENCY_MISMATCH, + "Order was paid in a different currency"); case TALER_MERCHANTDB_RS_TOO_HIGH: GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Refusing refund amount %s that is larger than original payment\n", @@ -318,41 +342,9 @@ TMH_private_post_orders_ID_refund (const struct TMH_RequestHandler *rh, TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_ID_REFUND_ORDER_UNPAID, hc->infix); case TALER_MERCHANTDB_RS_SUCCESS: - { - enum GNUNET_DB_QueryStatus qs; - json_t *contract_terms; - uint64_t order_serial; - bool paid; - - qs = TMH_db->lookup_contract_terms (TMH_db->cls, - hc->instance->settings.id, - hc->infix, - &contract_terms, - &order_serial, - &paid, - NULL); - if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) - { - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_NOT_FOUND, - TALER_EC_MERCHANT_GENERIC_ORDER_UNKNOWN, - hc->infix); - } - if (GNUNET_OK != - TALER_JSON_contract_hash (contract_terms, - &h_contract)) - { - GNUNET_break (0); - json_decref (contract_terms); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_FAILED_COMPUTE_JSON_HASH, - "Could not hash contract terms"); - } - json_decref (contract_terms); - } + /* continued below */ break; - } + } /* end switch */ { struct GNUNET_TIME_Timestamp timestamp; diff --git a/src/backend/taler-merchant-httpd_private-post-orders.c b/src/backend/taler-merchant-httpd_private-post-orders.c index 61051b9a..09c709e9 100644 --- a/src/backend/taler-merchant-httpd_private-post-orders.c +++ b/src/backend/taler-merchant-httpd_private-post-orders.c @@ -39,6 +39,11 @@ #define MAX_RETRIES 3 /** + * How long do we wait for /keys from the exchange? + */ +#define KEYS_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 2) + +/** * What is the label under which we find/place the merchant's * jurisdiction in the locations list by default? */ @@ -124,6 +129,11 @@ struct RekeyExchange */ struct TMH_EXCHANGES_KeysOperation *fo; + /** + * Timeout task handle. + */ + struct GNUNET_SCHEDULER_Task *tx; + }; @@ -419,6 +429,7 @@ clean_order (void *cls) oc->pending_reload_tail, rx); TMH_EXCHANGES_keys4exchange_cancel (rx->fo); + GNUNET_SCHEDULER_cancel (rx->tx); GNUNET_free (rx->url); GNUNET_free (rx); } @@ -964,6 +975,28 @@ get_acceptable (void *cls, /** + * Exchange `/keys` processing is done, resume handling + * the order. + * + * @param[in,out] oc context to resume + */ +static void +resume_with_keys (struct OrderContext *oc) +{ + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Resuming order processing after /keys downloads (now have %u accounts)\n", + (unsigned int) json_array_size (oc->exchanges)); + GNUNET_assert (GNUNET_YES == oc->suspended); + GNUNET_CONTAINER_DLL_remove (oc_head, + oc_tail, + oc); + oc->suspended = GNUNET_NO; + MHD_resume_connection (oc->connection); + TALER_MHD_daemon_trigger (); /* we resumed, kick MHD */ +} + + +/** * Function called with the result of a #TMH_EXCHANGES_keys4exchange() * operation. * @@ -983,13 +1016,15 @@ keys_cb ( &oc->hc->instance->settings; rx->fo = NULL; + GNUNET_SCHEDULER_cancel (rx->tx); + rx->tx = NULL; GNUNET_CONTAINER_DLL_remove (oc->pending_reload_head, oc->pending_reload_tail, rx); if (NULL == keys) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Failed to download %s/keys\n", + "Failed to download %skeys\n", rx->url); } else @@ -999,24 +1034,41 @@ keys_cb ( TALER_amount_is_valid (&oc->max_fee)) ) update_stefan (oc, keys); + get_acceptable (oc, + rx->url, + exchange); } - get_acceptable (oc, - rx->url, - exchange); GNUNET_free (rx->url); GNUNET_free (rx); if (NULL != oc->pending_reload_head) return; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Resuming order processing after /keys downloads (now have %u accounts)\n", - (unsigned int) json_array_size (oc->exchanges)); - GNUNET_assert (GNUNET_YES == oc->suspended); - GNUNET_CONTAINER_DLL_remove (oc_head, - oc_tail, - oc); - oc->suspended = GNUNET_NO; - MHD_resume_connection (oc->connection); - TALER_MHD_daemon_trigger (); /* we resumed, kick MHD */ + resume_with_keys (oc); +} + + +/** + * A `/keys` request to an exchange is taking too + * long, ignore the exchange for now. + * + * @param cls a `struct RekeyExchange *` + */ +static void +keys_timeout (void *cls) +{ + struct RekeyExchange *rx = cls; + struct OrderContext *oc = rx->oc; + + rx->tx = NULL; + TMH_EXCHANGES_keys4exchange_cancel (rx->fo); + rx->fo = NULL; + GNUNET_CONTAINER_DLL_remove (oc->pending_reload_head, + oc->pending_reload_tail, + rx); + GNUNET_free (rx->url); + GNUNET_free (rx); + if (NULL != oc->pending_reload_head) + return; + resume_with_keys (oc); } @@ -1050,6 +1102,10 @@ get_exchange_keys (void *cls, oc->forced_reload, &keys_cb, rx); + rx->tx + = GNUNET_SCHEDULER_add_delayed (KEYS_TIMEOUT, + &keys_timeout, + rx); } @@ -1247,6 +1303,16 @@ patch_order (struct OrderContext *oc) ret); return; } + if (! TMH_test_exchange_configured_for_currency (oc->brutto.currency)) + { + GNUNET_break_op (0); + GNUNET_JSON_parse_free (spec); + reply_with_error (oc, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_CURRENCY_MISMATCH, + NULL); + return; + } /* Add order_id if it doesn't exist. */ if (NULL == order_id) |