diff options
author | Christian Grothoff <christian@grothoff.org> | 2023-07-13 23:07:50 +0200 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2023-07-13 23:08:23 +0200 |
commit | 86f9c6823ec9d92dfbbbc1220655c91b129020cf (patch) | |
tree | c2a21180a3b8350ab4a98644bd359139df150c8c /src | |
parent | 4e45f3a965d5454b136eaf041d5d523f614343d4 (diff) |
merging /keys and /wire API in the exchange
Diffstat (limited to 'src')
17 files changed, 680 insertions, 800 deletions
diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c index be0140e1..d8be1edc 100644 --- a/src/backend/taler-merchant-httpd.c +++ b/src/backend/taler-merchant-httpd.c @@ -303,6 +303,7 @@ static void do_shutdown (void *cls) { (void) cls; + TMH_force_orders_resume (); TMH_force_reward_resume (); TMH_force_ac_resume (); TMH_force_pc_resume (); @@ -1666,8 +1667,8 @@ url_handler (void *cls, (! hc->rh->skip_instance) ) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Instance `%s' not known\n", - hc->infix); + "Instance for `%s' not known\n", + hc->url); return TALER_MHD_reply_with_error (connection, MHD_HTTP_NOT_FOUND, TALER_EC_MERCHANT_GENERIC_INSTANCE_UNKNOWN, diff --git a/src/backend/taler-merchant-httpd_exchanges.c b/src/backend/taler-merchant-httpd_exchanges.c index 5fca3c34..7e99704c 100644 --- a/src/backend/taler-merchant-httpd_exchanges.c +++ b/src/backend/taler-merchant-httpd_exchanges.c @@ -32,19 +32,6 @@ #define MAX_RETRIES 3 /** - * Minimum delay after which we'll re-fetch key information from the exchange. - */ -#define MIN_RELOAD_DELAY GNUNET_TIME_relative_multiply ( \ - GNUNET_TIME_UNIT_MINUTES, 2) - -/** - * Delay after which we'll allow clients to force us to re-fetch key - * information from the exchange if we don't know the denomination key. - */ -#define FORCED_RELOAD_DELAY GNUNET_TIME_relative_multiply ( \ - GNUNET_TIME_UNIT_MINUTES, 15) - -/** * Threshold after which exponential backoff should not increase. */ #define RETRY_BACKOFF_THRESHOLD GNUNET_TIME_relative_multiply ( \ @@ -52,19 +39,6 @@ /** - * Perform our exponential back-off calculation, starting at 1 ms - * and then going by a factor of 2 up unto a maximum of RETRY_BACKOFF_THRESHOLD. - * - * @param r current backoff time, initially zero - */ -#define RETRY_BACKOFF(r) GNUNET_TIME_relative_min (RETRY_BACKOFF_THRESHOLD, \ - GNUNET_TIME_relative_multiply ( \ - GNUNET_TIME_relative_max ( \ - GNUNET_TIME_UNIT_MILLISECONDS, \ - (r)), 2)); - - -/** * Information we keep for a pending #MMH_EXCHANGES_keys4exchange() operation. */ struct TMH_EXCHANGES_KeysOperation @@ -105,46 +79,6 @@ struct TMH_EXCHANGES_KeysOperation /** - * Information we keep for a pending #MMH_EXCHANGES_wire4exchange() operation. - */ -struct TMH_EXCHANGES_WireOperation -{ - - /** - * Kept in a DLL. - */ - struct TMH_EXCHANGES_WireOperation *next; - - /** - * Kept in a DLL. - */ - struct TMH_EXCHANGES_WireOperation *prev; - - /** - * Function to call with the result. - */ - TMH_EXCHANGES_WireContinuation fc; - - /** - * Closure for @e fc. - */ - void *fc_cls; - - /** - * Exchange we wait for the /wire for. - */ - struct TMH_Exchange *my_exchange; - - /** - * Task scheduled to asynchronously return the result to - * the find continuation. - */ - struct GNUNET_SCHEDULER_Task *at; - -}; - - -/** * Information about wire transfer fees of an exchange, by wire method. */ struct FeesByWireMethod @@ -281,21 +215,6 @@ struct TMH_Exchange struct TMH_EXCHANGES_KeysOperation *keys_tail; /** - * Head of /wire pending for this exchange. - */ - struct TMH_EXCHANGES_WireOperation *w_head; - - /** - * Tail of /wire pending for this exchange. - */ - struct TMH_EXCHANGES_WireOperation *w_tail; - - /** - * Request to /keys needed for our /wire operation. - */ - struct TMH_EXCHANGES_KeysOperation *ko; - - /** * Head of accounts of this exchange. */ struct ExchangeAccount *acc_head; @@ -321,16 +240,6 @@ struct TMH_Exchange struct TALER_EXCHANGE_Keys *keys; /** - * Active /wire request to the exchange, or NULL. - */ - struct TALER_EXCHANGE_WireHandle *wire_request; - - /** - * Task to re-run /wire after some delay. - */ - struct GNUNET_SCHEDULER_Task *wire_task; - - /** * Head of wire fees from /wire request. */ struct FeesByWireMethod *wire_fees_head; @@ -372,25 +281,6 @@ struct TMH_Exchange */ bool trusted; - /** - * true if this exchange did return to use the - * response from /wire. - */ - bool have_wire; - -}; - - -/** - * Opaque wire details abstraction returned to a client after successfully - * obtaining /wire. - */ -struct TMH_ExchangeWireDetails -{ - /** - * Internal representation of the exchange - */ - struct TMH_Exchange *exchange; }; @@ -418,6 +308,16 @@ static struct GNUNET_DB_EventHandler *keys_eh; static int trusted_exchange_count; +const struct TALER_MasterPublicKeyP * +TMH_EXCHANGES_get_master_pub ( + const struct TMH_Exchange *exchange) +{ + GNUNET_break ( (exchange->trusted) || + (NULL != exchange->keys) ); + return &exchange->master_pub; +} + + /** * Free data structures within @a ea, but not @a ea * pointer itself. @@ -590,37 +490,17 @@ keys_mgmt_cb ( * the callback. * * @param exchange the exchange to check for pending find operations - * @return true if we need /wire data from @a exchange */ -static bool +static void process_find_operations (struct TMH_Exchange *exchange) { - struct TMH_EXCHANGES_KeysOperation *fn2; - struct TMH_EXCHANGES_WireOperation *wn; struct GNUNET_TIME_Timestamp now; - fn2 = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Processing find operations for `%s'\n", - exchange->url); - for (struct TMH_EXCHANGES_KeysOperation *fo = exchange->keys_head; - NULL != fo; - fo = fn2) - { - fn2 = fo->next; - fo->fc (fo->fc_cls, - exchange->keys); - TMH_EXCHANGES_keys4exchange_cancel (fo); - } - if (! exchange->have_wire) - return true; now = GNUNET_TIME_timestamp_get (); for (struct FeesByWireMethod *fbw = exchange->wire_fees_head; NULL != fbw; fbw = fbw->next) { - bool removed = false; - while ( (NULL != fbw->af) && (GNUNET_TIME_timestamp_cmp (fbw->af->end_date, <, @@ -630,7 +510,6 @@ process_find_operations (struct TMH_Exchange *exchange) fbw->af = af->next; GNUNET_free (af); - removed = true; } if (NULL == fbw->af) { @@ -638,15 +517,10 @@ process_find_operations (struct TMH_Exchange *exchange) GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Exchange has no wire fees configured for `%s' wire method\n", fbw->wire_method); - if (removed) - { - exchange->have_wire = false; - return true; /* We just removed previous fees, try fetching update */ - } } - if (GNUNET_TIME_timestamp_cmp (fbw->af->start_date, - >, - now)) + else if (GNUNET_TIME_timestamp_cmp (fbw->af->start_date, + >, + now)) { /* Disagreement on the current time */ GNUNET_log (GNUNET_ERROR_TYPE_ERROR, @@ -656,45 +530,30 @@ process_find_operations (struct TMH_Exchange *exchange) fbw->af->start_date.abs_time), true)); } - } + } /* for all wire methods */ - if (! exchange->have_wire) - return true; /* need /wire response to continue */ { - struct TMH_ExchangeWireDetails wd = { - .exchange = exchange - }; + struct TMH_EXCHANGES_KeysOperation *kon; - wn = NULL; - for (struct TMH_EXCHANGES_WireOperation *w = exchange->w_head; - NULL != w; - w = wn) + kon = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Processing find operations for `%s'\n", + exchange->url); + for (struct TMH_EXCHANGES_KeysOperation *ko = exchange->keys_head; + NULL != ko; + ko = kon) { - wn = w->next; - w->fc (w->fc_cls, - &wd); - TMH_EXCHANGES_wire4exchange_cancel (w); + kon = ko->next; + ko->fc (ko->fc_cls, + exchange->keys, + exchange); + TMH_EXCHANGES_keys4exchange_cancel (ko); } } - return false; } /** - * Check if we have any remaining pending requests for the - * given @a exchange, and if we have the required data, call - * the callback. If requests without /wire data remain, - * retry the /wire request after some delay. - * - * Must only be called if 'exchange->keys' is non-NULL. - * - * @param cls a `struct TMH_Exchange` to check - */ -static void -wire_task_cb (void *cls); - - -/** * Function called with information about the wire fees for each wire method. * Stores the wire fees with the exchange for later use. * @@ -994,99 +853,6 @@ outer:; /** - * Callbacks of this type are used to serve the result of submitting a - * wire format inquiry request to a exchange. - * - * If the request fails to generate a valid response from the - * exchange, @a http_status will also be zero. - * - * Must only be called if 'exchange->keys' is non-NULL. - * - * @param cls closure, a `struct TMH_Exchange` - * @param wr response details - */ -static void -handle_wire_data (void *cls, - const struct TALER_EXCHANGE_WireResponse *wr) -{ - struct TMH_Exchange *exchange = cls; - const struct TALER_EXCHANGE_Keys *keys; - enum TALER_ErrorCode ecx; - - exchange->wire_request = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Received /wire response\n"); - if (MHD_HTTP_OK != wr->hr.http_status) - { - struct TMH_EXCHANGES_WireOperation *w; - - exchange->have_wire = false; - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Failed to obtain /wire details from `%s': %u/%d\n", - exchange->url, - wr->hr.http_status, - wr->hr.ec); - while (NULL != (w = exchange->w_head)) - { - w->fc (w->fc_cls, - NULL); - TMH_EXCHANGES_wire4exchange_cancel (w); - } - return; - } - keys = exchange->keys; - GNUNET_assert (NULL != keys); - ecx = process_wire_accounts (exchange, - &keys->master_pub, - wr->details.ok.accounts_len, - wr->details.ok.accounts); - if (TALER_EC_NONE == ecx) - ecx = process_wire_fees (exchange, - &keys->master_pub, - wr->details.ok.fees_len, - wr->details.ok.fees); - if (TALER_EC_NONE != ecx) - { - /* Report hard failure to all callbacks! */ - struct TMH_EXCHANGES_KeysOperation *keys; - - GNUNET_break_op (0); - exchange->have_wire = false; - while (NULL != (keys = exchange->keys_head)) - { - keys->fc (keys->fc_cls, - NULL); - TMH_EXCHANGES_keys4exchange_cancel (keys); - } - return; - } - exchange->have_wire = true; - if ( (process_find_operations (exchange)) && - (NULL == exchange->wire_task) && - (NULL == exchange->wire_request) ) - { - /* need to run /wire again. But as we DID get a successful reply, - and as the exchange is unlikely to offer new wire methods very - frequently, start with some significant delay */ - exchange->wire_retry_delay - = GNUNET_TIME_relative_max (GNUNET_TIME_UNIT_MINUTES, - exchange->wire_retry_delay); - exchange->wire_retry_delay = RETRY_BACKOFF (exchange->wire_retry_delay); - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Need %s/wire, next download attempt in %s\n", - exchange->url, - GNUNET_STRINGS_relative_time_to_string ( - exchange->wire_retry_delay, - true)); - exchange->wire_task - = GNUNET_SCHEDULER_add_delayed (exchange->wire_retry_delay, - &wire_task_cb, - exchange); - } -} - - -/** * Retry getting keys from the given exchange in the closure. * * @param cls the `struct TMH_Exchange *` @@ -1096,46 +862,13 @@ retry_exchange (void *cls) { struct TMH_Exchange *exchange = cls; - /* might be a scheduled reload and not our first attempt */ exchange->retry_task = NULL; - if (NULL != exchange->conn) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Already trying /keys\n"); - return; /* already trying */ - } - if ( (NULL != exchange->keys) && - (GNUNET_TIME_absolute_is_future ( - exchange->keys->key_data_expiration.abs_time)) ) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Already have valid /keys\n"); - if ( (process_find_operations (exchange)) && - (NULL == exchange->wire_request) && - (NULL == exchange->wire_task) ) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Got key data, but also need wire data. Will request /wire now\n"); - exchange->wire_request - = TALER_EXCHANGE_wire ( - TMH_curl_ctx, - exchange->url, - exchange->keys, - &handle_wire_data, - exchange); - } - return; /* still have a valid reply */ - } - /* increment exponential-backoff */ + GNUNET_assert (NULL == exchange->conn); exchange->retry_delay - = RETRY_BACKOFF (exchange->retry_delay); - /* No download until both backoff and #FORCED_RELOAD_DELAY - are satisfied again */ + = GNUNET_TIME_randomized_backoff (exchange->retry_delay, + RETRY_BACKOFF_THRESHOLD); exchange->first_retry - = GNUNET_TIME_relative_to_absolute ( - GNUNET_TIME_relative_max ( - exchange->retry_delay, - FORCED_RELOAD_DELAY)); + = GNUNET_TIME_relative_to_absolute (exchange->retry_delay); GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Fetching /keys from exchange %s in retry_exchange()\n", exchange->url); @@ -1174,6 +907,7 @@ return_keys (void *cls) struct TMH_EXCHANGES_KeysOperation * TMH_EXCHANGES_keys4exchange ( const char *chosen_exchange, + bool force_download, TMH_EXCHANGES_Find2Continuation fc, void *fc_cls) { @@ -1197,6 +931,7 @@ TMH_EXCHANGES_keys4exchange ( exchange->keys_tail, fo); if ( (NULL != exchange->keys) && + (! force_download) && (GNUNET_TIME_absolute_is_future ( exchange->keys->key_data_expiration.abs_time)) ) { @@ -1257,7 +992,7 @@ TMH_EXCHANGES_keys4exchange_cancel ( * @return NULL if we do not have fees for this method yet */ static const struct FeesByWireMethod * -get_wire_fees (struct TMH_Exchange *exchange, +get_wire_fees (const struct TMH_Exchange *exchange, struct GNUNET_TIME_Timestamp now, const char *wire_method) { @@ -1289,26 +1024,6 @@ get_wire_fees (struct TMH_Exchange *exchange, } -static void -wire_task_cb (void *cls) -{ - struct TMH_Exchange *exchange = cls; - - exchange->wire_task = NULL; - if (! process_find_operations (exchange)) - return; /* no more need */ - GNUNET_assert (NULL == exchange->wire_request); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Initiating /wire download\n"); - exchange->wire_request - = TALER_EXCHANGE_wire (TMH_curl_ctx, - exchange->url, - exchange->keys, - &handle_wire_data, - exchange); -} - - /** * Free @a exchange. * @@ -1319,6 +1034,10 @@ free_exchange_entry (struct TMH_Exchange *exchange) { struct FeesByWireMethod *f; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Releasing %s exchange %s\n", + exchange->trusted ? "trusted" : "untrusted", + exchange->url); GNUNET_CONTAINER_DLL_remove (exchange_head, exchange_tail, exchange); @@ -1338,16 +1057,6 @@ free_exchange_entry (struct TMH_Exchange *exchange) GNUNET_free (f->wire_method); GNUNET_free (f); } - if (NULL != exchange->wire_request) - { - TALER_EXCHANGE_wire_cancel (exchange->wire_request); - exchange->wire_request = NULL; - } - if (NULL != exchange->wire_task) - { - GNUNET_SCHEDULER_cancel (exchange->wire_task); - exchange->wire_task = NULL; - } if (NULL != exchange->conn) { TALER_EXCHANGE_get_keys_cancel (exchange->conn); @@ -1362,8 +1071,6 @@ free_exchange_entry (struct TMH_Exchange *exchange) } GNUNET_assert (NULL == exchange->keys_head); GNUNET_assert (NULL == exchange->keys_tail); - GNUNET_assert (NULL == exchange->w_head); - GNUNET_assert (NULL == exchange->w_tail); GNUNET_free (exchange->url); GNUNET_free (exchange); } @@ -1379,31 +1086,17 @@ static void fail_and_retry (struct TMH_Exchange *exchange) { struct TMH_EXCHANGES_KeysOperation *keys; - struct TMH_EXCHANGES_WireOperation *w; - if (NULL != exchange->wire_request) - { - TALER_EXCHANGE_wire_cancel (exchange->wire_request); - exchange->wire_request = NULL; - } - if (NULL != exchange->wire_task) - { - GNUNET_SCHEDULER_cancel (exchange->wire_task); - exchange->wire_task = NULL; - } while (NULL != (keys = exchange->keys_head)) { keys->fc (keys->fc_cls, - NULL); + NULL, + exchange); TMH_EXCHANGES_keys4exchange_cancel (keys); } - while (NULL != (w = exchange->w_head)) - { - w->fc (w->fc_cls, - NULL); - TMH_EXCHANGES_wire4exchange_cancel (w); - } - exchange->retry_delay = RETRY_BACKOFF (exchange->retry_delay); + exchange->retry_delay + = GNUNET_TIME_randomized_backoff (exchange->retry_delay, + RETRY_BACKOFF_THRESHOLD); GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Failed to fetch /keys from `%s'; retrying in %s\n", exchange->url, @@ -1435,6 +1128,31 @@ keys_mgmt_cb (void *cls, fail_and_retry (exchange); return; } + + /* store exchange online signing keys in our DB */ + for (unsigned int i = 0; i<keys->num_sign_keys; i++) + { + struct TALER_EXCHANGE_SigningPublicKey *sign_key = &keys->sign_keys[i]; + enum GNUNET_DB_QueryStatus qs; + + TMH_db->preflight (TMH_db->cls); + qs = TMH_db->insert_exchange_signkey ( + TMH_db->cls, + &keys->master_pub, + &sign_key->key, + sign_key->valid_from, + sign_key->valid_until, + sign_key->valid_legal, + &sign_key->master_sig); + /* 0 is OK, we may already have the key in the DB! */ + if (0 > qs) + { + GNUNET_break (0); + fail_and_retry (exchange); + return; + } + } + qs = TMH_db->insert_exchange_keys (TMH_db->cls, keys); TALER_EXCHANGE_keys_decref (keys); @@ -1450,129 +1168,20 @@ keys_mgmt_cb (void *cls, &es, exchange->url, strlen (exchange->url) + 1); + exchange->retry_delay = GNUNET_TIME_UNIT_ZERO; } } -/** - * Task to return find operation result asynchronously to caller. - * - * @param cls a `struct TMH_EXCHANGES_WireOperation` - */ -static void -return_wire_result (void *cls) -{ - struct TMH_EXCHANGES_WireOperation *w = cls; - struct TMH_Exchange *exchange = w->my_exchange; - - w->at = NULL; - process_find_operations (exchange); -} - - -struct TMH_EXCHANGES_WireOperation * -TMH_EXCHANGES_wire4exchange ( - const char *chosen_exchange, - TMH_EXCHANGES_WireContinuation fc, - void *fc_cls) -{ - struct TMH_Exchange *exchange; - struct TMH_EXCHANGES_WireOperation *w; - - if (NULL == TMH_curl_ctx) - { - GNUNET_break (0); - return NULL; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Trying to find chosen exchange `%s'\n", - chosen_exchange); - exchange = lookup_exchange (chosen_exchange); - w = GNUNET_new (struct TMH_EXCHANGES_WireOperation); - w->fc = fc; - w->fc_cls = fc_cls; - w->my_exchange = exchange; - GNUNET_CONTAINER_DLL_insert (exchange->w_head, - exchange->w_tail, - w); - if (exchange->have_wire) - { - /* We are not currently waiting for a reply, immediately - return result */ - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "The exchange `%s' is ready\n", - exchange->url); - GNUNET_assert (NULL == w->at); - w->at = GNUNET_SCHEDULER_add_now (&return_wire_result, - w); - return w; - } - if (NULL != exchange->wire_request) - return w; /* /wire request is active, do nothing */ - if ( (NULL != exchange->keys) && - (GNUNET_TIME_absolute_is_future ( - exchange->keys->key_data_expiration.abs_time)) ) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Requesting /wire from `%s'\n", - exchange->url); - exchange->wire_request - = TALER_EXCHANGE_wire (TMH_curl_ctx, - exchange->url, - exchange->keys, - &handle_wire_data, - exchange); - return w; - } - if ( (NULL == exchange->keys) && - ( (NULL != exchange->conn) || - (NULL != exchange->retry_task) ) ) - return w; /* /keys request active, do nothing */ - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "No valid keys, fetching /keys at %s\n", - GNUNET_TIME_absolute2s (exchange->first_retry)); - exchange->retry_task - = GNUNET_SCHEDULER_add_at (exchange->first_retry, - &retry_exchange, - exchange); - return w; -} - - -void -TMH_EXCHANGES_wire4exchange_cancel ( - struct TMH_EXCHANGES_WireOperation *w) -{ - struct TMH_Exchange *exchange = w->my_exchange; - - if (NULL != w->at) - { - GNUNET_SCHEDULER_cancel (w->at); - w->at = NULL; - } - GNUNET_CONTAINER_DLL_remove (exchange->w_head, - exchange->w_tail, - w); - GNUNET_free (w); -} - - enum GNUNET_GenericReturnValue TMH_EXCHANGES_lookup_wire_fee ( - const struct TMH_ExchangeWireDetails *wd, + const struct TMH_Exchange *exchange, const char *wire_method, struct TALER_Amount *wire_fee) { - struct TMH_Exchange *exchange = wd->exchange; const struct FeesByWireMethod *fbm; const struct TALER_EXCHANGE_WireAggregateFees *af; - if (! exchange->have_wire) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } fbm = get_wire_fees (exchange, GNUNET_TIME_timestamp_get (), wire_method); @@ -1586,11 +1195,17 @@ TMH_EXCHANGES_lookup_wire_fee ( enum GNUNET_GenericReturnValue TMH_exchange_check_debit ( - const struct TMH_ExchangeWireDetails *wd, + const struct TMH_Exchange *exchange, const struct TMH_WireMethod *wm) { - struct TMH_Exchange *ex = wd->exchange; - for (struct ExchangeAccount *acc = ex->acc_head; + if (NULL == exchange->acc_head) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "No accounts available for %s\n", + exchange->url); + return GNUNET_SYSERR; + } + for (struct ExchangeAccount *acc = exchange->acc_head; NULL != acc; acc = acc->next) { @@ -1632,48 +1247,25 @@ TMH_exchange_check_debit ( } -json_t * -TMH_exchange_get_acceptable (const struct TMH_WireMethod *wm) +void +TMH_exchange_get_trusted (TMH_ExchangeCallback cb, + void *cb_cls) { - json_t *te = json_array (); - - GNUNET_assert (NULL != te); - for (struct TMH_Exchange *exchange = exchange_head; + for (const struct TMH_Exchange *exchange = exchange_head; NULL != exchange; exchange = exchange->next) { - json_t *j_exchange; - unsigned int priority; - if (! exchange->trusted) - continue; - priority = 512; /* medium */ - if (exchange->have_wire) { - struct TMH_ExchangeWireDetails wd = { - .exchange = exchange - }; - - if (GNUNET_OK == - TMH_exchange_check_debit (&wd, - wm)) - priority = 1024; /* high */ - else - priority = 0; /* negative response */ + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Exchange %s not trusted, skipping!\n", + exchange->url); + continue; } - j_exchange = GNUNET_JSON_PACK ( - GNUNET_JSON_pack_string ("url", - exchange->url), - GNUNET_JSON_pack_uint64 ("priority", - priority), - GNUNET_JSON_pack_data_auto ("master_pub", - &exchange->master_pub)); - GNUNET_assert (NULL != j_exchange); - GNUNET_assert (0 == - json_array_append_new (te, - j_exchange)); + cb (cb_cls, + exchange->url, + exchange); } - return te; } @@ -1762,6 +1354,10 @@ accept_exchanges (void *cls, "MASTER_KEY missing in section '%s', not trusting exchange\n", section); } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Setup exchange %s as %s\n", + exchange->url, + exchange->trusted ? "trusted" : "untrusted"); GNUNET_assert (NULL == exchange->retry_task); exchange->retry_task = GNUNET_SCHEDULER_add_now (&retry_exchange, @@ -1785,7 +1381,7 @@ update_exchange_keys (void *cls, const char *url = extra; struct TMH_Exchange *exchange; struct TALER_EXCHANGE_Keys *keys; - struct GNUNET_TIME_Relative delay; + enum TALER_ErrorCode ecx; if ( (NULL == extra) || (0 == extra_len) ) @@ -1810,11 +1406,35 @@ update_exchange_keys (void *cls, GNUNET_break (0); return; } - TALER_EXCHANGE_keys_decref (exchange->keys); - exchange->keys = keys; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Loaded /keys from database with %u accounts\n", + keys->accounts_len); + // FIXME: split into parsing part (do here) + // and DB part (do when receiving /keys!) + ecx = process_wire_accounts (exchange, + &keys->master_pub, + keys->accounts_len, + keys->accounts); + if (TALER_EC_NONE == ecx) + ecx = process_wire_fees (exchange, + &keys->master_pub, + keys->fees_len, + keys->fees); + if (TALER_EC_NONE != ecx) + { + /* Report hard failure to all callbacks! */ + GNUNET_break_op (0); + fail_and_retry (exchange); + TALER_EXCHANGE_keys_decref (keys); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Reloaded /keys of %s from database\n", url); + TALER_EXCHANGE_keys_decref (exchange->keys); + exchange->keys = keys; if ( (exchange->trusted) && (0 != GNUNET_memcmp (&exchange->master_pub, &keys->master_pub)) ) @@ -1843,62 +1463,7 @@ update_exchange_keys (void *cls, } } - /* store exchange online signing keys in our DB */ - for (unsigned int i = 0; i<keys->num_sign_keys; i++) - { - struct TALER_EXCHANGE_SigningPublicKey *sign_key = &keys->sign_keys[i]; - enum GNUNET_DB_QueryStatus qs; - - TMH_db->preflight (TMH_db->cls); - qs = TMH_db->insert_exchange_signkey ( - TMH_db->cls, - &keys->master_pub, - &sign_key->key, - sign_key->valid_from, - sign_key->valid_until, - sign_key->valid_legal, - &sign_key->master_sig); - /* 0 is OK, we may already have the key in the DB! */ - if (0 > qs) - { - GNUNET_break (0); - fail_and_retry (exchange); - return; - } - } - - exchange->first_retry - = GNUNET_TIME_relative_to_absolute (MIN_RELOAD_DELAY); - delay = GNUNET_TIME_absolute_get_remaining ( - keys->key_data_expiration.abs_time); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "/keys response expires at %s! Retrying at that time!\n", - GNUNET_TIME_absolute2s ( - keys->key_data_expiration.abs_time)); - delay = GNUNET_TIME_relative_max (delay, - MIN_RELOAD_DELAY); - exchange->retry_delay = GNUNET_TIME_UNIT_ZERO; - if (NULL != exchange->retry_task) - GNUNET_SCHEDULER_cancel (exchange->retry_task); - exchange->retry_task - = GNUNET_SCHEDULER_add_delayed (delay, - &retry_exchange, - exchange); - - if ( (process_find_operations (exchange)) && - (NULL == exchange->wire_request) && - (NULL == exchange->wire_task) ) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Got key data, but also need wire data. Will request /wire now\n"); - exchange->wire_request - = TALER_EXCHANGE_wire ( - TMH_curl_ctx, - exchange->url, - exchange->keys, - &handle_wire_data, - exchange); - } + process_find_operations (exchange); } diff --git a/src/backend/taler-merchant-httpd_exchanges.h b/src/backend/taler-merchant-httpd_exchanges.h index 89e5deee..b0f2d879 100644 --- a/src/backend/taler-merchant-httpd_exchanges.h +++ b/src/backend/taler-merchant-httpd_exchanges.h @@ -37,19 +37,6 @@ struct TMH_Exchange; /** - * Return the list of exchanges we would find - * acceptable for a payment given the @a wire_method. - * - * @param wm wire method the payment should be - * made with - * @return list of exchanges we accept for @a wire_method - * (and that would accept our bank account) - */ -json_t * -TMH_exchange_get_acceptable (const struct TMH_WireMethod *wm); - - -/** * Parses "trusted" exchanges listed in the configuration. * * @param cfg the configuration @@ -73,30 +60,13 @@ TMH_EXCHANGES_done (void); * * @param cls closure * @param keys the keys of the exchange + * @param exchange representation of the exchange */ typedef void (*TMH_EXCHANGES_Find2Continuation)( void *cls, - struct TALER_EXCHANGE_Keys *keys); - - -/** - * Abstraction of wire knowledge returned internally. - */ -struct TMH_ExchangeWireDetails; - - -/** - * Function called with the result of a #TMH_EXCHANGES_wire4exchange() - * operation. - * - * @param cls closure - * @param wire the wire data of the exchange - */ -typedef void -(*TMH_EXCHANGES_WireContinuation)( - void *cls, - const struct TMH_ExchangeWireDetails *wire); + struct TALER_EXCHANGE_Keys *keys, + struct TMH_Exchange *exchange); /** @@ -104,22 +74,19 @@ typedef void */ struct TMH_EXCHANGES_KeysOperation; -/** - * Information we keep for a pending #MMH_EXCHANGES_wire4exchange() operation. - */ -struct TMH_EXCHANGES_WireOperation; - /** * Get /keys of the given @a exchange. * * @param exchange URL of the exchange we would like to talk to + * @param force_download force /keys download now * @param fc function to call with the handles for the exchange * @param fc_cls closure for @a fc */ struct TMH_EXCHANGES_KeysOperation * TMH_EXCHANGES_keys4exchange ( const char *exchange, + bool force_download, TMH_EXCHANGES_Find2Continuation fc, void *fc_cls); @@ -134,34 +101,47 @@ TMH_EXCHANGES_keys4exchange_cancel (struct TMH_EXCHANGES_KeysOperation *fo); /** - * Get /wire of the given @a exchange. + * Callback on an exchange known to us. Does not warrant + * that the "keys" information is actually available for + * @a exchange. * - * @param exchange URL of the exchange we would like to talk to - * @param fc function to call with the handles for the exchange - * @param fc_cls closure for @a fc + * @param cls closure + * @param url base URL of the exchange + * @param exchange internal handle for the exchange */ -struct TMH_EXCHANGES_WireOperation * -TMH_EXCHANGES_wire4exchange ( - const char *exchange, - TMH_EXCHANGES_WireContinuation fc, - void *fc_cls); +typedef void +(*TMH_ExchangeCallback)(void *cls, + const char *url, + const struct TMH_Exchange *exchange); /** - * Abort pending wire details lookup operation. + * Return all trusted exchanges to @a cb. * - * @param fo handle to operation to abort + * @param cb function to call + * @param cb_cls closure for @a cb */ void -TMH_EXCHANGES_wire4exchange_cancel ( - struct TMH_EXCHANGES_WireOperation *fo); +TMH_exchange_get_trusted (TMH_ExchangeCallback cb, + void *cb_cls); +/** + * Return the master public key of the given @a exchange. + * Will be returned from configuration for trusted + * exchanges. + * + * @param exchange exchange to get master public key for + * @return the master public key of @a exchange + */ +const struct TALER_MasterPublicKeyP * +TMH_EXCHANGES_get_master_pub ( + const struct TMH_Exchange *exchange); /** * Lookup current wire fee by @a exchange_url and * @a wire_method. * - * @param wd wire details of the exchange + * @param exchange the exchange to check * @param wire_method wire method to lookup fee by * @param[out] wire_fee set to the wire fee * @return #GNUNET_OK on success @@ -170,7 +150,7 @@ TMH_EXCHANGES_wire4exchange_cancel ( */ enum GNUNET_GenericReturnValue TMH_EXCHANGES_lookup_wire_fee ( - const struct TMH_ExchangeWireDetails *wd, + const struct TMH_Exchange *exchange, const char *wire_method, struct TALER_Amount *wire_fee); @@ -181,13 +161,13 @@ TMH_EXCHANGES_lookup_wire_fee ( * is trusted and that @a ex allows wire transfers * into the account given in @a wm. * - * @param wd the wire details of the exchange to check + * @param exchange the exchange to check * @param wm the wire method to check with * @return #GNUNET_OK if such a debit can happen */ enum GNUNET_GenericReturnValue TMH_exchange_check_debit ( - const struct TMH_ExchangeWireDetails *wd, + const struct TMH_Exchange *exchange, const struct TMH_WireMethod *wm); diff --git a/src/backend/taler-merchant-httpd_post-orders-ID-abort.c b/src/backend/taler-merchant-httpd_post-orders-ID-abort.c index f63404b4..09ac8acb 100644 --- a/src/backend/taler-merchant-httpd_post-orders-ID-abort.c +++ b/src/backend/taler-merchant-httpd_post-orders-ID-abort.c @@ -497,13 +497,16 @@ refund_cb (void *cls, * * @param cls the `struct AbortContext` * @param keys keys of the exchange + * @param exchange representation of the exchange */ static void process_abort_with_exchange (void *cls, - struct TALER_EXCHANGE_Keys *keys) + struct TALER_EXCHANGE_Keys *keys, + struct TMH_Exchange *exchange) { struct AbortContext *ac = cls; + (void) exchange; ac->fo = NULL; GNUNET_assert (GNUNET_YES == ac->suspended); if (NULL == keys) @@ -584,6 +587,7 @@ find_next_exchange (struct AbortContext *ac) { ac->current_exchange = rdi->exchange_url; ac->fo = TMH_EXCHANGES_keys4exchange (ac->current_exchange, + false, &process_abort_with_exchange, ac); if (NULL == ac->fo) diff --git a/src/backend/taler-merchant-httpd_post-orders-ID-pay.c b/src/backend/taler-merchant-httpd_post-orders-ID-pay.c index 75bc4345..d4e620bd 100644 --- a/src/backend/taler-merchant-httpd_post-orders-ID-pay.c +++ b/src/backend/taler-merchant-httpd_post-orders-ID-pay.c @@ -176,13 +176,6 @@ struct ExchangeGroup struct TMH_EXCHANGES_KeysOperation *fo; /** - * Handle for operation to lookup /wire from - * the exchange used for this transaction; NULL if no operation is - * pending. - */ - struct TMH_EXCHANGES_WireOperation *gwo; - - /** * URL of the exchange that issued this coin. Aliases * the exchange URL of one of the coins, do not free! */ @@ -692,8 +685,6 @@ pay_context_cleanup (void *cls) if (NULL != eg->fo) TMH_EXCHANGES_keys4exchange_cancel (eg->fo); - if (NULL != eg->gwo) - TMH_EXCHANGES_wire4exchange_cancel (eg->gwo); GNUNET_free (eg); } GNUNET_free (pc->egs); @@ -785,14 +776,17 @@ deposit_get_callback ( * * @param cls the `struct KycContext` * @param keys NULL if exchange was not found to be acceptable + * @param exchange representation of the exchange */ static void process_kyc_with_exchange ( void *cls, - struct TALER_EXCHANGE_Keys *keys) + struct TALER_EXCHANGE_Keys *keys, + struct TMH_Exchange *exchange) { struct KycContext *kc = cls; + (void) exchange; kc->fo = NULL; if (NULL == keys) { @@ -922,9 +916,11 @@ check_kyc (struct PayContext *pc, GNUNET_CONTAINER_DLL_insert (kc_head, kc_tail, kc); - kc->fo = TMH_EXCHANGES_keys4exchange (kc->exchange_url, - &process_kyc_with_exchange, - kc); + kc->fo = TMH_EXCHANGES_keys4exchange ( + kc->exchange_url, + false, + &process_kyc_with_exchange, + kc); if (NULL == kc->fo) { GNUNET_break (0); @@ -1146,15 +1142,26 @@ batch_deposit_cb ( /** + * Force re-downloading keys for @a eg. + * + * @param[in,out] eg group to re-download keys for + */ +static void +force_keys (struct ExchangeGroup *eg); + + +/** * Function called with the result of our exchange keys lookup. * * @param cls the `struct ExchangeGroup` * @param keys the keys of the exchange + * @param exchange representation of the exchange */ static void process_pay_with_keys ( void *cls, - struct TALER_EXCHANGE_Keys *keys) + struct TALER_EXCHANGE_Keys *keys, + struct TMH_Exchange *exchange) { struct ExchangeGroup *eg = cls; struct PayContext *pc = eg->pc; @@ -1178,6 +1185,62 @@ process_pay_with_keys ( return; } + if (GNUNET_OK != + TMH_exchange_check_debit (exchange, + pc->wm)) + { + if (eg->tried_force_keys) + { + GNUNET_break_op (0); + pc->pending_at_eg--; + resume_pay_with_error ( + pc, + TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_WIRE_METHOD_UNSUPPORTED, + NULL); + return; + } + eg->tried_force_keys = true; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Forcing /keys download (once) as wire method seems unsupported for debit\n"); + eg->fo = TMH_EXCHANGES_keys4exchange ( + eg->exchange_url, + true, + &process_pay_with_keys, + eg); + if (NULL == eg->fo) + { + GNUNET_break (0); + pc->pending_at_eg--; + resume_pay_with_error (pc, + TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_EXCHANGE_LOOKUP_FAILED, + "Failed to lookup exchange by URL"); + return; + } + return; + } + + if (GNUNET_OK != + TMH_EXCHANGES_lookup_wire_fee (exchange, + pc->wm->wire_method, + &eg->wire_fee)) + { + if (eg->tried_force_keys) + { + pc->pending_at_eg--; + GNUNET_break_op (0); + resume_pay_with_error ( + pc, + TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_WIRE_METHOD_UNSUPPORTED, + pc->wm->wire_method); + return; + } + force_keys (eg); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Got wire data for %s\n", + eg->exchange_url); + /* Initiate /batch-deposit operation for all coins of the current exchange (!) */ group_size = 0; @@ -1198,19 +1261,25 @@ process_pay_with_keys ( &dc->cdd.h_denom_pub); if (NULL == denom_details) { - pc->pending_at_eg--; - resume_pay_with_response ( - pc, - MHD_HTTP_BAD_REQUEST, - TALER_MHD_MAKE_JSON_PACK ( - TALER_JSON_pack_ec ( - TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_DENOMINATION_KEY_NOT_FOUND), - GNUNET_JSON_pack_data_auto ("h_denom_pub", - &dc->cdd.h_denom_pub), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_object_steal ( - "exchange_keys", - TALER_EXCHANGE_keys_to_json (keys))))); + if (eg->tried_force_keys) + { + GNUNET_break_op (0); + pc->pending_at_eg--; + resume_pay_with_response ( + pc, + MHD_HTTP_BAD_REQUEST, + TALER_MHD_MAKE_JSON_PACK ( + TALER_JSON_pack_ec ( + TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_DENOMINATION_KEY_NOT_FOUND), + GNUNET_JSON_pack_data_auto ("h_denom_pub", + &dc->cdd.h_denom_pub), + GNUNET_JSON_pack_allow_null ( + GNUNET_JSON_pack_object_steal ( + "exchange_keys", + TALER_EXCHANGE_keys_to_json (keys))))); + return; + } + force_keys (eg); return; } dc->deposit_fee = denom_details->fees.deposit; @@ -1270,6 +1339,7 @@ process_pay_with_keys ( AGE_FAIL: if (0 < code) { + GNUNET_break_op (0); pc->pending_at_eg--; GNUNET_free (dc->age_commitment.keys); resume_pay_with_response ( @@ -1383,73 +1453,21 @@ AGE_FAIL: } -/** - * Function called with the result of our exchange lookup. - * - * @param cls the `struct ExchangeGroup` - * @param keys the keys of the exchange - */ static void -process_pay_with_wire ( - void *cls, - const struct TMH_ExchangeWireDetails *wire) +force_keys (struct ExchangeGroup *eg) { - struct ExchangeGroup *eg = cls; - struct PayContext *pc = eg->pc; - struct TMH_HandlerContext *hc = pc->hc; - enum GNUNET_GenericReturnValue ret; - - eg->gwo = NULL; - GNUNET_SCHEDULER_begin_async_scope (&hc->async_scope_id); - if (NULL == wire) - { - GNUNET_break_op (0); - pc->pending_at_eg--; - /* FIXME: define more specific error code... */ - resume_pay_with_error ( - pc, - TALER_EC_MERCHANT_GENERIC_EXCHANGE_CONNECT_FAILURE, - NULL); - return; - } - if (GNUNET_OK != - TMH_exchange_check_debit (wire, - pc->wm)) - { - GNUNET_break_op (0); - pc->pending_at_eg--; - resume_pay_with_error ( - pc, - TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_WIRE_METHOD_UNSUPPORTED, - NULL); - return; - } - ret = TMH_EXCHANGES_lookup_wire_fee (wire, - pc->wm->wire_method, - &eg->wire_fee); - if (GNUNET_OK != ret) - { - enum TALER_ErrorCode ec; - - ec = (GNUNET_NO == ret) - ? TALER_EC_MERCHANT_POST_ORDERS_ID_PAY_WIRE_METHOD_UNSUPPORTED - : TALER_EC_MERCHANT_GENERIC_EXCHANGE_WIRE_REQUEST_FAILED; - pc->pending_at_eg--; - GNUNET_break_op (0); - resume_pay_with_error ( - pc, - ec, - pc->wm->wire_method); - return; - } + eg->tried_force_keys = true; GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Got /wire for %s, now going for /keys\n", - eg->exchange_url); - eg->fo = TMH_EXCHANGES_keys4exchange (eg->exchange_url, - &process_pay_with_keys, - eg); + "Forcing /keys download (once) as wire fees are unknown\n"); + eg->fo = TMH_EXCHANGES_keys4exchange ( + eg->exchange_url, + true, + &process_pay_with_keys, + eg); if (NULL == eg->fo) { + struct PayContext *pc = eg->pc; + GNUNET_break (0); pc->pending_at_eg--; resume_pay_with_error (pc, @@ -1489,12 +1507,14 @@ start_batch_deposits (struct PayContext *pc) if (! have_coins) continue; /* no coins left to deposit at this exchange */ GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Getting /wire details for %s\n", + "Getting /keys for %s\n", eg->exchange_url); - eg->gwo = TMH_EXCHANGES_wire4exchange (eg->exchange_url, - &process_pay_with_wire, - eg); - if (NULL == eg->gwo) + eg->fo = TMH_EXCHANGES_keys4exchange ( + eg->exchange_url, + false, + &process_pay_with_keys, + eg); + if (NULL == eg->fo) { GNUNET_break (0); resume_pay_with_error (pc, diff --git a/src/backend/taler-merchant-httpd_post-orders-ID-refund.c b/src/backend/taler-merchant-httpd_post-orders-ID-refund.c index ded6ff9d..f71eb17e 100644 --- a/src/backend/taler-merchant-httpd_post-orders-ID-refund.c +++ b/src/backend/taler-merchant-httpd_post-orders-ID-refund.c @@ -439,14 +439,17 @@ refund_cb (void *cls, * * @param cls a `struct CoinRefund *` * @param keys keys of exchange, NULL on error + * @param exchange representation of the exchange */ static void exchange_found_cb (void *cls, - struct TALER_EXCHANGE_Keys *keys) + struct TALER_EXCHANGE_Keys *keys, + struct TMH_Exchange *exchange) { struct CoinRefund *cr = cls; struct PostRefundData *prd = cr->prd; + (void) exchange; cr->fo = NULL; if (NULL == keys) { @@ -707,6 +710,7 @@ TMH_post_orders_ID_refund (const struct TMH_RequestHandler *rh, { /* We need to talk to the exchange */ cr->fo = TMH_EXCHANGES_keys4exchange (cr->exchange_url, + false, &exchange_found_cb, cr); } 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 b7aa31e1..05347c9f 100644 --- a/src/backend/taler-merchant-httpd_post-rewards-ID-pickup.c +++ b/src/backend/taler-merchant-httpd_post-rewards-ID-pickup.c @@ -384,14 +384,17 @@ withdraw_cb (void *cls, * * @param cls closure, with our `struct PlanchetOperation *` * @param keys keys for the exchange + * @param exchange representation of the exchange */ static void do_withdraw (void *cls, - struct TALER_EXCHANGE_Keys *keys) + struct TALER_EXCHANGE_Keys *keys, + struct TMH_Exchange *exchange) { struct PlanchetOperation *po = cls; struct PickupContext *pc = po->pc; + (void) exchange; po->fo = NULL; TMH_db->preflight (TMH_db->cls); if (NULL == keys) @@ -443,6 +446,7 @@ try_withdraw (struct PickupContext *pc, po->pd = *planchet; po->offset = offset; po->fo = TMH_EXCHANGES_keys4exchange (pc->exchange_url, + false, &do_withdraw, po); GNUNET_assert (NULL != po->fo); @@ -484,13 +488,16 @@ do_timeout (void *cls) * * @param cls closure, with our `struct PickupContext *` * @param keys the keys of the exchange + * @param exchange representation of the exchange */ static void compute_total_requested (void *cls, - struct TALER_EXCHANGE_Keys *keys) + struct TALER_EXCHANGE_Keys *keys, + struct TMH_Exchange *exchange) { struct PickupContext *pc = cls; + (void) exchange; pc->fo = NULL; stop_operations (pc); /* stops timeout job */ if (NULL == keys) @@ -727,6 +734,7 @@ TMH_post_rewards_ID_pickup (const struct TMH_RequestHandler *rh, &do_timeout, pc); pc->fo = TMH_EXCHANGES_keys4exchange (pc->exchange_url, + false, &compute_total_requested, pc); return MHD_YES; diff --git a/src/backend/taler-merchant-httpd_private-get-instances-ID-kyc.c b/src/backend/taler-merchant-httpd_private-get-instances-ID-kyc.c index eda0aaa8..d269402b 100644 --- a/src/backend/taler-merchant-httpd_private-get-instances-ID-kyc.c +++ b/src/backend/taler-merchant-httpd_private-get-instances-ID-kyc.c @@ -650,15 +650,18 @@ exchange_check_cb (void *cls, * * @param cls closure with our `struct ExchangeKycRequest *` * @param keys keys of the exchange context + * @param exchange representation of the exchange */ static void kyc_with_exchange (void *cls, - struct TALER_EXCHANGE_Keys *keys) + struct TALER_EXCHANGE_Keys *keys, + struct TMH_Exchange *exchange) { struct ExchangeKycRequest *ekr = cls; struct KycContext *kc = ekr->kc; struct TALER_PaytoHashP h_payto; + (void) exchange; ekr->fo = NULL; if (NULL == keys) { @@ -738,6 +741,7 @@ kyc_status_cb (void *cls, ekr->last_check = last_check; ekr->kc = kc; ekr->fo = TMH_EXCHANGES_keys4exchange (exchange_url, + false, &kyc_with_exchange, ekr); } diff --git a/src/backend/taler-merchant-httpd_private-get-orders-ID.c b/src/backend/taler-merchant-httpd_private-get-orders-ID.c index 710194f8..f732dc84 100644 --- a/src/backend/taler-merchant-httpd_private-get-orders-ID.c +++ b/src/backend/taler-merchant-httpd_private-get-orders-ID.c @@ -591,14 +591,17 @@ deposit_get_cb (void *cls, * * @param cls closure with a `struct GetOrderRequestContext *` * @param keys keys of the exchange + * @param exchange representation of the exchange */ static void exchange_found_cb (void *cls, - struct TALER_EXCHANGE_Keys *keys) + struct TALER_EXCHANGE_Keys *keys, + struct TMH_Exchange *exchange) { struct TransferQuery *tq = cls; struct GetOrderRequestContext *gorc = tq->gorc; + (void) exchange; tq->fo = NULL; if (NULL == keys) { @@ -678,6 +681,7 @@ deposit_cb (void *cls, tq->amount_with_fee = *amount_with_fee; tq->deposit_fee = *deposit_fee; tq->fo = TMH_EXCHANGES_keys4exchange (exchange_url, + false, &exchange_found_cb, tq); if (NULL == tq->fo) diff --git a/src/backend/taler-merchant-httpd_private-post-orders.c b/src/backend/taler-merchant-httpd_private-post-orders.c index 5646af4d..66cf71a5 100644 --- a/src/backend/taler-merchant-httpd_private-post-orders.c +++ b/src/backend/taler-merchant-httpd_private-post-orders.c @@ -94,6 +94,40 @@ struct InventoryProduct /** + * Handle for a rekey operation where we (re)request + * the /keys from the exchange. + */ +struct RekeyExchange +{ + /** + * Kept in a DLL. + */ + struct RekeyExchange *prev; + + /** + * Kept in a DLL. + */ + struct RekeyExchange *next; + + /** + * order this is for. + */ + struct OrderContext *oc; + + /** + * Base URL of the exchange. + */ + char *url; + + /** + * Request for keys. + */ + struct TMH_EXCHANGES_KeysOperation *fo; + +}; + + +/** * Information we keep per order we are processing. */ struct OrderContext @@ -105,6 +139,16 @@ struct OrderContext struct MHD_Connection *connection; /** + * Kept in a DLL while suspended. + */ + struct OrderContext *next; + + /** + * Kept in a DLL while suspended. + */ + struct OrderContext *prev; + + /** * Handler context for the request. */ struct TMH_HandlerContext *hc; @@ -132,6 +176,12 @@ struct OrderContext json_t *order; /** + * Array of exchanges we find acceptable for this + * order. + */ + json_t *exchanges; + + /** * RFC8905 payment target type to find a matching merchant account */ const char *payment_target; @@ -143,6 +193,18 @@ struct OrderContext const struct TMH_WireMethod *wm; /** + * Forced requests to /keys to update our exchange + * information. + */ + struct RekeyExchange *pending_reload_head; + + /** + * Forced requests to /keys to update our exchange + * information. + */ + struct RekeyExchange *pending_reload_tail; + + /** * Claim token for the request. */ struct TALER_ClaimTokenP claim_token; @@ -158,14 +220,19 @@ struct OrderContext struct InventoryProduct *inventory_products; /** - * Length of the @e uuids array. + * array of UUIDs used to reserve products from @a inventory_products. */ - unsigned int uuids_length; + struct GNUNET_Uuid *uuids; /** - * array of UUIDs used to reserve products from @a inventory_products. + * Shared key to use with @e pos_algorithm. */ - struct GNUNET_Uuid *uuids; + const char *pos_key; + + /** + * Our order ID. Pointer into @e order. + */ + const char *order_id; /** * which product (by offset) is out of stock, UINT_MAX if all were in-stock @@ -173,14 +240,14 @@ struct OrderContext unsigned int out_of_stock_index; /** - * Shared key to use with @e pos_algorithm. + * Length of the @e uuids array. */ - const char *pos_key; + unsigned int uuids_length; /** - * Our order ID. Pointer into @e order. + * #GNUNET_YES if suspended. */ - const char *order_id; + enum GNUNET_GenericReturnValue suspended; /** * Selected algorithm (by template) when we are to @@ -212,10 +279,50 @@ struct OrderContext ORDER_PHASE_FINISHED_MHD_NO } phase; + /** + * Set to true once we are sure that we have at + * least one good exchange. + */ + bool exchange_good; + + /** + * Did we previously force reloading of /keys from + * all exchanges? Set to 'true' to prevent us from + * doing it again (and again...). + */ + bool forced_reload; + }; /** + * Kept in a DLL while suspended. + */ +static struct OrderContext *oc_head; + +/** + * Kept in a DLL while suspended. + */ +static struct OrderContext *oc_tail; + + +void +TMH_force_orders_resume () +{ + struct OrderContext *oc; + + while (NULL != (oc = oc_head)) + { + GNUNET_CONTAINER_DLL_remove (oc_head, + oc_tail, + oc); + oc->suspended = GNUNET_SYSERR; + MHD_resume_connection (oc->connection); + } +} + + +/** * Update the phase of @a oc based on @a mret. * * @param[in,out] oc order to update phase for @@ -285,7 +392,22 @@ static void clean_order (void *cls) { struct OrderContext *oc = cls; + struct RekeyExchange *rx; + while (NULL != (rx = oc->pending_reload_head)) + { + GNUNET_CONTAINER_DLL_remove (oc->pending_reload_head, + oc->pending_reload_tail, + rx); + TMH_EXCHANGES_keys4exchange_cancel (rx->fo); + GNUNET_free (rx->url); + GNUNET_free (rx); + } + if (NULL != oc->exchanges) + { + json_decref (oc->exchanges); + oc->exchanges = NULL; + } GNUNET_array_grow (oc->inventory_products, oc->inventory_products_length, 0); @@ -734,38 +856,196 @@ check_contract (struct OrderContext *oc) /** - * Set list of acceptable exchanges in @a oc. + * Compute the set of exchanges that would be acceptable + * for this order. * - * @param[in,out] oc order context + * @param cls our `struct OrderContext` + * @param url base URL of an exchange (not used) + * @param exchange internal handle for the exchange */ static void -set_exchanges (struct OrderContext *oc) +get_acceptable (void *cls, + const char *url, + const struct TMH_Exchange *exchange) { - json_t *exchanges; + struct OrderContext *oc = cls; + unsigned int priority; + json_t *j_exchange; + enum GNUNET_GenericReturnValue res; - exchanges = TMH_exchange_get_acceptable (oc->wm); - if (0 == json_array_size (exchanges)) + res = TMH_exchange_check_debit (exchange, + oc->wm); + switch (res) + { + case GNUNET_OK: + priority = 1024; /* high */ + oc->exchange_good = true; + break; + case GNUNET_NO: + if (oc->forced_reload) + priority = 0; /* fresh negative response */ + else + priority = 512; /* stale negative response */ + break; + case GNUNET_SYSERR: + if (oc->forced_reload) + priority = 256; /* fresh, no accounts yet */ + else + priority = 768; /* stale, no accounts yet */ + break; + } + j_exchange = GNUNET_JSON_PACK ( + GNUNET_JSON_pack_string ("url", + url), + GNUNET_JSON_pack_uint64 ("priority", + priority), + GNUNET_JSON_pack_data_auto ("master_pub", + TMH_EXCHANGES_get_master_pub (exchange))); + GNUNET_assert (NULL != j_exchange); + GNUNET_assert (0 == + json_array_append_new (oc->exchanges, + j_exchange)); +} + + +/** + * Function called with the result of a #TMH_EXCHANGES_keys4exchange() + * operation. + * + * @param cls closure with our `struct RekeyExchange *` + * @param keys the keys of the exchange + * @param exchange representation of the exchange + */ +static void +keys_forced ( + void *cls, + struct TALER_EXCHANGE_Keys *keys, + struct TMH_Exchange *exchange) +{ + struct RekeyExchange *rx = cls; + struct OrderContext *oc = rx->oc; + + rx->fo = NULL; + GNUNET_CONTAINER_DLL_remove (oc->pending_reload_head, + oc->pending_reload_tail, + rx); + if (NULL == keys) { - json_decref (exchanges); - /* FIXME: maybe there are exchanges for which we simply - did not yet get /wire replies. We should *try* to - run find_exchange() or something to give those - exchanges a chance to respond => need to suspend! */ GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Cannot create order: lacking trusted exchanges for wire method `%s'\n", - oc->wm->wire_method); - reply_with_error ( - oc, - MHD_HTTP_CONFLICT, - TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_NO_EXCHANGES_FOR_WIRE_METHOD, - oc->wm->wire_method); + "Failed to download %s/keys\n", + rx->url); + } + 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 */ +} + + +/** + * Force re-downloading of /keys from @a exchange, + * we currently have no acceptable exchange, so we + * should try to get one. + * + * @param cls closure with our `struct OrderContext` + * @param url base URL of the exchange + * @param exchange internal handle for the exchange + */ +static void +rekey_exchanges (void *cls, + const char *url, + const struct TMH_Exchange *exchange) +{ + struct OrderContext *oc = cls; + struct RekeyExchange *rx; + + rx = GNUNET_new (struct RekeyExchange); + rx->oc = oc; + rx->url = GNUNET_strdup (url); + GNUNET_CONTAINER_DLL_insert (oc->pending_reload_head, + oc->pending_reload_tail, + rx); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Forcing download of %s/keys\n", + url); + rx->fo = TMH_EXCHANGES_keys4exchange (url, + true, + &keys_forced, + rx); +} + + +/** + * Set list of acceptable exchanges in @a oc. + * + * @param[in,out] oc order context + * @return true to suspend execution + */ +static bool +set_exchanges (struct OrderContext *oc) +{ + /* Note: re-building 'oc->exchanges' every time here might be a tad + expensive; could likely consider caching the result if it starts to + matter. */ + if (NULL == oc->exchanges) + oc->exchanges = json_array (); + TMH_exchange_get_trusted (&get_acceptable, + oc); + if (! oc->exchange_good) + { + if (! oc->forced_reload) + { + oc->forced_reload = true; + GNUNET_assert (0 == + json_array_clear (oc->exchanges)); + TMH_exchange_get_trusted (&rekey_exchanges, + oc); + } + if (NULL != oc->pending_reload_head) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Still trying to re-load /keys\n"); + MHD_suspend_connection (oc->connection); + oc->suspended = GNUNET_YES; + GNUNET_CONTAINER_DLL_insert (oc_head, + oc_tail, + oc); + return true; /* reloads pending */ + } + if (0 == json_array_size (oc->exchanges)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Cannot create order: lacking trusted exchanges\n"); + reply_with_error ( + oc, + MHD_HTTP_CONFLICT, + TALER_EC_MERCHANT_PRIVATE_POST_ORDERS_NO_EXCHANGES_FOR_WIRE_METHOD, + oc->wm->wire_method); + return false; + } + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Creating order, but possibly without usable trusted exchanges\n"); } + /* 'set' is correct here: reference in oc->exchanges released in cleanup_order() */ GNUNET_assert (0 == - json_object_set_new (oc->order, - "exchanges", - exchanges)); + json_object_set (oc->order, + "exchanges", + oc->exchanges)); oc->phase++; + return false; } @@ -1603,7 +1883,8 @@ TMH_private_post_orders_with_pos_secrets ( patch_order (oc); break; case ORDER_PHASE_SET_EXCHANGES: - set_exchanges (oc); + if (set_exchanges (oc)) + return MHD_YES; break; case ORDER_PHASE_CHECK_CONTRACT: check_contract (oc); @@ -1625,9 +1906,10 @@ TMH_private_post_orders_with_pos_secrets ( MHD_RESULT -TMH_private_post_orders (const struct TMH_RequestHandler *rh, - struct MHD_Connection *connection, - struct TMH_HandlerContext *hc) +TMH_private_post_orders ( + const struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + struct TMH_HandlerContext *hc) { return TMH_private_post_orders_with_pos_secrets (rh, connection, diff --git a/src/backend/taler-merchant-httpd_private-post-orders.h b/src/backend/taler-merchant-httpd_private-post-orders.h index 9d7b1841..cbbb59c0 100644 --- a/src/backend/taler-merchant-httpd_private-post-orders.h +++ b/src/backend/taler-merchant-httpd_private-post-orders.h @@ -23,6 +23,14 @@ #include "taler-merchant-httpd.h" + +/** + * Force resuming all suspended orders on shutdown. + */ +void +TMH_force_orders_resume (void); + + /** * Generate an order. We add the fields 'exchanges', 'merchant_pub', and * 'H_wire' to the order gotten from the frontend, as well as possibly other diff --git a/src/backend/taler-merchant-httpd_private-post-reserves.c b/src/backend/taler-merchant-httpd_private-post-reserves.c index c6641876..db40b017 100644 --- a/src/backend/taler-merchant-httpd_private-post-reserves.c +++ b/src/backend/taler-merchant-httpd_private-post-reserves.c @@ -89,11 +89,6 @@ struct PostReserveContext struct TMH_EXCHANGES_KeysOperation *fo; /** - * Handle for contacting the exchange for /wire. - */ - struct TMH_EXCHANGES_WireOperation *wo; - - /** * Master public key of the exchange matching * @e exchange_url. */ @@ -127,6 +122,11 @@ struct PostReserveContext * without a response due to shutdown. */ enum GNUNET_GenericReturnValue suspended; + + /** + * True if we already force reloaded /keys. + */ + bool force_reload; }; @@ -168,11 +168,6 @@ TMH_force_rc_resume () TMH_EXCHANGES_keys4exchange_cancel (rc->fo); rc->fo = NULL; } - if (NULL != rc->wo) - { - TMH_EXCHANGES_wire4exchange_cancel (rc->wo); - rc->wo = NULL; - } } } @@ -216,49 +211,17 @@ resume_request (struct PostReserveContext *rc) /** - * Function called once we go the /wire response from the exchange. - * - * @param cls a `struct PostReserveContext *` - * @param wire exchange wire details - */ -static void -wire_cb (void *cls, - const struct TMH_ExchangeWireDetails *wire) -{ - struct PostReserveContext *rc = cls; - - rc->wo = NULL; - rc->accounts = TMH_exchange_accounts_by_method ( - &rc->master_pub, - rc->wire_method); - if (NULL == rc->accounts) - { - rc->ec = TALER_EC_GENERIC_DB_FETCH_FAILED; - rc->http_status = MHD_HTTP_CONFLICT; - resume_request (rc); - return; - } - if (0 == json_array_size (rc->accounts)) - { - rc->ec = TALER_EC_MERCHANT_PRIVATE_POST_RESERVES_UNSUPPORTED_WIRE_METHOD; - rc->http_status = MHD_HTTP_CONFLICT; - resume_request (rc); - return; - } - resume_request (rc); -} - - -/** * Function called with the result of a #TMH_EXCHANGES_keys4exchange() * operation. * * @param cls closure with our `struct PostReserveContext *` * @param keys exchange keys + * @param exchange representation of the exchange */ static void handle_exchange (void *cls, - struct TALER_EXCHANGE_Keys *keys) + struct TALER_EXCHANGE_Keys *keys, + struct TMH_Exchange *exchange) { struct PostReserveContext *rc = cls; @@ -273,6 +236,18 @@ handle_exchange (void *cls, } if (! keys->rewards_allowed) { + if (! rc->force_reload) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Forcing %s/keys reload (rewards not allowed)\n", + rc->exchange_url); + rc->force_reload = true; + rc->fo = TMH_EXCHANGES_keys4exchange (rc->exchange_url, + true, + &handle_exchange, + rc); + return; + } rc->ec = TALER_EC_MERCHANT_PRIVATE_POST_RESERVES_REWARDS_NOT_ALLOWED; rc->http_status = MHD_HTTP_CONFLICT; resume_request (rc); @@ -281,9 +256,33 @@ handle_exchange (void *cls, rc->master_pub = keys->master_pub; rc->reserve_expiration = GNUNET_TIME_relative_to_timestamp (keys->reserve_closing_delay); - rc->wo = TMH_EXCHANGES_wire4exchange (rc->exchange_url, - &wire_cb, - rc); + rc->accounts = TMH_exchange_accounts_by_method ( + &rc->master_pub, + rc->wire_method); + if ( (NULL == rc->accounts) || + (0 == json_array_size (rc->accounts)) ) + { + if (! rc->force_reload) + { + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Forcing %s/keys reload (no accounts)\n", + rc->exchange_url); + rc->force_reload = true; + rc->fo = TMH_EXCHANGES_keys4exchange (rc->exchange_url, + true, + &handle_exchange, + rc); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Wire method `%s' not supported\n", + rc->wire_method); + rc->ec = TALER_EC_MERCHANT_PRIVATE_POST_RESERVES_UNSUPPORTED_WIRE_METHOD; + rc->http_status = MHD_HTTP_CONFLICT; + resume_request (rc); + return; + } + resume_request (rc); } @@ -326,6 +325,7 @@ TMH_private_post_reserves (const struct TMH_RequestHandler *rh, : MHD_NO; } rc->fo = TMH_EXCHANGES_keys4exchange (rc->exchange_url, + false, &handle_exchange, rc); rc->suspended = GNUNET_YES; diff --git a/src/lib/merchant_api_post_order_pay.c b/src/lib/merchant_api_post_order_pay.c index e48b7cd3..24c1cf61 100644 --- a/src/lib/merchant_api_post_order_pay.c +++ b/src/lib/merchant_api_post_order_pay.c @@ -329,6 +329,10 @@ parse_conflict (struct TALER_MERCHANT_OrderPayHandle *oph, break; default: GNUNET_break_op (0); + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unexpected error code %d: %s\n", + ec, + TALER_ErrorCode_get_hint (ec)); return GNUNET_SYSERR; } diff --git a/src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_1/1688579204 b/src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_1/1688579204 Binary files differdeleted file mode 100644 index 413ac682..00000000 --- a/src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_1/1688579204 +++ /dev/null diff --git a/src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_5/1688579204 b/src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_5/1688579204 deleted file mode 100644 index 56735a35..00000000 --- a/src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_5/1688579204 +++ /dev/null @@ -1,2 +0,0 @@ - 0c©ôbL -n#vìïœãpŒap¡¦(Kó°ÕáC
\ No newline at end of file diff --git a/src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_ct_1/1688579204 b/src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_ct_1/1688579204 deleted file mode 100644 index 9378dd7b..00000000 --- a/src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_ct_1/1688579204 +++ /dev/null @@ -1 +0,0 @@ -º|žÑ’Óå‹xjÄÕfóß4½¤3ØÞÜ€òƒ)
\ No newline at end of file diff --git a/src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_ct_10/1688579204 b/src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_ct_10/1688579204 deleted file mode 100644 index 5388599c..00000000 --- a/src/testing/test_merchant_api_home/taler/exchange-secmod-cs/keys/coin_eur_ct_10/1688579204 +++ /dev/null @@ -1 +0,0 @@ -P^ÛÃ(‘0B"ijL´u1ðÌɯä?@dÄÿ±
\ No newline at end of file |