summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2023-07-11 09:09:27 +0200
committerChristian Grothoff <christian@grothoff.org>2023-07-11 09:09:27 +0200
commitdc90a82f72469521ba6d50932c2fd52761e1040e (patch)
tree2e5c1e2238e887864abbe6d44830521ec2c08488
parent6299ac5c753a1f58e8729dd6cb19811844cea337 (diff)
downloadmerchant-dc90a82f72469521ba6d50932c2fd52761e1040e.tar.gz
merchant-dc90a82f72469521ba6d50932c2fd52761e1040e.tar.bz2
merchant-dc90a82f72469521ba6d50932c2fd52761e1040e.zip
-cache /keys in merchant DB
-rw-r--r--src/backend/taler-merchant-httpd.c31
-rw-r--r--src/backend/taler-merchant-httpd_exchanges.c707
-rw-r--r--src/backenddb/merchant-0001.sql6
-rw-r--r--src/backenddb/pg_insert_exchange_keys.c4
-rw-r--r--src/backenddb/pg_select_exchange_keys.c2
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
};