diff options
author | Christian Grothoff <christian@grothoff.org> | 2023-07-11 09:09:27 +0200 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2023-07-11 09:09:27 +0200 |
commit | dc90a82f72469521ba6d50932c2fd52761e1040e (patch) | |
tree | 2e5c1e2238e887864abbe6d44830521ec2c08488 | |
parent | 6299ac5c753a1f58e8729dd6cb19811844cea337 (diff) |
-cache /keys in merchant DB
-rw-r--r-- | src/backend/taler-merchant-httpd.c | 31 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_exchanges.c | 707 | ||||
-rw-r--r-- | src/backenddb/merchant-0001.sql | 6 | ||||
-rw-r--r-- | src/backenddb/pg_insert_exchange_keys.c | 4 | ||||
-rw-r--r-- | src/backenddb/pg_select_exchange_keys.c | 2 |
5 files changed, 425 insertions, 325 deletions
diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c index 1697268b..be0140e1 100644 --- a/src/backend/taler-merchant-httpd.c +++ b/src/backend/taler-merchant-httpd.c @@ -325,12 +325,12 @@ do_shutdown (void *cls) TMH_db->event_listen_cancel (instance_eh); instance_eh = NULL; } + TMH_EXCHANGES_done (); if (NULL != TMH_db) { TALER_MERCHANTDB_plugin_unload (TMH_db); TMH_db = NULL; } - TMH_EXCHANGES_done (); if (NULL != TMH_by_id_map) { GNUNET_CONTAINER_multihashmap_iterate (TMH_by_id_map, @@ -1865,8 +1865,6 @@ load_instances (void *cls, const char *id = extra; (void) cls; - (void) extra; - (void) extra_len; if ( (NULL != extra) && ( (0 == extra_len) || ('\0' != id[extra_len - 1]) ) ) @@ -2045,19 +2043,6 @@ run (void *cls, } /* /static/ is currently not used */ /* (void) TMH_statics_init (); */ - elen = TMH_EXCHANGES_init (config); - if (GNUNET_SYSERR == elen) - { - GNUNET_SCHEDULER_shutdown (); - return; - } - if (0 == elen) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Fatal: no trusted exchanges configured. Exiting.\n"); - GNUNET_SCHEDULER_shutdown (); - return; - } if (NULL == (TMH_by_id_map = GNUNET_CONTAINER_multihashmap_create (4, GNUNET_YES))) @@ -2079,6 +2064,20 @@ run (void *cls, GNUNET_SCHEDULER_shutdown (); return; } + elen = TMH_EXCHANGES_init (config); + if (GNUNET_SYSERR == elen) + { + GNUNET_SCHEDULER_shutdown (); + return; + } + if (0 == elen) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Fatal: no trusted exchanges configured. Exiting.\n"); + GNUNET_SCHEDULER_shutdown (); + return; + } + { struct GNUNET_DB_EventHeaderP es = { .size = ntohs (sizeof (es)), diff --git a/src/backend/taler-merchant-httpd_exchanges.c b/src/backend/taler-merchant-httpd_exchanges.c index d5737369..5fca3c34 100644 --- a/src/backend/taler-merchant-httpd_exchanges.c +++ b/src/backend/taler-merchant-httpd_exchanges.c @@ -21,6 +21,7 @@ */ #include "platform.h" #include <taler/taler_json_lib.h> +#include <taler/taler_dbevents.h> #include "taler-merchant-httpd_exchanges.h" #include "taler-merchant-httpd.h" #include <regex.h> @@ -404,6 +405,12 @@ static struct TMH_Exchange *exchange_head; static struct TMH_Exchange *exchange_tail; /** + * Our event handler listening for /keys downloads + * being put into the database. + */ +static struct GNUNET_DB_EventHandler *keys_eh; + +/** * How many exchanges do we trust (for our configured * currency) as per our configuration? Used for a * sanity-check on startup. @@ -477,6 +484,7 @@ static struct TMH_Exchange * lookup_exchange (const char *exchange_url) { struct TMH_Exchange *exchange; + enum GNUNET_DB_QueryStatus qs; for (exchange = exchange_head; NULL != exchange; @@ -489,6 +497,10 @@ lookup_exchange (const char *exchange_url) GNUNET_CONTAINER_DLL_insert (exchange_head, exchange_tail, exchange); + qs = TMH_db->select_exchange_keys (TMH_db->cls, + exchange->url, + &exchange->keys); + GNUNET_break (qs >= 0); GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "The exchange `%s' is new\n", exchange_url); @@ -573,50 +585,6 @@ keys_mgmt_cb ( /** - * Retry getting keys from the given exchange in the closure. - * - * @param cls the `struct TMH_Exchange *` - */ -static void -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) - return; /* already trying */ - if ( (NULL != exchange->keys) && - (GNUNET_TIME_absolute_is_future ( - exchange->keys->key_data_expiration.abs_time)) ) - return; /* still have a valid reply */ - /* increment exponential-backoff */ - exchange->retry_delay - = RETRY_BACKOFF (exchange->retry_delay); - /* No download until both backoff and #FORCED_RELOAD_DELAY - are satisfied again */ - exchange->first_retry - = GNUNET_TIME_relative_to_absolute ( - GNUNET_TIME_relative_max ( - exchange->retry_delay, - FORCED_RELOAD_DELAY)); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Fetching /keys from exchange %s in retry_exchange()\n", - exchange->url); - exchange->conn - = TALER_EXCHANGE_get_keys ( - TMH_curl_ctx, - exchange->url, - exchange->keys, - &keys_mgmt_cb, - exchange); - /* Note: while the API spec says 'returns NULL on error', the implementation - actually never returns NULL. */ - GNUNET_break (NULL != exchange->conn); -} - - -/** * Check if we have any remaining pending requests for the * given @a exchange, and if we have the required data, call * the callback. @@ -713,99 +681,17 @@ process_find_operations (struct TMH_Exchange *exchange) /** - * Task to asynchronously return keys operation result to caller. + * 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. * - * @param cls a `struct TMH_EXCHANGES_KeysOperation` + * Must only be called if 'exchange->keys' is non-NULL. + * + * @param cls a `struct TMH_Exchange` to check */ static void -return_keys (void *cls) -{ - struct TMH_EXCHANGES_KeysOperation *fo = cls; - struct TMH_Exchange *exchange = fo->my_exchange; - - fo->at = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Returning key data for %s instantly\n", - exchange->url); - process_find_operations (exchange); -} - - -struct TMH_EXCHANGES_KeysOperation * -TMH_EXCHANGES_keys4exchange ( - const char *chosen_exchange, - TMH_EXCHANGES_Find2Continuation fc, - void *fc_cls) -{ - struct TMH_Exchange *exchange; - struct TMH_EXCHANGES_KeysOperation *fo; - - if (NULL == TMH_curl_ctx) - { - GNUNET_break (0); - return NULL; - } - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Trying to find chosen exchange `%s'\n", - chosen_exchange); - exchange = lookup_exchange (chosen_exchange); - fo = GNUNET_new (struct TMH_EXCHANGES_KeysOperation); - fo->fc = fc; - fo->fc_cls = fc_cls; - fo->my_exchange = exchange; - GNUNET_CONTAINER_DLL_insert (exchange->keys_head, - exchange->keys_tail, - fo); - if ( (NULL != exchange->keys) && - (GNUNET_TIME_absolute_is_future ( - exchange->keys->key_data_expiration.abs_time)) ) - { - /* We have a valid reply, immediately return result */ - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "The exchange `%s' is ready\n", - exchange->url); - GNUNET_assert (NULL == fo->at); - fo->at = GNUNET_SCHEDULER_add_now (&return_keys, - fo); - return fo; - } - if ( (NULL == exchange->retry_task) && - (NULL == exchange->conn) ) - { - 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 fo; - } - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Next /keys request scheduled for %s\n", - GNUNET_TIME_absolute2s ( - exchange->first_retry)); - /* No activity to launch, we are already doing so. */ - return fo; -} - - -void -TMH_EXCHANGES_keys4exchange_cancel ( - struct TMH_EXCHANGES_KeysOperation *fo) -{ - struct TMH_Exchange *exchange = fo->my_exchange; - - if (NULL != fo->at) - { - GNUNET_SCHEDULER_cancel (fo->at); - fo->at = NULL; - } - GNUNET_CONTAINER_DLL_remove (exchange->keys_head, - exchange->keys_tail, - fo); - GNUNET_free (fo); -} +wire_task_cb (void *cls); /** @@ -1006,10 +892,11 @@ add_restriction (json_t *restrictions, * @return #TALER_EC_NONE on success */ static enum TALER_ErrorCode -process_wire_accounts (struct TMH_Exchange *exchange, - const struct TALER_MasterPublicKeyP *master_pub, - unsigned int accounts_len, - const struct TALER_EXCHANGE_WireAccount *accounts) +process_wire_accounts ( + struct TMH_Exchange *exchange, + const struct TALER_MasterPublicKeyP *master_pub, + unsigned int accounts_len, + const struct TALER_EXCHANGE_WireAccount accounts[static accounts_len]) { for (unsigned int r = 0; r<MAX_RETRIES; r++) { @@ -1107,61 +994,6 @@ outer:; /** - * Obtain applicable fees for @a exchange and @a wire_method. - * - * @param exchange the exchange to query - * @param now current time - * @param wire_method the wire method we want the fees for - * @return NULL if we do not have fees for this method yet - */ -static const struct FeesByWireMethod * -get_wire_fees (struct TMH_Exchange *exchange, - struct GNUNET_TIME_Timestamp now, - const char *wire_method) -{ - for (struct FeesByWireMethod *fbw = exchange->wire_fees_head; - NULL != fbw; - fbw = fbw->next) - { - if (0 == strcasecmp (fbw->wire_method, - wire_method) ) - { - struct TALER_EXCHANGE_WireAggregateFees *af; - - /* Advance through list up to current time */ - while ( (NULL != (af = fbw->af)) && - (GNUNET_TIME_timestamp_cmp (now, - >=, - af->end_date)) ) - { - fbw->af = af->next; - GNUNET_free (af); - } - return fbw; - } - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Exchange supports `%s' as a wire method (but we do not use that one)\n", - fbw->wire_method); - } - return NULL; -} - - -/** - * 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); - - -/** * Callbacks of this type are used to serve the result of submitting a * wire format inquiry request to a exchange. * @@ -1254,6 +1086,209 @@ handle_wire_data (void *cls, } +/** + * Retry getting keys from the given exchange in the closure. + * + * @param cls the `struct TMH_Exchange *` + */ +static void +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 */ + exchange->retry_delay + = RETRY_BACKOFF (exchange->retry_delay); + /* No download until both backoff and #FORCED_RELOAD_DELAY + are satisfied again */ + exchange->first_retry + = GNUNET_TIME_relative_to_absolute ( + GNUNET_TIME_relative_max ( + exchange->retry_delay, + FORCED_RELOAD_DELAY)); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Fetching /keys from exchange %s in retry_exchange()\n", + exchange->url); + exchange->conn + = TALER_EXCHANGE_get_keys ( + TMH_curl_ctx, + exchange->url, + exchange->keys, + &keys_mgmt_cb, + exchange); + /* Note: while the API spec says 'returns NULL on error', the implementation + actually never returns NULL. */ + GNUNET_break (NULL != exchange->conn); +} + + +/** + * Task to asynchronously return keys operation result to caller. + * + * @param cls a `struct TMH_EXCHANGES_KeysOperation` + */ +static void +return_keys (void *cls) +{ + struct TMH_EXCHANGES_KeysOperation *fo = cls; + struct TMH_Exchange *exchange = fo->my_exchange; + + fo->at = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Returning key data for %s instantly\n", + exchange->url); + process_find_operations (exchange); +} + + +struct TMH_EXCHANGES_KeysOperation * +TMH_EXCHANGES_keys4exchange ( + const char *chosen_exchange, + TMH_EXCHANGES_Find2Continuation fc, + void *fc_cls) +{ + struct TMH_Exchange *exchange; + struct TMH_EXCHANGES_KeysOperation *fo; + + if (NULL == TMH_curl_ctx) + { + GNUNET_break (0); + return NULL; + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Trying to find chosen exchange `%s'\n", + chosen_exchange); + exchange = lookup_exchange (chosen_exchange); + fo = GNUNET_new (struct TMH_EXCHANGES_KeysOperation); + fo->fc = fc; + fo->fc_cls = fc_cls; + fo->my_exchange = exchange; + GNUNET_CONTAINER_DLL_insert (exchange->keys_head, + exchange->keys_tail, + fo); + if ( (NULL != exchange->keys) && + (GNUNET_TIME_absolute_is_future ( + exchange->keys->key_data_expiration.abs_time)) ) + { + /* We have a valid reply, immediately return result */ + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "The exchange `%s' is ready\n", + exchange->url); + GNUNET_assert (NULL == fo->at); + fo->at = GNUNET_SCHEDULER_add_now (&return_keys, + fo); + return fo; + } + if ( (NULL == exchange->retry_task) && + (NULL == exchange->conn) ) + { + 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 fo; + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Next /keys request scheduled for %s\n", + GNUNET_TIME_absolute2s ( + exchange->first_retry)); + /* No activity to launch, we are already doing so. */ + return fo; +} + + +void +TMH_EXCHANGES_keys4exchange_cancel ( + struct TMH_EXCHANGES_KeysOperation *fo) +{ + struct TMH_Exchange *exchange = fo->my_exchange; + + if (NULL != fo->at) + { + GNUNET_SCHEDULER_cancel (fo->at); + fo->at = NULL; + } + GNUNET_CONTAINER_DLL_remove (exchange->keys_head, + exchange->keys_tail, + fo); + GNUNET_free (fo); +} + + +/** + * Obtain applicable fees for @a exchange and @a wire_method. + * + * @param exchange the exchange to query + * @param now current time + * @param wire_method the wire method we want the fees for + * @return NULL if we do not have fees for this method yet + */ +static const struct FeesByWireMethod * +get_wire_fees (struct TMH_Exchange *exchange, + struct GNUNET_TIME_Timestamp now, + const char *wire_method) +{ + for (struct FeesByWireMethod *fbw = exchange->wire_fees_head; + NULL != fbw; + fbw = fbw->next) + { + if (0 == strcasecmp (fbw->wire_method, + wire_method) ) + { + struct TALER_EXCHANGE_WireAggregateFees *af; + + /* Advance through list up to current time */ + while ( (NULL != (af = fbw->af)) && + (GNUNET_TIME_timestamp_cmp (now, + >=, + af->end_date)) ) + { + fbw->af = af->next; + GNUNET_free (af); + } + return fbw; + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Exchange supports `%s' as a wire method (but we do not use that one)\n", + fbw->wire_method); + } + return NULL; +} + + static void wire_task_cb (void *cls) { @@ -1339,11 +1374,9 @@ free_exchange_entry (struct TMH_Exchange *exchange) * about our failure, abort pending operations and retry later. * * @param exchange exchange that failed - * @param hr details about the HTTP reply */ static void -fail_and_retry (struct TMH_Exchange *exchange, - const struct TALER_EXCHANGE_HttpResponse *hr) +fail_and_retry (struct TMH_Exchange *exchange) { struct TMH_EXCHANGES_KeysOperation *keys; struct TMH_EXCHANGES_WireOperation *w; @@ -1370,18 +1403,10 @@ fail_and_retry (struct TMH_Exchange *exchange, NULL); TMH_EXCHANGES_wire4exchange_cancel (w); } - if (TALER_EC_GENERIC_CONFIGURATION_INVALID == hr->ec) - { - /* This can NEVER work, so don't retry */ - free_exchange_entry (exchange); - return; - } exchange->retry_delay = RETRY_BACKOFF (exchange->retry_delay); GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Failed to fetch /keys from `%s': %d/%u, retrying in %s\n", + "Failed to fetch /keys from `%s'; retrying in %s\n", exchange->url, - (int) hr->ec, - hr->http_status, GNUNET_STRINGS_relative_time_to_string (exchange->retry_delay, true)); if (NULL != exchange->retry_task) @@ -1402,101 +1427,29 @@ keys_mgmt_cb (void *cls, struct TALER_EXCHANGE_Keys *keys) { struct TMH_Exchange *exchange = cls; - struct GNUNET_TIME_Relative delay; + enum GNUNET_DB_QueryStatus qs; exchange->conn = NULL; if (MHD_HTTP_OK != kr->hr.http_status) { - fail_and_retry (exchange, - &kr->hr); + fail_and_retry (exchange); return; } - TALER_EXCHANGE_keys_decref (exchange->keys); - exchange->keys = keys; - if ( (exchange->trusted) && - (0 != GNUNET_memcmp (&exchange->master_pub, - &keys->master_pub)) ) + qs = TMH_db->insert_exchange_keys (TMH_db->cls, + keys); + TALER_EXCHANGE_keys_decref (keys); + GNUNET_break (qs > 0); + if (qs > 0) { - /* master pub differs => do not trust the exchange (without auditor) */ - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Master public key of exchange `%s' differs from our configuration. Not trusting exchange.\n", - exchange->url); - exchange->trusted = false; - } - if (! exchange->trusted) - { - exchange->master_pub = keys->master_pub; - for (struct TMH_Exchange *e = exchange_head; - NULL != e; - e = e->next) - { - if (e == exchange) - continue; - if (! e->trusted) - continue; - if (0 == - GNUNET_memcmp (&e->master_pub, - &exchange->master_pub)) - exchange->trusted = true; /* same exchange, different URL => trust applies */ - } - } - - /* 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, - &kr->hr); - 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); + struct GNUNET_DB_EventHeaderP es = { + .size = ntohs (sizeof (es)), + .type = ntohs (TALER_DBEVENT_MERCHANT_EXCHANGE_KEYS) + }; - 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); + TMH_db->event_notify (TMH_db->cls, + &es, + exchange->url, + strlen (exchange->url) + 1); } } @@ -1777,8 +1730,8 @@ accept_exchanges (void *cls, "EXCHANGE_BASE_URL"); return; } - exchange = GNUNET_new (struct TMH_Exchange); - exchange->url = url; + exchange = lookup_exchange (url); + GNUNET_free (url); if (GNUNET_OK == GNUNET_CONFIGURATION_get_value_string (cfg, section, @@ -1786,10 +1739,10 @@ accept_exchanges (void *cls, &mks)) { if (GNUNET_OK == - GNUNET_CRYPTO_eddsa_public_key_from_string (mks, - strlen (mks), - &exchange->master_pub. - eddsa_pub)) + GNUNET_CRYPTO_eddsa_public_key_from_string ( + mks, + strlen (mks), + &exchange->master_pub.eddsa_pub)) { exchange->trusted = true; trusted_exchange_count++; @@ -1809,12 +1762,143 @@ accept_exchanges (void *cls, "MASTER_KEY missing in section '%s', not trusting exchange\n", section); } - GNUNET_CONTAINER_DLL_insert (exchange_head, - exchange_tail, - exchange); GNUNET_assert (NULL == exchange->retry_task); - exchange->retry_task = GNUNET_SCHEDULER_add_now (&retry_exchange, - exchange); + exchange->retry_task + = GNUNET_SCHEDULER_add_now (&retry_exchange, + exchange); +} + + +/** + * Trigger (re)loading of keys from DB. + * + * @param cls NULL + * @param extra base URL of the exchange that changed + * @param extra_len number of bytes in @a extra + */ +static void +update_exchange_keys (void *cls, + const void *extra, + size_t extra_len) +{ + enum GNUNET_DB_QueryStatus qs; + const char *url = extra; + struct TMH_Exchange *exchange; + struct TALER_EXCHANGE_Keys *keys; + struct GNUNET_TIME_Relative delay; + + if ( (NULL == extra) || + (0 == extra_len) ) + { + GNUNET_break (0); + return; + } + if ('\0' != url[extra_len - 1]) + { + GNUNET_break (0); + return; + } + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Received keys change notification: reload `%s'\n", + url); + exchange = lookup_exchange (url); + qs = TMH_db->select_exchange_keys (TMH_db->cls, + url, + &keys); + if (qs <= 0) + { + GNUNET_break (0); + return; + } + TALER_EXCHANGE_keys_decref (exchange->keys); + exchange->keys = keys; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Reloaded /keys of %s from database\n", + url); + if ( (exchange->trusted) && + (0 != GNUNET_memcmp (&exchange->master_pub, + &keys->master_pub)) ) + { + /* master pub differs => do not trust the exchange (without auditor) */ + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Master public key of exchange `%s' differs from our configuration. Not trusting exchange.\n", + exchange->url); + exchange->trusted = false; + } + if (! exchange->trusted) + { + exchange->master_pub = keys->master_pub; + for (struct TMH_Exchange *e = exchange_head; + NULL != e; + e = e->next) + { + if (e == exchange) + continue; + if (! e->trusted) + continue; + if (0 == + GNUNET_memcmp (&e->master_pub, + &exchange->master_pub)) + exchange->trusted = true; /* same exchange, different URL => trust applies */ + } + } + + /* 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); + } } @@ -1822,6 +1906,18 @@ enum GNUNET_GenericReturnValue TMH_EXCHANGES_init (const struct GNUNET_CONFIGURATION_Handle *cfg) { /* get exchanges from the merchant configuration and try to connect to them */ + { + struct GNUNET_DB_EventHeaderP es = { + .size = ntohs (sizeof (es)), + .type = ntohs (TALER_DBEVENT_MERCHANT_EXCHANGE_KEYS) + }; + + keys_eh = TMH_db->event_listen (TMH_db->cls, + &es, + GNUNET_TIME_UNIT_FOREVER_REL, + &update_exchange_keys, + NULL); + } GNUNET_CONFIGURATION_iterate_sections (cfg, &accept_exchanges, (void *) cfg); @@ -1833,6 +1929,11 @@ TMH_EXCHANGES_init (const struct GNUNET_CONFIGURATION_Handle *cfg) void TMH_EXCHANGES_done () { + if (NULL != keys_eh) + { + TMH_db->event_listen_cancel (keys_eh); + keys_eh = NULL; + } while (NULL != exchange_head) free_exchange_entry (exchange_head); } diff --git a/src/backenddb/merchant-0001.sql b/src/backenddb/merchant-0001.sql index 313d8ee9..7bd8d737 100644 --- a/src/backenddb/merchant-0001.sql +++ b/src/backenddb/merchant-0001.sql @@ -763,14 +763,14 @@ COMMENT ON COLUMN merchant_exchange_accounts.credit_restrictions CREATE TABLE IF NOT EXISTS merchant_exchange_keys (mek_serial BIGINT GENERATED BY DEFAULT AS IDENTITY UNIQUE - ,exchange_url VARCHAR PRIMARY KEY CHECK + ,exchange_url VARCHAR PRIMARY KEY ,keys_json VARCHAR NOT NULL ,expiration_time INT8 NOT NULL ); COMMENT ON TABLE merchant_exchange_keys IS 'Here we store the cached /keys response from an exchange in JSON format'; -COMMENT ON COLUMN merchant_exchange_keys.master_pub - IS 'Master public key of the exchange with these keys'; +COMMENT ON COLUMN merchant_exchange_keys.exchange_url + IS 'Base URL of the exchange with these keys'; COMMENT ON COLUMN merchant_exchange_keys.keys_json IS 'JSON string of the /keys as generated by libtalerexchange'; COMMENT ON COLUMN merchant_exchange_keys.expiration_time diff --git a/src/backenddb/pg_insert_exchange_keys.c b/src/backenddb/pg_insert_exchange_keys.c index a38c8359..e3e9e84a 100644 --- a/src/backenddb/pg_insert_exchange_keys.c +++ b/src/backenddb/pg_insert_exchange_keys.c @@ -33,9 +33,9 @@ TMH_PG_insert_exchange_keys (void *cls, struct PostgresClosure *pg = cls; json_t *jkeys = TALER_EXCHANGE_keys_to_json (keys); struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (keys->exchange_url), - GNUNET_PQ_query_param_timestamp (&keys->last_denom_issue_date), TALER_PQ_query_param_json (jkeys), + GNUNET_PQ_query_param_timestamp (&keys->last_denom_issue_date), + GNUNET_PQ_query_param_string (keys->exchange_url), GNUNET_PQ_query_param_end }; enum GNUNET_DB_QueryStatus qs; diff --git a/src/backenddb/pg_select_exchange_keys.c b/src/backenddb/pg_select_exchange_keys.c index 9b9aaf60..926319d1 100644 --- a/src/backenddb/pg_select_exchange_keys.c +++ b/src/backenddb/pg_select_exchange_keys.c @@ -38,7 +38,7 @@ TMH_PG_select_exchange_keys (void *cls, }; json_t *jkeys; struct GNUNET_PQ_ResultSpec rs[] = { - TALER_PQ_result_spec_json ("keys", + TALER_PQ_result_spec_json ("keys_json", &jkeys), GNUNET_PQ_result_spec_end }; |