[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[taler-merchant] 02/02: first implementation of exchangekeyupdate
From: |
gnunet |
Subject: |
[taler-merchant] 02/02: first implementation of exchangekeyupdate |
Date: |
Fri, 06 Sep 2024 22:09:35 +0200 |
This is an automated email from the git hooks/post-receive script.
grothoff pushed a commit to branch master
in repository merchant.
commit 58a20632468cbac1e72b8b353aec6743e4f243c6
Author: Christian Grothoff <christian@grothoff.org>
AuthorDate: Fri Sep 6 22:09:30 2024 +0200
first implementation of exchangekeyupdate
---
src/backend/taler-merchant-exchangekeyupdate.c | 825 ++++++++++++++++++++-----
1 file changed, 655 insertions(+), 170 deletions(-)
diff --git a/src/backend/taler-merchant-exchangekeyupdate.c
b/src/backend/taler-merchant-exchangekeyupdate.c
index c3ded9d1..4790953a 100644
--- a/src/backend/taler-merchant-exchangekeyupdate.c
+++ b/src/backend/taler-merchant-exchangekeyupdate.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2023 Taler Systems SA
+ Copyright (C) 2023, 2024 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free
Software
@@ -28,24 +28,21 @@
#include "taler_merchantdb_plugin.h"
/**
- * Timeout for the exchange interaction. Rather long as we should do
- * long-polling and do not want to wake up too often.
+ * Maximum frequency for the exchange interaction.
*/
-#define EXCHANGE_TIMEOUT GNUNET_TIME_relative_multiply ( \
+#define EXCHANGE_MAXFREQ GNUNET_TIME_relative_multiply ( \
GNUNET_TIME_UNIT_MINUTES, \
- 30)
+ 5)
/**
* How many inquiries do we process concurrently at most.
*/
#define OPEN_INQUIRY_LIMIT 1024
-
/**
- * Information about an inquiry job.
+ * How often do we retry after DB serialization errors (at most)?
*/
-struct Inquiry;
-
+#define MAX_RETRIES 3
/**
* Information about an exchange.
@@ -63,17 +60,22 @@ struct Exchange
struct Exchange *prev;
/**
- * Which exchange are we tracking here.
+ * Base URL of the exchange are we tracking here.
*/
char *exchange_url;
/**
- * A connection to this exchange
+ * Expected currency of the exchange.
+ */
+ char *currency;
+
+ /**
+ * A /keys request to this exchange, NULL if not active.
*/
struct TALER_EXCHANGE_GetKeysHandle *conn;
/**
- * The keys of this exchange
+ * The keys of this exchange, NULL if not known.
*/
struct TALER_EXCHANGE_Keys *keys;
@@ -82,6 +84,11 @@ struct Exchange
*/
struct GNUNET_SCHEDULER_Task *retry_task;
+ /**
+ * Master public key expected for this exchange.
+ */
+ struct TALER_MasterPublicKeyP master_pub;
+
/**
* How soon can may we, at the earliest, re-download /keys?
*/
@@ -89,22 +96,15 @@ struct Exchange
/**
* How long should we wait between the next retry?
+ * Used for exponential back-offs.
*/
struct GNUNET_TIME_Relative retry_delay;
/**
- * How long should we wait between requests
- * for transfer details?
+ * Are we waiting for /keys downloads due to our
+ * hard limit?
*/
- struct GNUNET_TIME_Relative transfer_delay;
-
- /**
- * False to indicate that there is an ongoing
- * /keys transfer we are waiting for;
- * true to indicate that /keys data is up-to-date.
- */
- bool ready;
-
+ bool limited;
};
@@ -128,6 +128,11 @@ static const struct GNUNET_CONFIGURATION_Handle *cfg;
*/
static struct TALER_MERCHANTDB_Plugin *db_plugin;
+/**
+ * Our event handler listening for /keys forced downloads.
+ */
+static struct GNUNET_DB_EventHandler *eh;
+
/**
* Handle to the context for interacting with the bank.
*/
@@ -138,21 +143,11 @@ static struct GNUNET_CURL_Context *ctx;
*/
static struct GNUNET_CURL_RescheduleContext *rc;
-/**
- * Main task for #find_work().
- */
-static struct GNUNET_SCHEDULER_Task *task;
-
/**
* How many active inquiries do we have right now.
*/
static unsigned int active_inquiries;
-/**
- * Set to true if we ever encountered any problem.
- */
-static bool found_problem;
-
/**
* Value to return from main(). 0 on success, non-zero on errors.
*/
@@ -182,36 +177,32 @@ download_keys (void *cls);
/**
- * Finds new transfers that require work in the merchant database.
- *
- * @param cls NULL
- */
-static void
-find_work (void *cls);
-
-
-/**
- * Free resources of @a e.
- *
- * @param[in] e inquiry job to terminate
+ * An inquiry finished, check if we need to start more.
*/
static void
-end_inquiry (struct Exchange *e)
+end_inquiry (void)
{
GNUNET_assert (active_inquiries > 0);
-
active_inquiries--;
if ( (active_inquiries < OPEN_INQUIRY_LIMIT / 2) &&
- (NULL == task) &&
(at_limit) )
{
at_limit = false;
- GNUNET_assert (NULL == task);
- task = GNUNET_SCHEDULER_add_now (&find_work,
- NULL);
+ for (struct Exchange *e = e_head;
+ NULL != e;
+ e = e->next)
+ {
+ if (! e->limited)
+ continue;
+ e->limited = false;
+ /* done synchronously so that the active_inquiries
+ is updated immediately */
+ download_keys (e);
+ if (at_limit)
+ break;
+ }
}
- if ( (NULL == task) &&
- (! at_limit) &&
+ if ( (! at_limit) &&
(0 == active_inquiries) &&
(test_mode) )
{
@@ -223,6 +214,268 @@ end_inquiry (struct Exchange *e)
}
+/**
+ * Add account restriction @a a to array of @a restrictions.
+ *
+ * @param[in,out] restrictions JSON array to build
+ * @param r restriction to add to @a restrictions
+ * @return #GNUNET_SYSERR if @a r is malformed
+ */
+static enum GNUNET_GenericReturnValue
+add_restriction (json_t *restrictions,
+ const struct TALER_EXCHANGE_AccountRestriction *r)
+{
+ json_t *jr;
+
+ jr = NULL;
+ switch (r->type)
+ {
+ case TALER_EXCHANGE_AR_INVALID:
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ case TALER_EXCHANGE_AR_DENY:
+ jr = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string ("type",
+ "deny")
+ );
+ break;
+ case TALER_EXCHANGE_AR_REGEX:
+ jr = GNUNET_JSON_PACK (
+ GNUNET_JSON_pack_string (
+ "type",
+ "regex"),
+ GNUNET_JSON_pack_string (
+ "regex",
+ r->details.regex.posix_egrep),
+ GNUNET_JSON_pack_string (
+ "human_hint",
+ r->details.regex.human_hint),
+ GNUNET_JSON_pack_object_incref (
+ "human_hint_i18n",
+ (json_t *) r->details.regex.human_hint_i18n)
+ );
+ break;
+ }
+ if (NULL == jr)
+ {
+ GNUNET_break_op (0);
+ return GNUNET_SYSERR;
+ }
+ GNUNET_assert (0 ==
+ json_array_append_new (restrictions,
+ jr));
+ return GNUNET_OK;
+
+}
+
+
+/**
+ * Update our information in the database about the
+ * /keys of an exchange. Run inside of a database
+ * transaction scope that will re-try and/or commit
+ * depending on the return value.
+ *
+ * @param keys information to persist
+ * @return transaction status
+ */
+static enum GNUNET_DB_QueryStatus
+insert_keys_data (const struct TALER_EXCHANGE_Keys *keys)
+{
+ enum GNUNET_DB_QueryStatus qs;
+
+ /* store exchange online signing keys in our DB */
+ for (unsigned int i = 0; i<keys->num_sign_keys; i++)
+ {
+ const struct TALER_EXCHANGE_SigningPublicKey *sign_key
+ = &keys->sign_keys[i];
+
+ qs = db_plugin->insert_exchange_signkey (
+ db_plugin->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 (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ return qs;
+ }
+ }
+
+ qs = db_plugin->insert_exchange_keys (db_plugin->cls,
+ keys);
+ if (0 > qs)
+ {
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ return qs;
+ }
+
+ qs = db_plugin->delete_exchange_accounts (db_plugin->cls,
+ &keys->master_pub);
+ if (0 > qs)
+ {
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ return qs;
+ }
+
+ for (unsigned int i = 0; i<keys->accounts_len; i++)
+ {
+ const struct TALER_EXCHANGE_WireAccount *account
+ = &keys->accounts[i];
+ json_t *debit_restrictions;
+ json_t *credit_restrictions;
+
+ debit_restrictions = json_array ();
+ GNUNET_assert (NULL != debit_restrictions);
+ credit_restrictions = json_array ();
+ GNUNET_assert (NULL != credit_restrictions);
+ for (unsigned int j = 0; j<account->debit_restrictions_length; j++)
+ {
+ if (GNUNET_OK !=
+ add_restriction (debit_restrictions,
+ &account->debit_restrictions[j]))
+ {
+ db_plugin->rollback (db_plugin->cls);
+ GNUNET_break (0);
+ json_decref (debit_restrictions);
+ json_decref (credit_restrictions);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+ }
+ for (unsigned int j = 0; j<account->credit_restrictions_length; j++)
+ {
+ if (GNUNET_OK !=
+ add_restriction (credit_restrictions,
+ &account->credit_restrictions[j]))
+ {
+ db_plugin->rollback (db_plugin->cls);
+ GNUNET_break (0);
+ json_decref (debit_restrictions);
+ json_decref (credit_restrictions);
+ return GNUNET_DB_STATUS_HARD_ERROR;
+ }
+ }
+ qs = db_plugin->insert_exchange_account (
+ db_plugin->cls,
+ &keys->master_pub,
+ account->payto_uri,
+ account->conversion_url,
+ debit_restrictions,
+ credit_restrictions,
+ &account->master_sig);
+ json_decref (debit_restrictions);
+ json_decref (credit_restrictions);
+ if (qs < 0)
+ {
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ return qs;
+ }
+ } /* end 'for all accounts' */
+
+ for (unsigned int i = 0; i<keys->fees_len; i++)
+ {
+ const struct TALER_EXCHANGE_WireFeesByMethod *fbm
+ = &keys->fees[i];
+ const char *wire_method = fbm->method;
+ const struct TALER_EXCHANGE_WireAggregateFees *fees
+ = fbm->fees_head;
+
+ while (NULL != fees)
+ {
+ struct GNUNET_HashCode h_wire_method;
+
+ GNUNET_CRYPTO_hash (wire_method,
+ strlen (wire_method) + 1,
+ &h_wire_method);
+ qs = db_plugin->store_wire_fee_by_exchange (
+ db_plugin->cls,
+ &keys->master_pub,
+ &h_wire_method,
+ &fees->fees,
+ fees->start_date,
+ fees->end_date,
+ &fees->master_sig);
+ if (0 > qs)
+ {
+ GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR == qs);
+ return qs;
+ }
+ fees = fees->next;
+ } /* all fees for this method */
+ } /* for all methods (i) */
+
+ {
+ struct GNUNET_DB_EventHeaderP es = {
+ .size = ntohs (sizeof (es)),
+ .type = ntohs (TALER_DBEVENT_MERCHANT_EXCHANGE_KEYS)
+ };
+
+ db_plugin->event_notify (db_plugin->cls,
+ &es,
+ keys->exchange_url,
+ strlen (keys->exchange_url) + 1);
+ }
+ return qs;
+}
+
+
+/**
+ * Run database transaction to store the @a keys in
+ * the merchant database (and notify other processes
+ * that may care about them).
+ *
+ * @param keys the keys to store
+ * @return true on success
+ */
+static bool
+store_keys (struct TALER_EXCHANGE_Keys *keys)
+{
+ enum GNUNET_DB_QueryStatus qs;
+
+ db_plugin->preflight (db_plugin->cls);
+ for (unsigned int r = 0; r<MAX_RETRIES; r++)
+ {
+ if (GNUNET_OK !=
+ db_plugin->start (db_plugin->cls,
+ "update exchange key data"))
+ {
+ db_plugin->rollback (db_plugin->cls);
+ GNUNET_break (0);
+ return false;
+ }
+
+ qs = insert_keys_data (keys);
+ if (qs < 0)
+ {
+ db_plugin->rollback (db_plugin->cls);
+ if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+ continue;
+ GNUNET_break (0);
+ return false;
+ }
+
+ qs = db_plugin->commit (db_plugin->cls);
+ if (qs < 0)
+ {
+ db_plugin->rollback (db_plugin->cls);
+ if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
+ continue;
+ GNUNET_break (0);
+ return false;
+ }
+ } /* end of retry loop */
+ if (qs < 0)
+ {
+ GNUNET_break (0);
+ return false;
+ }
+ return true;
+}
+
+
/**
* Function called with information about who is auditing
* a particular exchange and what keys the exchange is using.
@@ -241,18 +494,39 @@ cert_cb (
struct GNUNET_TIME_Absolute n;
e->conn = NULL;
+ /* limit retry */
+ e->first_retry
+ = GNUNET_TIME_relative_to_absolute (
+ EXCHANGE_MAXFREQ);
switch (kr->hr.http_status)
{
case MHD_HTTP_OK:
- e->ready = true;
TALER_EXCHANGE_keys_decref (e->keys);
- e->keys = keys;
- end_inquiry (e);
+ e->keys = NULL;
+ if (0 != strcmp (e->currency,
+ keys->currency))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "/keys response from `%s' is for currency `%s', but we
expected `%s'. Ignoring response.\n",
+ e->exchange_url,
+ keys->currency,
+ e->currency);
+ break;
+ }
+ if (0 != GNUNET_memcmp (&keys->master_pub,
+ &e->master_pub))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Master public key in %skeys response does not match.
Ignoring response.\n",
+ e->exchange_url);
+ break;
+ }
+ if (! store_keys (keys))
+ break;
+ e->keys = TALER_EXCHANGE_keys_incref (keys);
/* Reset back-off */
- e->retry_delay = GNUNET_TIME_UNIT_ZERO;
- /* Success: rate limit at once per minute */
- e->first_retry = GNUNET_TIME_relative_to_absolute (
- GNUNET_TIME_UNIT_MINUTES);
+ e->retry_delay = EXCHANGE_MAXFREQ;
+ /* Limit by expiration */
n = GNUNET_TIME_absolute_max (e->first_retry,
keys->key_data_expiration.abs_time);
if (NULL != e->retry_task)
@@ -260,19 +534,30 @@ cert_cb (
e->retry_task = GNUNET_SCHEDULER_add_at (n,
&download_keys,
e);
- break;
+ end_inquiry ();
+ return;
default:
- e->retry_delay
- = GNUNET_TIME_STD_BACKOFF (e->retry_delay);
- e->first_retry
- = GNUNET_TIME_relative_to_absolute (e->retry_delay);
- if (NULL != e->retry_task)
- GNUNET_SCHEDULER_cancel (e->retry_task);
- e->retry_task = GNUNET_SCHEDULER_add_delayed (e->retry_delay,
- &download_keys,
- e);
break;
}
+ /* Try again (soon-ish) */
+ e->retry_delay
+ = GNUNET_TIME_STD_BACKOFF (e->retry_delay);
+ n = GNUNET_TIME_absolute_max (
+ e->first_retry,
+ GNUNET_TIME_relative_to_absolute (e->retry_delay));
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Will download %skeys in %s\n",
+ e->exchange_url,
+ GNUNET_TIME_relative2s (
+ GNUNET_TIME_absolute_get_remaining (n),
+ true));
+ if (NULL != e->retry_task)
+ GNUNET_SCHEDULER_cancel (e->retry_task);
+ e->retry_task
+ = GNUNET_SCHEDULER_add_at (n,
+ &download_keys,
+ e);
+ end_inquiry ();
}
@@ -280,55 +565,291 @@ static void
download_keys (void *cls)
{
struct Exchange *e = cls;
- struct GNUNET_TIME_Relative n;
-
- /* If we do not hear back again soon, try again automatically */
- n = GNUNET_TIME_STD_BACKOFF (e->retry_delay);
- n = GNUNET_TIME_relative_max (n,
- GNUNET_TIME_UNIT_MINUTES);
- e->retry_task = GNUNET_SCHEDULER_add_delayed (n,
- &download_keys,
- e);
-
- if ( (NULL == e->keys) ||
- (GNUNET_TIME_absolute_is_past (e->keys->key_data_expiration.abs_time)) )
- {
- e->conn = TALER_EXCHANGE_get_keys (ctx,
- e->exchange_url,
- e->keys,
- &cert_cb,
- e);
- if (NULL != e->conn)
- active_inquiries++;
+
+ e->retry_task = NULL;
+ GNUNET_break (OPEN_INQUIRY_LIMIT >= active_inquiries);
+ if (OPEN_INQUIRY_LIMIT <= active_inquiries)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Cannot run job: at limit\n");
+ e->limited = true;
+ at_limit = true;
+ return;
+ }
+ e->retry_delay
+ = GNUNET_TIME_STD_BACKOFF (e->retry_delay);
+ e->conn = TALER_EXCHANGE_get_keys (ctx,
+ e->exchange_url,
+ e->keys,
+ &cert_cb,
+ e);
+ if (NULL != e->conn)
+ {
+ active_inquiries++;
+ }
+ else
+ {
+ struct GNUNET_TIME_Relative n;
+
+ n = GNUNET_TIME_relative_max (e->retry_delay,
+ EXCHANGE_MAXFREQ);
+ e->retry_task
+ = GNUNET_SCHEDULER_add_delayed (n,
+ &download_keys,
+ e);
}
}
/**
- * Lookup our internal data structure for the given
- * @a exchange_url or create one if we do not yet have
- * one.
+ * Lookup exchange by @a exchange_url. Create one
+ * if it does not exist.
*
- * @param exchange_url base URL of the exchange
- * @return our state for this exchange
+ * @param exchange_url base URL to match against
+ * @return NULL if not found
*/
static struct Exchange *
-find_exchange (const char *exchange_url)
+lookup_exchange (const char *exchange_url)
+{
+ for (struct Exchange *e = e_head;
+ NULL != e;
+ e = e->next)
+ if (0 == strcmp (e->exchange_url,
+ exchange_url))
+ return e;
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Got notification about unknown exchange `%s'\n",
+ exchange_url);
+ return NULL;
+}
+
+
+/**
+ * Force immediate (re)loading of /keys for an exchange.
+ *
+ * @param cls NULL
+ * @param extra base URL of the exchange that changed
+ * @param extra_len number of bytes in @a extra
+ */
+static void
+force_exchange_keys (void *cls,
+ const void *extra,
+ size_t extra_len)
{
+ const char *url = extra;
struct Exchange *e;
- for (e = e_head; NULL != e; e = e->next)
- if (0 == strcmp (exchange_url,
- e->exchange_url))
- return e;
- e = GNUNET_new (struct Exchange);
- e->exchange_url = GNUNET_strdup (exchange_url);
- GNUNET_CONTAINER_DLL_insert (e_head,
- e_tail,
+ 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);
+ e = lookup_exchange (url);
+ if (NULL == e)
+ {
+ GNUNET_break (0);
+ return;
+ }
+ if (NULL != e->conn)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Already downloading %skeys\n",
+ url);
+ return;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Will download %skeys in %s\n",
+ url,
+ GNUNET_TIME_relative2s (
+ GNUNET_TIME_absolute_get_remaining (
+ e->first_retry),
+ true));
+ if (NULL != e->retry_task)
+ GNUNET_SCHEDULER_cancel (e->retry_task);
+ e->retry_task
+ = GNUNET_SCHEDULER_add_at (e->first_retry,
+ &download_keys,
e);
- e->retry_task = GNUNET_SCHEDULER_add_now (&download_keys,
- e);
- return e;
+}
+
+
+/**
+ * Function called on each configuration section. Finds sections
+ * about exchanges, parses the entries.
+ *
+ * @param cls NULL
+ * @param section name of the section
+ */
+static void
+accept_exchanges (void *cls,
+ const char *section)
+{
+ char *url;
+ char *mks;
+ char *currency;
+
+ (void) cls;
+ if (0 !=
+ strncasecmp (section,
+ "merchant-exchange-",
+ strlen ("merchant-exchange-")))
+ return;
+ if (GNUNET_YES ==
+ GNUNET_CONFIGURATION_get_value_yesno (cfg,
+ section,
+ "DISABLED"))
+ return;
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (cfg,
+ section,
+ "EXCHANGE_BASE_URL",
+ &url))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ section,
+ "EXCHANGE_BASE_URL");
+ global_ret = EXIT_NOTCONFIGURED;
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ for (struct Exchange *e = e_head;
+ NULL != e;
+ e = e->next)
+ {
+ if (0 == strcmp (url,
+ e->exchange_url))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Exchange `%s' configured in multiple sections, maybe set
DISABLED=YES in section `%s'?\n",
+ url,
+ section);
+ GNUNET_free (url);
+ global_ret = EXIT_NOTCONFIGURED;
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ }
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (cfg,
+ section,
+ "CURRENCY",
+ ¤cy))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ section,
+ "CURRENCY");
+ GNUNET_free (url);
+ global_ret = EXIT_NOTCONFIGURED;
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ if (GNUNET_OK !=
+ GNUNET_CONFIGURATION_get_value_string (cfg,
+ section,
+ "MASTER_KEY",
+ &mks))
+ {
+ GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
+ section,
+ "MASTER_KEY");
+ global_ret = EXIT_NOTCONFIGURED;
+ GNUNET_SCHEDULER_shutdown ();
+ GNUNET_free (currency);
+ GNUNET_free (url);
+ return;
+ }
+
+ {
+ struct Exchange *e;
+
+ e = GNUNET_new (struct Exchange);
+ e->exchange_url = url;
+ e->currency = currency;
+ GNUNET_CONTAINER_DLL_insert (e_head,
+ e_tail,
+ e);
+ if (GNUNET_OK !=
+ GNUNET_CRYPTO_eddsa_public_key_from_string (
+ mks,
+ strlen (mks),
+ &e->master_pub.eddsa_pub))
+ {
+ GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
+ section,
+ "MASTER_KEY",
+ "malformed EdDSA key");
+ global_ret = EXIT_NOTCONFIGURED;
+ GNUNET_SCHEDULER_shutdown ();
+ GNUNET_free (mks);
+ return;
+ }
+ GNUNET_free (mks);
+
+ {
+ enum GNUNET_DB_QueryStatus qs;
+ struct TALER_EXCHANGE_Keys *keys = NULL;
+
+ qs = db_plugin->select_exchange_keys (db_plugin->cls,
+ url,
+ &keys);
+ if (qs < 0)
+ {
+ GNUNET_break (0);
+ global_ret = EXIT_FAILURE;
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ if ( (NULL != keys) &&
+ (0 != strcmp (keys->currency,
+ e->currency)) )
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "/keys cached in our database were for currency `%s', but
we expected `%s'. Fetching /keys again.\n",
+ keys->currency,
+ e->currency);
+ TALER_EXCHANGE_keys_decref (keys);
+ keys = NULL;
+ }
+ if ( (NULL != keys) &&
+ (0 != GNUNET_memcmp (&e->master_pub,
+ &keys->master_pub)) )
+ {
+ /* master pub differs => fetch keys again */
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Master public key of exchange `%s' differs from our
configuration. Fetching /keys again.\n",
+ e->exchange_url);
+ TALER_EXCHANGE_keys_decref (keys);
+ keys = NULL;
+ }
+ e->keys = keys;
+ if (NULL == keys)
+ {
+ /* done synchronously so that the active_inquiries
+ is updated immediately */
+
+ download_keys (e);
+ }
+ else
+ {
+ e->retry_task
+ = GNUNET_SCHEDULER_add_at (keys->key_data_expiration.abs_time,
+ &download_keys,
+ e);
+ }
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Exchange `%s' setup\n",
+ e->exchange_url);
+ }
}
@@ -348,6 +869,7 @@ shutdown_task (void *cls)
struct Exchange *e = e_head;
GNUNET_free (e->exchange_url);
+ GNUNET_free (e->currency);
if (NULL != e->conn)
{
TALER_EXCHANGE_get_keys_cancel (e->conn);
@@ -368,10 +890,10 @@ shutdown_task (void *cls)
e);
GNUNET_free (e);
}
- if (NULL != task)
+ if (NULL != eh)
{
- GNUNET_SCHEDULER_cancel (task);
- task = NULL;
+ db_plugin->event_listen_cancel (eh);
+ eh = NULL;
}
TALER_MERCHANTDB_plugin_unload (db_plugin);
db_plugin = NULL;
@@ -389,60 +911,6 @@ shutdown_task (void *cls)
}
-static void
-find_work (void *cls)
-{
- // enum GNUNET_DB_QueryStatus qs;
- int limit;
-
- (void) cls;
- task = NULL;
- GNUNET_assert (OPEN_INQUIRY_LIMIT >= active_inquiries);
- limit = OPEN_INQUIRY_LIMIT - active_inquiries;
- if (0 == limit)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Not looking for work: at limit\n");
- at_limit = true;
- return;
- }
- at_limit = false;
-#if FIXME
- qs = db_plugin->select_open_transfers (db_plugin->cls,
- limit,
- &start_inquiry,
- NULL);
- if (qs < 0)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Failed to obtain open transfers from database\n");
- GNUNET_SCHEDULER_shutdown ();
- return;
- }
- if (qs == limit)
- {
- /* DB limited response, re-trigger DB interaction
- the moment we significantly fall below the
- limit */
- at_limit = true;
- }
-#endif
- if (0 == active_inquiries)
- {
- if (test_mode)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "No more open inquiries and in test mode. Existing.\n");
- GNUNET_SCHEDULER_shutdown ();
- return;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "No open inquiries found, waiting for notification to
resume\n")
- ;
- }
-}
-
-
/**
* First task.
*
@@ -491,9 +959,29 @@ run (void *cls,
global_ret = EXIT_FAILURE;
return;
}
- GNUNET_assert (NULL == task);
- task = GNUNET_SCHEDULER_add_now (&find_work,
- NULL);
+ {
+ struct GNUNET_DB_EventHeaderP es = {
+ .size = ntohs (sizeof (es)),
+ .type = ntohs (TALER_DBEVENT_MERCHANT_EXCHANGE_FORCE_KEYS)
+ };
+
+ eh = db_plugin->event_listen (db_plugin->cls,
+ &es,
+ GNUNET_TIME_UNIT_FOREVER_REL,
+ &force_exchange_keys,
+ NULL);
+ }
+ GNUNET_CONFIGURATION_iterate_sections (cfg,
+ &accept_exchanges,
+ NULL);
+ if ( (0 == active_inquiries) &&
+ (test_mode) )
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "No more open inquiries and in test mode. Existing.\n");
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
}
@@ -537,9 +1025,6 @@ main (int argc,
return EXIT_INVALIDARGUMENT;
if (GNUNET_NO == ret)
return EXIT_SUCCESS;
- if ( (found_problem) &&
- (0 == global_ret) )
- global_ret = 7;
return global_ret;
}
--
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.