gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-merchant] branch master updated (39704d64 -> 58a20632)


From: gnunet
Subject: [taler-merchant] branch master updated (39704d64 -> 58a20632)
Date: Fri, 06 Sep 2024 22:09:33 +0200

This is an automated email from the git hooks/post-receive script.

grothoff pushed a change to branch master
in repository merchant.

    from 39704d64 add parsing of full kyc-status response to libtalermerchant
     new cd514603 style fixes
     new 58a20632 first implementation of exchangekeyupdate

The 2 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 src/backend/taler-merchant-exchangekeyupdate.c     | 825 ++++++++++++++++-----
 .../taler-merchant-httpd_post-orders-ID-abort.c    |   1 +
 src/backend/taler-merchant-httpd_spa.c             |   4 +
 3 files changed, 660 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;
 }
 
diff --git a/src/backend/taler-merchant-httpd_post-orders-ID-abort.c 
b/src/backend/taler-merchant-httpd_post-orders-ID-abort.c
index 50a793a3..3dbb6fa8 100644
--- a/src/backend/taler-merchant-httpd_post-orders-ID-abort.c
+++ b/src/backend/taler-merchant-httpd_post-orders-ID-abort.c
@@ -28,6 +28,7 @@
 #include <taler/taler_exchange_service.h>
 #include "taler-merchant-httpd_exchanges.h"
 #include "taler-merchant-httpd_helper.h"
+#include "taler-merchant-httpd_post-orders-ID-abort.h"
 
 
 /**
diff --git a/src/backend/taler-merchant-httpd_spa.c 
b/src/backend/taler-merchant-httpd_spa.c
index 4fc64042..2d97f931 100644
--- a/src/backend/taler-merchant-httpd_spa.c
+++ b/src/backend/taler-merchant-httpd_spa.c
@@ -339,6 +339,10 @@ TMH_spa_init ()
  * Nicely shut down.
  */
 void __attribute__ ((destructor))
+get_spa_fini (void);
+
+/* Declaration to avoid compiler warning */
+void __attribute__ ((destructor))
 get_spa_fini ()
 {
   struct WebuiFile *w;

-- 
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]