gnunet-svn
[Top][All Lists]
Advanced

[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",
+                                             &currency))
+  {
+    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.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]