gnunet-svn
[Top][All Lists]
Advanced

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

[taler-exchange] 02/02: activating implementation of #6175


From: gnunet
Subject: [taler-exchange] 02/02: activating implementation of #6175
Date: Mon, 14 Dec 2020 15:42:37 +0100

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

grothoff pushed a commit to branch master
in repository exchange.

commit b5d88fc2d1832fd27bdd7df0860c07ae3c61312c
Author: Christian Grothoff <christian@grothoff.org>
AuthorDate: Mon Dec 14 15:42:32 2020 +0100

    activating implementation of #6175
---
 contrib/gana                                       |   2 +-
 contrib/gana-update.sh                             |   6 +-
 src/auditor/generate-auditor-basedb.sh             |   5 +
 src/auditor/generate-revoke-basedb.sh              |   5 +
 src/exchange/taler-exchange-httpd.c                |  26 +-
 src/exchange/taler-exchange-httpd.h                |   5 +
 src/exchange/taler-exchange-httpd_deposit.c        |  73 +++-
 src/exchange/taler-exchange-httpd_deposits_get.c   |  17 +-
 src/exchange/taler-exchange-httpd_keys.c           | 459 ++++++++++++++++-----
 src/exchange/taler-exchange-httpd_keys.h           |  75 +++-
 src/exchange/taler-exchange-httpd_keystate.c       |   2 +-
 .../taler-exchange-httpd_management_auditors.c     |   3 +-
 ...nge-httpd_management_denominations_HDP_revoke.c |   3 +-
 .../taler-exchange-httpd_management_post_keys.c    |  16 +-
 ...r-exchange-httpd_management_signkey_EP_revoke.c |   3 +-
 .../taler-exchange-httpd_management_wire.c         |   3 +-
 src/exchange/taler-exchange-httpd_melt.c           | 167 ++++----
 src/exchange/taler-exchange-httpd_recoup.c         |  56 ++-
 .../taler-exchange-httpd_refreshes_reveal.c        | 102 +++--
 src/exchange/taler-exchange-httpd_refund.c         |  44 +-
 src/exchange/taler-exchange-httpd_responses.c      |  41 +-
 src/exchange/taler-exchange-httpd_transfers_get.c  |  23 +-
 src/exchange/taler-exchange-httpd_withdraw.c       | 100 +++--
 src/exchange/test_taler_exchange_httpd.conf        |   1 +
 src/exchange/test_taler_exchange_httpd.get         |   2 +-
 src/exchange/test_taler_exchange_httpd.sh          |   6 +-
 src/exchange/test_taler_exchange_httpd_restart.sh  |  11 -
 src/exchange/test_taler_exchange_unix.conf         |   1 +
 src/include/taler_mhd_lib.h                        |  15 +
 src/mhd/mhd_responses.c                            |  31 ++
 src/testing/Makefile.am                            |   5 +-
 src/testing/test_auditor_api.c                     |   6 +-
 src/testing/test_exchange_api.c                    |  10 +-
 .../test_exchange_api_keys_cherry_picking.c        |   8 +-
 src/testing/test_exchange_api_revocation.c         |   2 +-
 src/testing/test_exchange_api_twisted.c            |  11 +-
 src/testing/test_exchange_management_api.c         |   2 +-
 src/testing/test_taler_exchange_wirewatch.c        |   2 +-
 src/testing/testing_api_cmd_offline_sign_keys.c    |   3 +-
 src/testing/testing_api_cmd_revoke.c               |  13 +-
 src/testing/testing_api_helpers_exchange.c         |   2 +-
 src/testing/testing_api_loop.c                     |  81 ++--
 src/util/taler-helper-crypto-rsa.c                 |  62 ++-
 43 files changed, 1038 insertions(+), 472 deletions(-)

diff --git a/contrib/gana b/contrib/gana
index 3501eb7b..912dc84d 160000
--- a/contrib/gana
+++ b/contrib/gana
@@ -1 +1 @@
-Subproject commit 3501eb7b857d573258c1ab1c42d7e827c36cec9d
+Subproject commit 912dc84dc52a1291b635e19da32c7c824719f8d4
diff --git a/contrib/gana-update.sh b/contrib/gana-update.sh
index a5da6f1b..196d4b53 100755
--- a/contrib/gana-update.sh
+++ b/contrib/gana-update.sh
@@ -10,9 +10,9 @@ make
 cd ../../..
 if ! diff contrib/gana/gnu-taler-error-codes/taler_error_codes.h 
src/include/taler_error_codes.h > /dev/null
 then
-  echo "Deploying latest new GANA database..."
   cp contrib/gana/gnu-taler-error-codes/taler_error_codes.h 
src/include/taler_error_codes.h
+fi
+if ! diff contrib/gana/gnu-taler-error-codes/taler_error_codes.c 
src/util/taler_error_codes.c > /dev/null
+then
   cp contrib/gana/gnu-taler-error-codes/taler_error_codes.c 
src/util/taler_error_codes.c
-else
-  echo "GANA database already up-to-date"
 fi
diff --git a/src/auditor/generate-auditor-basedb.sh 
b/src/auditor/generate-auditor-basedb.sh
index 86e8fe2e..c41d4a98 100755
--- a/src/auditor/generate-auditor-basedb.sh
+++ b/src/auditor/generate-auditor-basedb.sh
@@ -116,6 +116,11 @@ mv a2e.dat $ABD
 # Launch services
 echo "Launching services"
 taler-bank-manage-testing $CONF postgres:///$TARGET_DB serve &
+TFN=`which taler-exchange-httpd`
+TBINPFX=`dirname $TFN`
+TLIBEXEC=${BINPFX}/../lib/libexec/taler/
+$TLIBEXEC/taler-helper-crypto-eddsa -c $CONF 2> taler-helper-crypto-eddsa.log &
+$TLIBEXEC/taler-helper-crypto-rsa -c $CONF 2> taler-helper-crypto-rsa.log &
 taler-exchange-httpd -c $CONF 2> taler-exchange-httpd.log &
 taler-merchant-httpd -c $CONF -L INFO 2> taler-merchant-httpd.log &
 taler-exchange-wirewatch -c $CONF 2> taler-exchange-wirewatch.log &
diff --git a/src/auditor/generate-revoke-basedb.sh 
b/src/auditor/generate-revoke-basedb.sh
index ef77e4d7..3874e349 100755
--- a/src/auditor/generate-revoke-basedb.sh
+++ b/src/auditor/generate-revoke-basedb.sh
@@ -104,6 +104,11 @@ mv a2e.dat $ABD
 # Launch services
 echo "Launching services"
 taler-bank-manage-testing $CONF postgres:///$TARGET_DB serve &> 
revocation-bank.log &
+TFN=`which taler-exchange-httpd`
+TBINPFX=`dirname $TFN`
+TLIBEXEC=${BINPFX}/../lib/libexec/taler/
+$TLIBEXEC/taler-helper-crypto-eddsa -c $CONF 2> taler-helper-crypto-eddsa.log &
+$TLIBEXEC/taler-helper-crypto-rsa -c $CONF 2> taler-helper-crypto-rsa.log &
 taler-exchange-httpd -c $CONF 2> taler-exchange-httpd.log &
 EXCHANGE_PID=$!
 taler-merchant-httpd -c $CONF -L INFO 2> taler-merchant-httpd.log &
diff --git a/src/exchange/taler-exchange-httpd.c 
b/src/exchange/taler-exchange-httpd.c
index d9c56540..8ee82ce1 100644
--- a/src/exchange/taler-exchange-httpd.c
+++ b/src/exchange/taler-exchange-httpd.c
@@ -562,8 +562,8 @@ handle_post_management (const struct TEH_RequestHandler *rh,
       return r404 (connection,
                    "/management/denominations/$HDP/revoke");
     if (GNUNET_OK !=
-        GNUNET_STRINGS_string_to_data (args[2],
-                                       strlen (args[2]),
+        GNUNET_STRINGS_string_to_data (args[1],
+                                       strlen (args[1]),
                                        &h_denom_pub,
                                        sizeof (h_denom_pub)))
     {
@@ -571,7 +571,7 @@ handle_post_management (const struct TEH_RequestHandler *rh,
       return TALER_MHD_reply_with_error (connection,
                                          MHD_HTTP_BAD_REQUEST,
                                          TALER_EC_GENERIC_PARAMETER_MALFORMED,
-                                         args[2]);
+                                         args[1]);
     }
     return TEH_handler_management_denominations_HDP_revoke (connection,
                                                             &h_denom_pub,
@@ -591,8 +591,8 @@ handle_post_management (const struct TEH_RequestHandler *rh,
       return r404 (connection,
                    "/management/signkeys/$HDP/revoke");
     if (GNUNET_OK !=
-        GNUNET_STRINGS_string_to_data (args[2],
-                                       strlen (args[2]),
+        GNUNET_STRINGS_string_to_data (args[1],
+                                       strlen (args[1]),
                                        &exchange_pub,
                                        sizeof (exchange_pub)))
     {
@@ -600,7 +600,7 @@ handle_post_management (const struct TEH_RequestHandler *rh,
       return TALER_MHD_reply_with_error (connection,
                                          MHD_HTTP_BAD_REQUEST,
                                          TALER_EC_GENERIC_PARAMETER_MALFORMED,
-                                         args[2]);
+                                         args[1]);
     }
     return TEH_handler_management_signkeys_EP_revoke (connection,
                                                       &exchange_pub,
@@ -805,7 +805,7 @@ handle_mhd_request (void *cls,
     {
       .url = "keys",
       .method = MHD_HTTP_METHOD_GET,
-      .handler.get = &TEH_handler_keys, // FIXME => TEH_keys_get_handler
+      .handler.get = &TEH_keys_get_handler,
     },
     /* Requests for wiring information */
     {
@@ -1427,6 +1427,7 @@ run_single_request (void)
     }
     MHD_run (mhd);
   }
+  TEH_resume_keys_requests ();
   MHD_stop_daemon (mhd);
   mhd = NULL;
   if (cld != waitpid (cld,
@@ -1463,6 +1464,7 @@ run_main_loop (int fh,
     = MHD_start_daemon (MHD_USE_SELECT_INTERNALLY | MHD_USE_PIPE_FOR_SHUTDOWN
                         | MHD_USE_DEBUG | MHD_USE_DUAL_STACK
                         | MHD_USE_INTERNAL_POLLING_THREAD
+                        | MHD_ALLOW_SUSPEND_RESUME
                         | MHD_USE_TCP_FASTOPEN,
                         (-1 == fh) ? serve_port : 0,
                         NULL, NULL,
@@ -1484,11 +1486,17 @@ run_main_loop (int fh,
   }
 
   atexit (&write_stats);
-  ret = TEH_KS_loop ();
+  ret = TEH_keys_init ();
+  if (GNUNET_OK == ret)
+  {
+    ret = TEH_KS_loop ();
+    TEH_keys_done ();
+  }
   switch (ret)
   {
   case GNUNET_OK:
   case GNUNET_SYSERR:
+    TEH_resume_keys_requests ();
     MHD_stop_daemon (mhd);
     break;
   case GNUNET_NO:
@@ -1544,11 +1552,13 @@ run_main_loop (int fh,
              num_connections)
         sleep (1);
       /* Now we're really done, practice clean shutdown */
+      TEH_resume_keys_requests ();
       MHD_stop_daemon (mhd);
     }
     break;
   default:
     GNUNET_break (0);
+    TEH_resume_keys_requests ();
     MHD_stop_daemon (mhd);
     break;
   }
diff --git a/src/exchange/taler-exchange-httpd.h 
b/src/exchange/taler-exchange-httpd.h
index 316e565b..497ff16a 100644
--- a/src/exchange/taler-exchange-httpd.h
+++ b/src/exchange/taler-exchange-httpd.h
@@ -78,6 +78,11 @@ extern struct TALER_EXCHANGEDB_Plugin *TEH_plugin;
  */
 extern char *TEH_currency;
 
+/**
+ * Are we shutting down?
+ */
+extern volatile bool MHD_terminating;
+
 /**
  * @brief Struct describing an URL and the handler for it.
  */
diff --git a/src/exchange/taler-exchange-httpd_deposit.c 
b/src/exchange/taler-exchange-httpd_deposit.c
index bd38c625..e8ca04f8 100644
--- a/src/exchange/taler-exchange-httpd_deposit.c
+++ b/src/exchange/taler-exchange-httpd_deposit.c
@@ -33,6 +33,7 @@
 #include "taler-exchange-httpd_deposit.h"
 #include "taler-exchange-httpd_responses.h"
 #include "taler-exchange-httpd_keystate.h"
+#include "taler-exchange-httpd_keys.h"
 
 
 /**
@@ -75,18 +76,18 @@ reply_deposit_success (struct MHD_Connection *connection,
     .coin_pub = *coin_pub,
     .merchant = *merchant
   };
+  enum TALER_ErrorCode ec;
 
   TALER_amount_hton (&dc.amount_without_fee,
                      amount_without_fee);
-  if (GNUNET_OK !=
-      TEH_KS_sign (&dc,
-                   &pub,
-                   &sig))
+  if (TALER_EC_NONE !=
+      (ec = TEH_keys_exchange_sign (&dc,
+                                    &pub,
+                                    &sig)))
   {
-    return TALER_MHD_reply_with_error (connection,
-                                       MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                       
TALER_EC_EXCHANGE_GENERIC_BAD_CONFIGURATION,
-                                       "no keys");
+    return TALER_MHD_reply_with_ec (connection,
+                                    ec,
+                                    NULL);
   }
   return TALER_MHD_reply_json_pack (
     connection,
@@ -430,9 +431,10 @@ TEH_handler_deposit (struct MHD_Connection *connection,
   /* check denomination exists and is valid */
   {
     struct TEH_KS_StateHandle *key_state;
-    struct TALER_EXCHANGEDB_DenominationKey *dki;
+    struct TEH_DenominationKey *dk;
     enum TALER_ErrorCode ec;
     unsigned int hc;
+    struct GNUNET_TIME_Absolute now;
 
     key_state = TEH_KS_acquire (dc.exchange_timestamp);
     if (NULL == key_state)
@@ -444,12 +446,10 @@ TEH_handler_deposit (struct MHD_Connection *connection,
                                          
TALER_EC_EXCHANGE_GENERIC_BAD_CONFIGURATION,
                                          "no keys");
     }
-    dki = TEH_KS_denomination_key_lookup_by_hash (key_state,
-                                                  &deposit.coin.denom_pub_hash,
-                                                  TEH_KS_DKU_DEPOSIT,
-                                                  &ec,
-                                                  &hc);
-    if (NULL == dki)
+    dk = TEH_keys_denomination_by_hash (&deposit.coin.denom_pub_hash,
+                                        &ec,
+                                        &hc);
+    if (NULL == dk)
     {
       TALER_LOG_DEBUG ("Unknown denomination key in /deposit request\n");
       TEH_KS_release (key_state);
@@ -459,8 +459,42 @@ TEH_handler_deposit (struct MHD_Connection *connection,
                                          ec,
                                          NULL);
     }
-    TALER_amount_ntoh (&deposit.deposit_fee,
-                       &dki->issue.properties.fee_deposit);
+    now = GNUNET_TIME_absolute_get ();
+    if (now.abs_value_us >= dk->meta.expire_deposit.abs_value_us)
+    {
+      /* This denomination is past the expiration time for deposits */
+      TEH_KS_release (key_state);
+      GNUNET_JSON_parse_free (spec);
+      return TALER_MHD_reply_with_error (
+        connection,
+        MHD_HTTP_GONE,
+        TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED,
+        NULL);
+    }
+    if (now.abs_value_us < dk->meta.start.abs_value_us)
+    {
+      /* This denomination is not yet valid */
+      TEH_KS_release (key_state);
+      GNUNET_JSON_parse_free (spec);
+      return TALER_MHD_reply_with_error (
+        connection,
+        MHD_HTTP_PRECONDITION_FAILED,
+        TALER_EC_EXCHANGE_GENERIC_DENOMINATION_VALIDITY_IN_FUTURE,
+        NULL);
+    }
+    if (dk->recoup_possible)
+    {
+      /* This denomination has been revoked */
+      TEH_KS_release (key_state);
+      GNUNET_JSON_parse_free (spec);
+      return TALER_MHD_reply_with_error (
+        connection,
+        MHD_HTTP_GONE,
+        TALER_EC_EXCHANGE_GENERIC_DENOMINATION_REVOKED,
+        NULL);
+    }
+
+    deposit.deposit_fee = dk->meta.fee_deposit;
     if (GNUNET_YES !=
         TALER_amount_cmp_currency (&deposit.amount_with_fee,
                                    &deposit.deposit_fee) )
@@ -476,7 +510,7 @@ TEH_handler_deposit (struct MHD_Connection *connection,
     /* check coin signature */
     if (GNUNET_YES !=
         TALER_test_coin_valid (&deposit.coin,
-                               &dki->denom_pub))
+                               &dk->denom_pub))
     {
       TALER_LOG_WARNING ("Invalid coin passed for /deposit\n");
       TEH_KS_release (key_state);
@@ -486,8 +520,7 @@ TEH_handler_deposit (struct MHD_Connection *connection,
                                          
TALER_EC_EXCHANGE_DENOMINATION_SIGNATURE_INVALID,
                                          NULL);
     }
-    TALER_amount_ntoh (&dc.value,
-                       &dki->issue.properties.value);
+    dc.value = dk->meta.value;
     TEH_KS_release (key_state);
   }
   if (0 < TALER_amount_cmp (&deposit.deposit_fee,
diff --git a/src/exchange/taler-exchange-httpd_deposits_get.c 
b/src/exchange/taler-exchange-httpd_deposits_get.c
index 5b75bdcf..a4932a1e 100644
--- a/src/exchange/taler-exchange-httpd_deposits_get.c
+++ b/src/exchange/taler-exchange-httpd_deposits_get.c
@@ -27,6 +27,7 @@
 #include "taler_mhd_lib.h"
 #include "taler_signatures.h"
 #include "taler-exchange-httpd_keystate.h"
+#include "taler-exchange-httpd_keys.h"
 #include "taler-exchange-httpd_deposits_get.h"
 #include "taler-exchange-httpd_responses.h"
 
@@ -65,18 +66,18 @@ reply_deposit_details (struct MHD_Connection *connection,
     .coin_pub = *coin_pub,
     .execution_time = GNUNET_TIME_absolute_hton (exec_time)
   };
+  enum TALER_ErrorCode ec;
 
   TALER_amount_hton (&cw.coin_contribution,
                      coin_contribution);
-  if (GNUNET_OK !=
-      TEH_KS_sign (&cw,
-                   &pub,
-                   &sig))
+  if (TALER_EC_NONE !=
+      (ec = TEH_keys_exchange_sign (&cw,
+                                    &pub,
+                                    &sig)))
   {
-    return TALER_MHD_reply_with_error (connection,
-                                       MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                       
TALER_EC_EXCHANGE_GENERIC_BAD_CONFIGURATION,
-                                       "no keys");
+    return TALER_MHD_reply_with_ec (connection,
+                                    ec,
+                                    NULL);
   }
   return TALER_MHD_reply_json_pack (connection,
                                     MHD_HTTP_OK,
diff --git a/src/exchange/taler-exchange-httpd_keys.c 
b/src/exchange/taler-exchange-httpd_keys.c
index 6e778677..adc95079 100644
--- a/src/exchange/taler-exchange-httpd_keys.c
+++ b/src/exchange/taler-exchange-httpd_keys.c
@@ -232,17 +232,7 @@ struct SigningKey
 };
 
 
-/**
- * Snapshot of the (coin and signing) keys (including private keys) of
- * the exchange.  There can be multiple instances of this struct, as it is
- * reference counted and only destroyed once the last user is done
- * with it.  The current instance is acquired using
- * #TEH_KS_acquire().  Using this function increases the
- * reference count.  The contents of this structure (except for the
- * reference counter) should be considered READ-ONLY until it is
- * ultimately destroyed (as there can be many concurrent users).
- */
-struct KeyStateHandle
+struct TEH_KeyStateHandle
 {
 
   /**
@@ -307,7 +297,30 @@ struct KeyStateHandle
 
 
 /**
- * Thread-local.  Contains a pointer to `struct KeyStateHandle` or NULL.
+ * Entry of /keys requests that are currently suspended because we are
+ * waiting for /keys to become ready.
+ */
+struct SuspendedKeysRequests
+{
+  /**
+   * Kept in a DLL.
+   */
+  struct SuspendedKeysRequests *next;
+
+  /**
+   * Kept in a DLL.
+   */
+  struct SuspendedKeysRequests *prev;
+
+  /**
+   * The suspended connection.
+   */
+  struct MHD_Connection *connection;
+};
+
+
+/**
+ * Thread-local.  Contains a pointer to `struct TEH_KeyStateHandle` or NULL.
  * Stores the per-thread latest generation of our key state.
  */
 static pthread_key_t key_state;
@@ -321,6 +334,16 @@ static pthread_key_t key_state;
  */
 static volatile uint64_t key_generation;
 
+/**
+ * Head of DLL of suspended /keys requests.
+ */
+static struct SuspendedKeysRequests *skr_head;
+
+/**
+ * Tail of DLL of suspended /keys requests.
+ */
+static struct SuspendedKeysRequests *skr_tail;
+
 /**
  * For how long should a signing key be legally retained?
  * Configuration value.
@@ -343,6 +366,73 @@ static struct TALER_SecurityModulePublicKeyP esign_sm_pub;
  */
 static pthread_mutex_t sm_pub_mutex = PTHREAD_MUTEX_INITIALIZER;
 
+/**
+ * Mutex protecting access to #skr_head and #skr_tail.
+ * (Could be split into two locks if ever needed.)
+ */
+static pthread_mutex_t skr_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+/**
+ * Are we shutting down?
+ */
+static bool terminating;
+
+/**
+ * Did we ever initialize #key_state?
+ */
+static bool key_state_available;
+
+
+/**
+ * Suspend /keys request while we (hopefully) are waiting to be
+ * provisioned with key material.
+ *
+ * @param[in] connection to suspend
+ */
+static MHD_RESULT
+suspend_request (struct MHD_Connection *connection)
+{
+  struct SuspendedKeysRequests *skr;
+
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Suspending /keys request until key material changes\n");
+  GNUNET_assert (0 == pthread_mutex_lock (&skr_mutex));
+  if (terminating)
+  {
+    GNUNET_assert (0 == pthread_mutex_unlock (&skr_mutex));
+    return TALER_MHD_reply_with_error (connection,
+                                       MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                       TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING,
+                                       "Exchange terminating");
+  }
+  skr = GNUNET_new (struct SuspendedKeysRequests);
+  skr->connection = connection;
+  MHD_suspend_connection (connection);
+  GNUNET_CONTAINER_DLL_insert (skr_head,
+                               skr_tail,
+                               skr);
+  GNUNET_assert (0 == pthread_mutex_unlock (&skr_mutex));
+  return MHD_YES;
+}
+
+
+void
+TEH_resume_keys_requests (void)
+{
+  struct SuspendedKeysRequests *skr;
+
+  GNUNET_assert (0 == pthread_mutex_lock (&skr_mutex));
+  while (NULL != (skr = skr_head))
+  {
+    GNUNET_CONTAINER_DLL_remove (skr_head,
+                                 skr_tail,
+                                 skr);
+    MHD_resume_connection (skr->connection);
+    GNUNET_free (skr);
+  }
+  GNUNET_assert (0 == pthread_mutex_unlock (&skr_mutex));
+}
+
 
 /**
  * Clear memory for responses to "/keys" in @a ksh.
@@ -350,7 +440,7 @@ static pthread_mutex_t sm_pub_mutex = 
PTHREAD_MUTEX_INITIALIZER;
  * @param[in,out] ksh key state to update
  */
 static void
-clear_response_cache (struct KeyStateHandle *ksh)
+clear_response_cache (struct TEH_KeyStateHandle *ksh)
 {
   for (unsigned int i = 0; i<ksh->krd_array_length; i++)
   {
@@ -530,6 +620,12 @@ helper_denom_cb (
   struct HelperDenomination *hd;
 
   check_denom_sm_pub (sm_pub);
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "RSA helper announces key %s for denomination type %s with 
validity %s\n",
+              GNUNET_h2s (h_denom_pub),
+              section_name,
+              GNUNET_STRINGS_relative_time_to_string (validity_duration,
+                                                      GNUNET_NO));
   hd = GNUNET_CONTAINER_multihashmap_get (hs->denom_keys,
                                           h_denom_pub);
   if (NULL != hd)
@@ -594,6 +690,11 @@ helper_esign_cb (
   struct GNUNET_PeerIdentity pid;
 
   check_esign_sm_pub (sm_pub);
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "EdDSA helper announces signing key %s with validity %s\n",
+              TALER_B2S (exchange_pub),
+              GNUNET_STRINGS_relative_time_to_string (validity_duration,
+                                                      GNUNET_NO));
   pid.public_key = exchange_pub->eddsa_pub;
   hsk = GNUNET_CONTAINER_multipeermap_get (hs->esign_keys,
                                            &pid);
@@ -675,7 +776,7 @@ sync_key_helpers (struct HelperState *hs)
 /**
  * Free denomination key data.
  *
- * @param cls a `struct KeyStateHandle`, unused
+ * @param cls a `struct TEH_KeyStateHandle`, unused
  * @param h_denom_pub hash of the denomination public key, unused
  * @param value a `struct TEH_DenominationKey` to free
  * @return #GNUNET_OK (continue to iterate)
@@ -706,7 +807,7 @@ clear_denomination_cb (void *cls,
 /**
  * Free denomination key data.
  *
- * @param cls a `struct KeyStateHandle`, unused
+ * @param cls a `struct TEH_KeyStateHandle`, unused
  * @param h_denom_pub hash of the denomination public key, unused
  * @param value a `struct SigningKey` to free
  * @return #GNUNET_OK (continue to iterate)
@@ -733,7 +834,7 @@ clear_signkey_cb (void *cls,
  * @param free_helper true to also release the helper state
  */
 static void
-destroy_key_state (struct KeyStateHandle *ksh,
+destroy_key_state (struct TEH_KeyStateHandle *ksh,
                    bool free_helper)
 {
   clear_response_cache (ksh);
@@ -762,12 +863,12 @@ destroy_key_state (struct KeyStateHandle *ksh,
  * Free all resources associated with @a cls.  Called when
  * the respective pthread is destroyed.
  *
- * @param[in] cls a `struct KeyStateHandle`.
+ * @param[in] cls a `struct TEH_KeyStateHandle`.
  */
 static void
 destroy_key_state_cb (void *cls)
 {
-  struct KeyStateHandle *ksh = cls;
+  struct TEH_KeyStateHandle *ksh = cls;
 
   destroy_key_state (ksh,
                      true);
@@ -786,6 +887,7 @@ TEH_keys_init ()
       pthread_key_create (&key_state,
                           &destroy_key_state_cb))
     return GNUNET_SYSERR;
+  key_state_available = true;
   if (GNUNET_OK !=
       GNUNET_CONFIGURATION_get_value_time (TEH_cfg,
                                            "exchange",
@@ -801,21 +903,33 @@ TEH_keys_init ()
 }
 
 
-/**
- * Close down keys submodule.
- */
 void
 TEH_keys_done ()
 {
-  GNUNET_assert (0 ==
-                 pthread_key_delete (key_state));
+  GNUNET_assert (0 == pthread_mutex_lock (&skr_mutex));
+  terminating = true;
+  GNUNET_assert (0 == pthread_mutex_unlock (&skr_mutex));
+}
+
+
+/**
+ * Fully clean up our state.
+ */
+void __attribute__ ((destructor))
+TEH_keys_finished ()
+{
+  if (key_state_available)
+  {
+    GNUNET_assert (0 ==
+                   pthread_key_delete (key_state));
+  }
 }
 
 
 /**
  * Function called with information about the exchange's denomination keys.
  *
- * @param cls closure with a `struct KeyStateHandle *`
+ * @param cls closure with a `struct TEH_KeyStateHandle *`
  * @param denom_pub public key of the denomination
  * @param h_denom_pub hash of @a denom_pub
  * @param meta meta data information about the denomination type (value, 
expirations, fees)
@@ -832,7 +946,7 @@ denomination_info_cb (
   const struct TALER_MasterSignatureP *master_sig,
   bool recoup_possible)
 {
-  struct KeyStateHandle *ksh = cls;
+  struct TEH_KeyStateHandle *ksh = cls;
   struct TEH_DenominationKey *dk;
 
   dk = GNUNET_new (struct TEH_DenominationKey);
@@ -854,7 +968,7 @@ denomination_info_cb (
 /**
  * Function called with information about the exchange's online signing keys.
  *
- * @param cls closure with a `struct KeyStateHandle *`
+ * @param cls closure with a `struct TEH_KeyStateHandle *`
  * @param exchange_pub the public key
  * @param meta meta data information about the denomination type (expirations)
  * @param master_sig master signature affirming the validity of this 
denomination
@@ -866,7 +980,7 @@ signkey_info_cb (
   const struct TALER_EXCHANGEDB_SignkeyMetaData *meta,
   const struct TALER_MasterSignatureP *master_sig)
 {
-  struct KeyStateHandle *ksh = cls;
+  struct TEH_KeyStateHandle *ksh = cls;
   struct SigningKey *sk;
   struct GNUNET_PeerIdentity pid;
 
@@ -884,10 +998,66 @@ signkey_info_cb (
 }
 
 
+/**
+ * Closure for #get_auditor_sigs.
+ */
+struct GetAuditorSigsContext
+{
+  /**
+   * Where to store the matching signatures.
+   */
+  json_t *denom_keys;
+
+  /**
+   * Public key of the auditor to match against.
+   */
+  const struct TALER_AuditorPublicKeyP *auditor_pub;
+};
+
+
+/**
+ * Extract the auditor signatures matching the auditor's public
+ * key from the @a value and generate the respective JSON.
+ *
+ * @param cls a `struct GetAuditorSigsContext`
+ * @param h_denom_pub hash of the denomination public key
+ * @param value a `struct TEH_DenominationKey`
+ * @return #GNUNET_OK (continue to iterate)
+ */
+static int
+get_auditor_sigs (void *cls,
+                  const struct GNUNET_HashCode *h_denom_pub,
+                  void *value)
+{
+  struct GetAuditorSigsContext *ctx = cls;
+  struct TEH_DenominationKey *dk = value;
+
+  for (struct TEH_AuditorSignature *as = dk->as_head;
+       NULL != as;
+       as = as->next)
+  {
+    if (0 !=
+        GNUNET_memcmp (ctx->auditor_pub,
+                       &as->apub))
+      continue;
+    GNUNET_break (0 ==
+                  json_array_append_new (
+                    ctx->denom_keys,
+                    json_pack (
+                      "{s:o, s:o}",
+                      "denom_pub_h",
+                      GNUNET_JSON_from_data_auto (h_denom_pub),
+                      "auditor_sig",
+                      GNUNET_JSON_from_data_auto (&as->asig))));
+  }
+  return GNUNET_OK;
+}
+
+
 /**
  * Function called with information about the exchange's auditors.
  *
- * @param cls closure with a `struct KeyStateHandle *`
+ * @param cls closure with a `struct TEH_KeyStateHandle *`
  * @param auditor_pub the public key of the auditor
  * @param auditor_url URL of the REST API of the auditor
  * @param auditor_name human readable official name of the auditor
@@ -899,18 +1069,26 @@ auditor_info_cb (
   const char *auditor_url,
   const char *auditor_name)
 {
-  struct KeyStateHandle *ksh = cls;
+  struct TEH_KeyStateHandle *ksh = cls;
+  struct GetAuditorSigsContext ctx;
 
+  ctx.denom_keys = json_array ();
+  ctx.auditor_pub = auditor_pub;
+  GNUNET_CONTAINER_multihashmap_iterate (ksh->denomkey_map,
+                                         &get_auditor_sigs,
+                                         &ctx);
   GNUNET_break (0 ==
                 json_array_append_new (
                   ksh->auditors,
-                  json_pack ("{s:s, s:o, s:s}",
-                             "name",
+                  json_pack ("{s:s, s:o, s:s, s:o}",
+                             "auditor_name",
                              auditor_name,
                              "auditor_pub",
                              GNUNET_JSON_from_data_auto (auditor_pub),
-                             "url",
-                             auditor_url)));
+                             "auditor_url",
+                             auditor_url,
+                             "denomination_keys",
+                             ctx.denom_keys)));
 }
 
 
@@ -918,7 +1096,7 @@ auditor_info_cb (
  * Function called with information about the denominations
  * audited by the exchange's auditors.
  *
- * @param cls closure with a `struct KeyStateHandle *`
+ * @param cls closure with a `struct TEH_KeyStateHandle *`
  * @param auditor_pub the public key of an auditor
  * @param h_denom_pub hash of a denomination key audited by this auditor
  * @param auditor_sig signature from the auditor affirming this
@@ -930,7 +1108,7 @@ auditor_denom_cb (
   const struct GNUNET_HashCode *h_denom_pub,
   const struct TALER_AuditorSignatureP *auditor_sig)
 {
-  struct KeyStateHandle *ksh = cls;
+  struct TEH_KeyStateHandle *ksh = cls;
   struct TEH_DenominationKey *dk;
   struct TEH_AuditorSignature *as;
 
@@ -959,13 +1137,13 @@ auditor_denom_cb (
  * @param[in] hs helper state to (re)use, NULL if not available
  * @return NULL on error (i.e. failed to access database)
  */
-static struct KeyStateHandle *
+static struct TEH_KeyStateHandle *
 build_key_state (struct HelperState *hs)
 {
-  struct KeyStateHandle *ksh;
+  struct TEH_KeyStateHandle *ksh;
   enum GNUNET_DB_QueryStatus qs;
 
-  ksh = GNUNET_new (struct KeyStateHandle);
+  ksh = GNUNET_new (struct TEH_KeyStateHandle);
   ksh->reload_time = GNUNET_TIME_absolute_get ();
   GNUNET_TIME_round_abs (&ksh->reload_time);
   /* We must use the key_generation from when we STARTED the process! */
@@ -1215,7 +1393,7 @@ get_date_string (struct GNUNET_TIME_Absolute at,
  * @return #GNUNET_OK on success
  */
 static int
-setup_general_response_headers (const struct KeyStateHandle *ksh,
+setup_general_response_headers (const struct TEH_KeyStateHandle *ksh,
                                 struct MHD_Response *response)
 {
   char dat[128];
@@ -1262,9 +1440,10 @@ setup_general_response_headers (const struct 
KeyStateHandle *ksh,
  * @param signkeys list of sign keys to return
  * @param recoup list of revoked keys to return
  * @param denoms list of denominations to return
+ * @return #GNUNET_OK on success
  */
-static void
-create_krd (struct KeyStateHandle *ksh,
+static int
+create_krd (struct TEH_KeyStateHandle *ksh,
             const struct GNUNET_HashCode *denom_keys_hash,
             struct GNUNET_TIME_Absolute last_cpd,
             json_t *signkeys,
@@ -1284,15 +1463,23 @@ create_krd (struct KeyStateHandle *ksh,
       .list_issue_date = GNUNET_TIME_absolute_hton (last_cpd),
       .hc = *denom_keys_hash
     };
+    enum TALER_ErrorCode ec;
 
-    TEH_keys_exchange_sign (&ks,
-                            &exchange_pub,
-                            &exchange_sig);
+    if (TALER_EC_NONE !=
+        (ec = TEH_keys_exchange_sign (&ks,
+                                      &exchange_pub,
+                                      &exchange_sig)))
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                  "Could not create key response data: cannot sign (%s)\n",
+                  TALER_ErrorCode_get_hint (ec));
+      return GNUNET_SYSERR;
+    }
   }
 
   keys = json_pack (
     "{s:s, s:o, s:o, s:O, s:O,"
-    " s:O, s:o, s:o, s:o, s:o}",
+    " s:O, s:O, s:o, s:o, s:o}",
     /* 1-5 */
     "version", EXCHANGE_PROTOCOL_VERSION,
     "master_public_key", GNUNET_JSON_from_data_auto (&TEH_master_public_key),
@@ -1352,9 +1539,11 @@ create_krd (struct KeyStateHandle *ksh,
                    setup_general_response_headers (ksh,
                                                    krd.response_compressed));
   }
+  krd.cherry_pick_date = last_cpd;
   GNUNET_array_append (ksh->krd_array,
                        ksh->krd_array_length,
                        krd);
+  return GNUNET_OK;
 }
 
 
@@ -1364,11 +1553,11 @@ create_krd (struct KeyStateHandle *ksh,
  * This function is to recompute all (including cherry-picked) responses we
  * might want to return, based on the state already in @a ksh.
  *
- *
  * @param[in,out] ksh state handle to update
+ * @return #GNUNET_OK on success
  */
-static void
-update_keys_response (struct KeyStateHandle *ksh)
+static int
+update_keys_response (struct TEH_KeyStateHandle *ksh)
 {
   json_t *recoup;
   struct SignKeyCtx sctx;
@@ -1417,12 +1606,24 @@ update_keys_response (struct KeyStateHandle *ksh)
         GNUNET_CRYPTO_hash_context_finish (
           GNUNET_CRYPTO_hash_context_copy (hash_context),
           &hc);
-        create_krd (ksh,
-                    &hc,
-                    last_cpd,
-                    sctx.signkeys,
-                    recoup,
-                    denoms);
+        if (GNUNET_OK !=
+            create_krd (ksh,
+                        &hc,
+                        last_cpd,
+                        sctx.signkeys,
+                        recoup,
+                        denoms))
+        {
+          GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                      "Failed to generate key response data for %s\n",
+                      GNUNET_STRINGS_absolute_time_to_string (last_cpd));
+          GNUNET_CRYPTO_hash_context_abort (hash_context);
+          GNUNET_CONTAINER_heap_destroy (heap);
+          json_decref (denoms);
+          json_decref (sctx.signkeys);
+          json_decref (recoup);
+          return GNUNET_SYSERR;
+        }
         last_cpd = dk->meta.start;
       }
       GNUNET_CRYPTO_hash_context_read (hash_context,
@@ -1468,16 +1669,28 @@ update_keys_response (struct KeyStateHandle *ksh)
 
     GNUNET_CRYPTO_hash_context_finish (hash_context,
                                        &hc);
-    create_krd (ksh,
-                &hc,
-                last_cpd,
-                sctx.signkeys,
-                recoup,
-                denoms);
+    if (GNUNET_OK !=
+        create_krd (ksh,
+                    &hc,
+                    last_cpd,
+                    sctx.signkeys,
+                    recoup,
+                    denoms))
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                  "Failed to generate key response data for %s\n",
+                  GNUNET_STRINGS_absolute_time_to_string (last_cpd));
+      json_decref (denoms);
+      json_decref (sctx.signkeys);
+      json_decref (recoup);
+      return GNUNET_SYSERR;
+    }
+
   }
   json_decref (sctx.signkeys);
   json_decref (recoup);
   json_decref (denoms);
+  return GNUNET_OK;
 }
 
 
@@ -1486,21 +1699,17 @@ TEH_keys_update_states ()
 {
   __sync_fetch_and_add (&key_generation,
                         1);
+  TEH_resume_keys_requests ();
 }
 
 
-/**
- * Return the current key state for this thread.  Possibly re-builds the key
- * state if we have reason to believe that something changed.
- *
- * @return NULL on error
- */
-static struct KeyStateHandle *
-get_key_state (void)
+struct TEH_KeyStateHandle *
+TEH_get_key_state (void)
 {
-  struct KeyStateHandle *old_ksh;
-  struct KeyStateHandle *ksh;
+  struct TEH_KeyStateHandle *old_ksh;
+  struct TEH_KeyStateHandle *ksh;
 
+  GNUNET_assert (key_state_available);
   old_ksh = pthread_getspecific (key_state);
   if (NULL == old_ksh)
   {
@@ -1540,21 +1749,34 @@ get_key_state (void)
 
 
 struct TEH_DenominationKey *
-TEH_keys_denomination_by_hash (
-  const struct GNUNET_HashCode *h_denom_pub,
-  enum TALER_ErrorCode *ec,
-  unsigned int *hc)
+TEH_keys_denomination_by_hash (const struct GNUNET_HashCode *h_denom_pub,
+                               enum TALER_ErrorCode *ec,
+                               unsigned int *hc)
 {
-  struct KeyStateHandle *ksh;
-  struct TEH_DenominationKey *dk;
+  struct TEH_KeyStateHandle *ksh;
 
-  ksh = get_key_state ();
+  ksh = TEH_get_key_state ();
   if (NULL == ksh)
   {
     *hc = MHD_HTTP_INTERNAL_SERVER_ERROR;
     *ec = TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING;
     return NULL;
   }
+  return TEH_keys_denomination_by_hash2 (ksh,
+                                         h_denom_pub,
+                                         ec,
+                                         hc);
+}
+
+
+struct TEH_DenominationKey *
+TEH_keys_denomination_by_hash2 (struct TEH_KeyStateHandle *ksh,
+                                const struct GNUNET_HashCode *h_denom_pub,
+                                enum TALER_ErrorCode *ec,
+                                unsigned int *hc)
+{
+  struct TEH_DenominationKey *dk;
+
   dk = GNUNET_CONTAINER_multihashmap_get (ksh->denomkey_map,
                                           h_denom_pub);
   if (NULL == dk)
@@ -1568,16 +1790,15 @@ TEH_keys_denomination_by_hash (
 
 
 struct TALER_DenominationSignature
-TEH_keys_denomination_sign (
-  const struct GNUNET_HashCode *h_denom_pub,
-  const void *msg,
-  size_t msg_size,
-  enum TALER_ErrorCode *ec)
+TEH_keys_denomination_sign (const struct GNUNET_HashCode *h_denom_pub,
+                            const void *msg,
+                            size_t msg_size,
+                            enum TALER_ErrorCode *ec)
 {
-  struct KeyStateHandle *ksh;
+  struct TEH_KeyStateHandle *ksh;
   struct TALER_DenominationSignature none = { NULL };
 
-  ksh = get_key_state ();
+  ksh = TEH_get_key_state ();
   if (NULL == ksh)
   {
     *ec = TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING;
@@ -1592,12 +1813,11 @@ TEH_keys_denomination_sign (
 
 
 void
-TEH_keys_denomination_revoke (
-  const struct GNUNET_HashCode *h_denom_pub)
+TEH_keys_denomination_revoke (const struct GNUNET_HashCode *h_denom_pub)
 {
-  struct KeyStateHandle *ksh;
+  struct TEH_KeyStateHandle *ksh;
 
-  ksh = get_key_state ();
+  ksh = TEH_get_key_state ();
   if (NULL == ksh)
   {
     GNUNET_break (0);
@@ -1615,10 +1835,10 @@ TEH_keys_exchange_sign_ (const struct
                          struct TALER_ExchangePublicKeyP *pub,
                          struct TALER_ExchangeSignatureP *sig)
 {
-  struct KeyStateHandle *ksh;
+  struct TEH_KeyStateHandle *ksh;
   enum TALER_ErrorCode ec;
 
-  ksh = get_key_state ();
+  ksh = TEH_get_key_state ();
   if (NULL == ksh)
   {
     /* This *can* happen if the exchange's crypto helper is not running
@@ -1646,9 +1866,10 @@ TEH_keys_exchange_sign_ (const struct
                                             &pid);
     if (NULL == sk)
     {
-      GNUNET_break (0);
       /* just to be safe, zero out the (valid) signature, as the key
-         should no longer be used */
+         should not or no longer be used */
+      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+                  "Cannot sign, offline key signatures are missing!\n");
       memset (sig,
               0,
               sizeof (*sig));
@@ -1662,9 +1883,9 @@ TEH_keys_exchange_sign_ (const struct
 void
 TEH_keys_exchange_revoke (const struct TALER_ExchangePublicKeyP *exchange_pub)
 {
-  struct KeyStateHandle *ksh;
+  struct TEH_KeyStateHandle *ksh;
 
-  ksh = get_key_state ();
+  ksh = TEH_get_key_state ();
   if (NULL == ksh)
   {
     GNUNET_break (0);
@@ -1742,19 +1963,19 @@ TEH_keys_get_handler (const struct TEH_RequestHandler 
*rh,
   }
 
   {
-    struct KeyStateHandle *ksh;
+    struct TEH_KeyStateHandle *ksh;
     const struct KeysResponseData *krd;
 
-    ksh = get_key_state ();
+    ksh = TEH_get_key_state ();
     if (NULL == ksh)
     {
-      GNUNET_break (0);
-      return TALER_MHD_reply_with_error (connection,
-                                         MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                         
TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING,
-                                         "no key state");
+      return suspend_request (connection);
+    }
+    if (GNUNET_OK !=
+        update_keys_response (ksh))
+    {
+      return suspend_request (connection);
     }
-    update_keys_response (ksh);
     krd = bsearch (&last_issue_date,
                    ksh->krd_array,
                    ksh->krd_array_length,
@@ -1927,11 +2148,11 @@ TEH_keys_load_fees (const struct GNUNET_HashCode 
*h_denom_pub,
                     struct TALER_DenominationPublicKey *denom_pub,
                     struct TALER_EXCHANGEDB_DenominationKeyMetaData *meta)
 {
-  struct KeyStateHandle *ksh;
+  struct TEH_KeyStateHandle *ksh;
   struct HelperDenomination *hd;
   int ok;
 
-  ksh = get_key_state ();
+  ksh = TEH_get_key_state ();
   if (NULL == ksh)
   {
     GNUNET_break (0);
@@ -1959,11 +2180,11 @@ int
 TEH_keys_get_timing (const struct TALER_ExchangePublicKeyP *exchange_pub,
                      struct TALER_EXCHANGEDB_SignkeyMetaData *meta)
 {
-  struct KeyStateHandle *ksh;
+  struct TEH_KeyStateHandle *ksh;
   struct HelperSignkey *hsk;
   struct GNUNET_PeerIdentity pid;
 
-  ksh = get_key_state ();
+  ksh = TEH_get_key_state ();
   if (NULL == ksh)
   {
     GNUNET_break (0);
@@ -1990,7 +2211,7 @@ struct FutureBuilderContext
   /**
    * Our key state.
    */
-  struct KeyStateHandle *ksh;
+  struct TEH_KeyStateHandle *ksh;
 
   /**
    * Array of denomination keys.
@@ -2044,7 +2265,10 @@ add_future_denomkey_cb (void *cls,
     0 ==
     json_array_append_new (
       fbc->denoms,
-      json_pack ("{s:o, s:o, s:o, s:o, s:o, s:o, s:o, s:o, s:o, s:o, s:o}",
+      json_pack ("{s:o, s:o, s:o, s:o, s:o,"
+                 " s:o, s:o, s:o, s:o, s:o,"
+                 " s:o, s:s}",
+                 /* 1-5 */
                  "value",
                  TALER_JSON_from_amount (&meta.value),
                  "stamp_start",
@@ -2055,8 +2279,9 @@ add_future_denomkey_cb (void *cls,
                  GNUNET_JSON_from_time_abs (meta.expire_deposit),
                  "stamp_expire_legal",
                  GNUNET_JSON_from_time_abs (meta.expire_legal),
+                 /* 6-10 */
                  "denom_pub",
-                 GNUNET_JSON_from_rsa_public_key 
(dk->denom_pub.rsa_public_key),
+                 GNUNET_JSON_from_rsa_public_key 
(hd->denom_pub.rsa_public_key),
                  "fee_withdraw",
                  TALER_JSON_from_amount (&meta.fee_withdraw),
                  "fee_deposit",
@@ -2065,8 +2290,11 @@ add_future_denomkey_cb (void *cls,
                  TALER_JSON_from_amount (&meta.fee_refresh),
                  "fee_refund",
                  TALER_JSON_from_amount (&meta.fee_refund),
+                 /* 11- */
                  "denom_secmod_sig",
-                 GNUNET_JSON_from_data_auto (&hd->sm_sig))));
+                 GNUNET_JSON_from_data_auto (&hd->sm_sig),
+                 "section_name",
+                 hd->section_name)));
   return GNUNET_OK;
 }
 
@@ -2123,10 +2351,10 @@ MHD_RESULT
 TEH_keys_management_get_handler (const struct TEH_RequestHandler *rh,
                                  struct MHD_Connection *connection)
 {
-  struct KeyStateHandle *ksh;
+  struct TEH_KeyStateHandle *ksh;
   json_t *reply;
 
-  ksh = get_key_state ();
+  ksh = TEH_get_key_state ();
   if (NULL == ksh)
   {
     GNUNET_break (0);
@@ -2143,6 +2371,8 @@ TEH_keys_management_get_handler (const struct 
TEH_RequestHandler *rh,
       .signkeys = json_array ()
     };
 
+    GNUNET_assert (NULL != fbc.denoms);
+    GNUNET_assert (NULL != fbc.signkeys);
     GNUNET_CONTAINER_multihashmap_iterate (ksh->helpers.denom_keys,
                                            &add_future_denomkey_cb,
                                            &fbc);
@@ -2161,6 +2391,11 @@ TEH_keys_management_get_handler (const struct 
TEH_RequestHandler *rh,
       GNUNET_JSON_from_data_auto (&denom_sm_pub),
       "signkey_secmod_public_key",
       GNUNET_JSON_from_data_auto (&esign_sm_pub));
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "Returning GET /management/keys response:\n");
+    json_dumpf (reply,
+                stderr,
+                JSON_INDENT (2));
     if (NULL == reply)
       return TALER_MHD_reply_with_error (connection,
                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
diff --git a/src/exchange/taler-exchange-httpd_keys.h 
b/src/exchange/taler-exchange-httpd_keys.h
index 5a1314f1..6966290f 100644
--- a/src/exchange/taler-exchange-httpd_keys.h
+++ b/src/exchange/taler-exchange-httpd_keys.h
@@ -83,6 +83,33 @@ struct TEH_DenominationKey
 };
 
 
+/**
+ * Snapshot of the (coin and signing) keys (including private keys) of
+ * the exchange.  There can be multiple instances of this struct, as it is
+ * reference counted and only destroyed once the last user is done
+ * with it.  The current instance is acquired using
+ * #TEH_KS_acquire().  Using this function increases the
+ * reference count.  The contents of this structure (except for the
+ * reference counter) should be considered READ-ONLY until it is
+ * ultimately destroyed (as there can be many concurrent users).
+ */
+struct TEH_KeyStateHandle;
+
+
+/**
+ * Return the current key state for this thread.  Possibly re-builds the key
+ * state if we have reason to believe that something changed.
+ *
+ * The result is ONLY valid until the next call to
+ * #TEH_keys_denomination_by_hash() or #TEH_get_key_state()
+ * or #TEH_keys_exchange_sign().
+ *
+ * @return NULL on error
+ */
+struct TEH_KeyStateHandle *
+TEH_get_key_state (void);
+
+
 /**
  * Something changed in the database. Rebuild all key states.  This function
  * should be called if the exchange learns about a new signature from an
@@ -109,12 +136,30 @@ TEH_keys_update_states (void);
  *         or NULL if @a h_denom_pub could not be found
  */
 struct TEH_DenominationKey *
-TEH_keys_denomination_by_hash (
-  const struct GNUNET_HashCode *h_denom_pub,
-  enum TALER_ErrorCode *ec,
-  unsigned int *hc);
+TEH_keys_denomination_by_hash (const struct GNUNET_HashCode *h_denom_pub,
+                               enum TALER_ErrorCode *ec,
+                               unsigned int *hc);
 
 
+/**
+ * Look up the issue for a denom public key using a given @a ksh.  This allows
+ * requesting multiple denominations with the same @a ksh which thus will
+ * remain valid until the next call to #TEH_keys_denomination_by_hash() or
+ * #TEH_get_key_state() or #TEH_keys_exchange_sign().
+ *
+ * @param key_state state to look in
+ * @param h_denom_pub hash of denomination public key
+ * @param[out] ec set to the error code, in case the operation failed
+ * @param[out] hc set to the HTTP status code to use
+ * @return the denomination key issue,
+ *         or NULL if @a h_denom_pub could not be found
+ */
+struct TEH_DenominationKey *
+TEH_keys_denomination_by_hash2 (struct TEH_KeyStateHandle *ksh,
+                                const struct GNUNET_HashCode *h_denom_pub,
+                                enum TALER_ErrorCode *ec,
+                                unsigned int *hc);
+
 /**
  * Request to sign @a msg using the public key corresponding to
  * @a h_denom_pub.
@@ -127,11 +172,10 @@ TEH_keys_denomination_by_hash (
  *         see @a ec for details about the failure
  */
 struct TALER_DenominationSignature
-TEH_keys_denomination_sign (
-  const struct GNUNET_HashCode *h_denom_pub,
-  const void *msg,
-  size_t msg_size,
-  enum TALER_ErrorCode *ec);
+TEH_keys_denomination_sign (const struct GNUNET_HashCode *h_denom_pub,
+                            const void *msg,
+                            size_t msg_size,
+                            enum TALER_ErrorCode *ec);
 
 
 /**
@@ -146,8 +190,17 @@ TEH_keys_denomination_sign (
  * @param h_denom_pub hash of the public key to revoke
  */
 void
-TEH_keys_denomination_revoke (
-  const struct GNUNET_HashCode *h_denom_pub);
+TEH_keys_denomination_revoke (const struct GNUNET_HashCode *h_denom_pub);
+
+
+/**
+ * Resumse all suspended /keys requests, we may now have key material
+ * (or are shuting down).
+ *
+ * @param[in] connection to suspend
+ */
+void
+TEH_resume_keys_requests (void);
 
 
 /**
diff --git a/src/exchange/taler-exchange-httpd_keystate.c 
b/src/exchange/taler-exchange-httpd_keystate.c
index 9491234e..8d5a1851 100644
--- a/src/exchange/taler-exchange-httpd_keystate.c
+++ b/src/exchange/taler-exchange-httpd_keystate.c
@@ -2054,7 +2054,7 @@ TEH_KS_denomination_key_lookup_by_hash (
       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
                   "Not returning DKI for %s, as time to create coins has 
passed\n",
                   GNUNET_h2s (denom_pub_hash));
-      *ec = TALER_EC_EXCHANGE_WITHDRAW_VALIDITY_IN_PAST;
+      *ec = TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED;
       *hc = MHD_HTTP_GONE;
       return NULL;
     }
diff --git a/src/exchange/taler-exchange-httpd_management_auditors.c 
b/src/exchange/taler-exchange-httpd_management_auditors.c
index 33f1c6df..1a2494da 100644
--- a/src/exchange/taler-exchange-httpd_management_auditors.c
+++ b/src/exchange/taler-exchange-httpd_management_auditors.c
@@ -103,7 +103,8 @@ add_auditor (void *cls,
                                            "lookup auditor");
     return qs;
   }
-  if (last_date.abs_value_us > aac->validity_start.abs_value_us)
+  if ( (0 < qs) &&
+       (last_date.abs_value_us > aac->validity_start.abs_value_us) )
   {
     *mhd_ret = TALER_MHD_reply_with_error (
       connection,
diff --git 
a/src/exchange/taler-exchange-httpd_management_denominations_HDP_revoke.c 
b/src/exchange/taler-exchange-httpd_management_denominations_HDP_revoke.c
index 75ce3d76..8fb5b083 100644
--- a/src/exchange/taler-exchange-httpd_management_denominations_HDP_revoke.c
+++ b/src/exchange/taler-exchange-httpd_management_denominations_HDP_revoke.c
@@ -29,6 +29,7 @@
 #include "taler-exchange-httpd_management.h"
 #include "taler-exchange-httpd_responses.h"
 #include "taler-exchange-httpd_keystate.h"
+#include "taler-exchange-httpd_keys.h"
 
 
 MHD_RESULT
@@ -81,7 +82,7 @@ TEH_handler_management_denominations_HDP_revoke (
                                        TALER_EC_GENERIC_DB_STORE_FAILED,
                                        "denomination revocation");
   }
-  // FIXME: also update our '/keys' replies! (signal all threads!?!?)
+  TEH_keys_update_states ();
   return TALER_MHD_reply_static (
     connection,
     MHD_HTTP_NO_CONTENT,
diff --git a/src/exchange/taler-exchange-httpd_management_post_keys.c 
b/src/exchange/taler-exchange-httpd_management_post_keys.c
index 06750716..84ec1f53 100644
--- a/src/exchange/taler-exchange-httpd_management_post_keys.c
+++ b/src/exchange/taler-exchange-httpd_management_post_keys.c
@@ -212,6 +212,9 @@ add_keys (void *cls,
                                              "activate denomination key");
       return qs;
     }
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "Added offline signature for denomination `%s'\n",
+                GNUNET_h2s (&akc->d_sigs[i].h_denom_pub));
     GNUNET_assert (0 != qs);
   }
 
@@ -296,9 +299,11 @@ add_keys (void *cls,
                                              "activate signing key");
       return qs;
     }
+    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+                "Added offline signature for signing key `%s'\n",
+                TALER_B2S (&akc->s_sigs[i].exchange_pub));
     GNUNET_assert (0 != qs);
   }
-
   return GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; /* only 'success', so >=0, 
matters here */
 }
 
@@ -382,9 +387,9 @@ TEH_handler_management_post_keys (
     return ret;
   }
   akc.ns_sigs = json_array_size (signkey_sigs);
-  akc.s_sigs = GNUNET_new_array (akc.nd_sigs,
+  akc.s_sigs = GNUNET_new_array (akc.ns_sigs,
                                  struct SigningSig);
-  for (unsigned int i = 0; i<akc.nd_sigs; i++)
+  for (unsigned int i = 0; i<akc.ns_sigs; i++)
   {
     struct SigningSig *s = &akc.s_sigs[i];
     struct GNUNET_JSON_Specification ispec[] = {
@@ -419,6 +424,10 @@ TEH_handler_management_post_keys (
     GNUNET_free (akc.s_sigs);
     return ret;
   }
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Received %u denomination and %u signing key signatures\n",
+              akc.nd_sigs,
+              akc.ns_sigs);
   qs = TEH_DB_run_transaction (connection,
                                "add keys",
                                &ret,
@@ -426,6 +435,7 @@ TEH_handler_management_post_keys (
                                &akc);
   if (qs < 0)
     return ret;
+  TEH_keys_update_states ();
   return TALER_MHD_reply_static (
     connection,
     MHD_HTTP_NO_CONTENT,
diff --git a/src/exchange/taler-exchange-httpd_management_signkey_EP_revoke.c 
b/src/exchange/taler-exchange-httpd_management_signkey_EP_revoke.c
index 8a462f96..3a84296c 100644
--- a/src/exchange/taler-exchange-httpd_management_signkey_EP_revoke.c
+++ b/src/exchange/taler-exchange-httpd_management_signkey_EP_revoke.c
@@ -29,6 +29,7 @@
 #include "taler-exchange-httpd_management.h"
 #include "taler-exchange-httpd_responses.h"
 #include "taler-exchange-httpd_keystate.h"
+#include "taler-exchange-httpd_keys.h"
 
 
 MHD_RESULT
@@ -80,7 +81,7 @@ TEH_handler_management_signkeys_EP_revoke (
                                        TALER_EC_GENERIC_DB_STORE_FAILED,
                                        "signkey revocation");
   }
-  // FIXME: also update our '/keys' replies! (signal all threads!?!?)
+  TEH_keys_update_states ();
   return TALER_MHD_reply_static (
     connection,
     MHD_HTTP_NO_CONTENT,
diff --git a/src/exchange/taler-exchange-httpd_management_wire.c 
b/src/exchange/taler-exchange-httpd_management_wire.c
index 15e5b361..c462bfc3 100644
--- a/src/exchange/taler-exchange-httpd_management_wire.c
+++ b/src/exchange/taler-exchange-httpd_management_wire.c
@@ -101,7 +101,8 @@ add_wire (void *cls,
                                            "lookup wire");
     return qs;
   }
-  if (last_date.abs_value_us > awc->validity_start.abs_value_us)
+  if ( (0 < qs) &&
+       (last_date.abs_value_us > awc->validity_start.abs_value_us) )
   {
     *mhd_ret = TALER_MHD_reply_with_error (
       connection,
diff --git a/src/exchange/taler-exchange-httpd_melt.c 
b/src/exchange/taler-exchange-httpd_melt.c
index 8b5914e2..76cf52eb 100644
--- a/src/exchange/taler-exchange-httpd_melt.c
+++ b/src/exchange/taler-exchange-httpd_melt.c
@@ -30,6 +30,7 @@
 #include "taler-exchange-httpd_melt.h"
 #include "taler-exchange-httpd_responses.h"
 #include "taler-exchange-httpd_keystate.h"
+#include "taler-exchange-httpd_keys.h"
 
 
 /**
@@ -107,16 +108,16 @@ reply_melt_success (struct MHD_Connection *connection,
     .rc = *rc,
     .noreveal_index = htonl (noreveal_index)
   };
+  enum TALER_ErrorCode ec;
 
-  if (GNUNET_OK !=
-      TEH_KS_sign (&body,
-                   &pub,
-                   &sig))
+  if (TALER_EC_NONE !=
+      (ec = TEH_keys_exchange_sign (&body,
+                                    &pub,
+                                    &sig)))
   {
-    return TALER_MHD_reply_with_error (connection,
-                                       MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                       
TALER_EC_EXCHANGE_GENERIC_BAD_CONFIGURATION,
-                                       "no keys");
+    return TALER_MHD_reply_with_ec (connection,
+                                    ec,
+                                    NULL);
   }
   return TALER_MHD_reply_json_pack (
     connection,
@@ -477,94 +478,94 @@ check_for_denomination_key (struct MHD_Connection 
*connection,
   {
     /* Baseline: check if deposits/refreshs are generally
        simply still allowed for this denomination */
-    struct TALER_EXCHANGEDB_DenominationKey *dki;
+    struct TEH_DenominationKey *dk;
     unsigned int hc;
     enum TALER_ErrorCode ec;
+    struct GNUNET_TIME_Absolute now;
 
-    dki = TEH_KS_denomination_key_lookup_by_hash (
-      key_state,
+    dk = TEH_keys_denomination_by_hash (
       &rmc->refresh_session.coin.denom_pub_hash,
-      TEH_KS_DKU_DEPOSIT,
       &ec,
       &hc);
-    /* Consider case that denomination was revoked but
-       this coin was already seen and thus refresh is OK. */
-    if (NULL == dki)
+    if (NULL == dk)
     {
-      dki = TEH_KS_denomination_key_lookup_by_hash (
-        key_state,
-        &rmc->refresh_session.coin.denom_pub_hash,
-        TEH_KS_DKU_RECOUP,
-        &ec,
-        &hc);
-      if (NULL != dki)
-      {
-        struct GNUNET_HashCode denom_hash;
-        enum GNUNET_DB_QueryStatus qs;
-
-        /* Check that the coin is dirty (we have seen it before), as we will
-           not just allow melting of a *fresh* coin where the denomination was
-           revoked (those must be recouped) */
-        qs = TEH_plugin->get_coin_denomination (
-          TEH_plugin->cls,
-          NULL,
-          &rmc->refresh_session.coin.coin_pub,
-          &denom_hash);
-        if (0 > qs)
-        {
-          TEH_KS_release (key_state);
-          /* There is no good reason for a serialization failure here: */
-          GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
-          return TALER_MHD_reply_with_error (connection,
-                                             MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                             TALER_EC_GENERIC_DB_FETCH_FAILED,
-                                             "coin denomination");
-        }
-        /* sanity check */
-        GNUNET_break (0 ==
-                      GNUNET_memcmp (&denom_hash,
-                                     
&rmc->refresh_session.coin.denom_pub_hash));
-        if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
-        {
-          /* We never saw this coin before, so _this_ justification is not OK 
*/
-          dki = NULL;
-        }
-        else
-        {
-          /* Minor optimization: no need to run the
-             "ensure_coin_known" part of the transaction */
-          rmc->coin_is_dirty = true;
-        }
-      }
+      TEH_KS_release (key_state);
+      return TALER_MHD_reply_with_error (
+        connection,
+        MHD_HTTP_NOT_FOUND,
+        TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN,
+        NULL);
     }
-
-    /* Consider the case that the denomination expired for deposits, but
-       recoup of a refreshed coin refilled the balance of the 'zombie' coin
-       and we should thus allow the refresh during the legal period. */
-    if (NULL == dki)
+    now = GNUNET_TIME_absolute_get ();
+    if (now.abs_value_us >= dk->meta.expire_legal.abs_value_us)
     {
-      dki = TEH_KS_denomination_key_lookup_by_hash (key_state,
-                                                    &rmc->refresh_session.coin.
-                                                    denom_pub_hash,
-                                                    TEH_KS_DKU_ZOMBIE,
-                                                    &ec,
-                                                    &hc);
-      if (NULL != dki)
-        rmc->zombie_required = true; /* check later that zombie is satisfied */
+      /* Way too late now, even zombies have expired */
+      TEH_KS_release (key_state);
+      return TALER_MHD_reply_with_error (
+        connection,
+        MHD_HTTP_GONE,
+        TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED,
+        NULL);
     }
-    if (NULL == dki)
+    if (now.abs_value_us < dk->meta.start.abs_value_us)
     {
+      /* This denomination is not yet valid */
       TEH_KS_release (key_state);
-      return TALER_MHD_reply_with_error (connection,
-                                         hc,
-                                         ec,
-                                         NULL);
+      return TALER_MHD_reply_with_error (
+        connection,
+        MHD_HTTP_PRECONDITION_FAILED,
+        TALER_EC_EXCHANGE_GENERIC_DENOMINATION_VALIDITY_IN_FUTURE,
+        NULL);
+    }
+    if (now.abs_value_us >= dk->meta.expire_deposit.abs_value_us)
+    {
+      /* We are past deposit expiration time, but maybe this is a zombie? */
+      struct GNUNET_HashCode denom_hash;
+      enum GNUNET_DB_QueryStatus qs;
+
+      /* Check that the coin is dirty (we have seen it before), as we will
+         not just allow melting of a *fresh* coin where the denomination was
+         revoked (those must be recouped) */
+      qs = TEH_plugin->get_coin_denomination (
+        TEH_plugin->cls,
+        NULL,
+        &rmc->refresh_session.coin.coin_pub,
+        &denom_hash);
+      if (0 > qs)
+      {
+        TEH_KS_release (key_state);
+        /* There is no good reason for a serialization failure here: */
+        GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
+        return TALER_MHD_reply_with_error (connection,
+                                           MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                           TALER_EC_GENERIC_DB_FETCH_FAILED,
+                                           "coin denomination");
+      }
+      /* sanity check */
+      GNUNET_break (0 ==
+                    GNUNET_memcmp (&denom_hash,
+                                   &rmc->refresh_session.coin.denom_pub_hash));
+      if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
+      {
+        /* We never saw this coin before, so _this_ justification is not OK */
+        TEH_KS_release (key_state);
+        return TALER_MHD_reply_with_error (
+          connection,
+          MHD_HTTP_GONE,
+          TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED,
+          NULL);
+      }
+      else
+      {
+        /* Minor optimization: no need to run the
+           "ensure_coin_known" part of the transaction */
+        rmc->coin_is_dirty = true;
+      }
+      rmc->zombie_required = true; /* check later that zombie is satisfied */
     }
 
-    TALER_amount_ntoh (&rmc->coin_refresh_fee,
-                       &dki->issue.properties.fee_refresh);
-    TALER_amount_ntoh (&rmc->coin_value,
-                       &dki->issue.properties.value);
+    rmc->coin_refresh_fee = dk->meta.fee_refresh;
+    rmc->coin_value = dk->meta.value;
     /* check client used sane currency */
     if (GNUNET_YES !=
         TALER_amount_cmp_currency (&rmc->refresh_session.amount_with_fee,
@@ -581,7 +582,7 @@ check_for_denomination_key (struct MHD_Connection 
*connection,
     /* check coin is actually properly signed */
     if (GNUNET_OK !=
         TALER_test_coin_valid (&rmc->refresh_session.coin,
-                               &dki->denom_pub))
+                               &dk->denom_pub))
     {
       GNUNET_break_op (0);
       TEH_KS_release (key_state);
diff --git a/src/exchange/taler-exchange-httpd_recoup.c 
b/src/exchange/taler-exchange-httpd_recoup.c
index fe8b8d60..aa521d66 100644
--- a/src/exchange/taler-exchange-httpd_recoup.c
+++ b/src/exchange/taler-exchange-httpd_recoup.c
@@ -31,6 +31,7 @@
 #include "taler-exchange-httpd_recoup.h"
 #include "taler-exchange-httpd_responses.h"
 #include "taler-exchange-httpd_keystate.h"
+#include "taler-exchange-httpd_keys.h"
 
 
 /**
@@ -359,7 +360,7 @@ verify_and_execute_recoup (struct MHD_Connection 
*connection,
                            int refreshed)
 {
   struct RecoupContext pc;
-  const struct TALER_EXCHANGEDB_DenominationKey *dki;
+  const struct TEH_DenominationKey *dk;
   struct GNUNET_HashCode c_hash;
   void *coin_ev;
   size_t coin_ev_size;
@@ -369,6 +370,7 @@ verify_and_execute_recoup (struct MHD_Connection 
*connection,
   /* check denomination exists and is in recoup mode */
   {
     struct TEH_KS_StateHandle *key_state;
+    struct GNUNET_TIME_Absolute now;
 
     key_state = TEH_KS_acquire (GNUNET_TIME_absolute_get ());
     if (NULL == key_state)
@@ -379,12 +381,10 @@ verify_and_execute_recoup (struct MHD_Connection 
*connection,
                                          
TALER_EC_EXCHANGE_GENERIC_BAD_CONFIGURATION,
                                          "no keys");
     }
-    dki = TEH_KS_denomination_key_lookup_by_hash (key_state,
-                                                  &coin->denom_pub_hash,
-                                                  TEH_KS_DKU_RECOUP,
-                                                  &ec,
-                                                  &hc);
-    if (NULL == dki)
+    dk = TEH_keys_denomination_by_hash (&coin->denom_pub_hash,
+                                        &ec,
+                                        &hc);
+    if (NULL == dk)
     {
       TEH_KS_release (key_state);
       TALER_LOG_WARNING (
@@ -394,13 +394,45 @@ verify_and_execute_recoup (struct MHD_Connection 
*connection,
                                          ec,
                                          NULL);
     }
-    TALER_amount_ntoh (&pc.value,
-                       &dki->issue.properties.value);
+
+    now = GNUNET_TIME_absolute_get ();
+    if (now.abs_value_us >= dk->meta.expire_deposit.abs_value_us)
+    {
+      /* This denomination is past the expiration time for recoup */
+      TEH_KS_release (key_state);
+      return TALER_MHD_reply_with_error (
+        connection,
+        MHD_HTTP_GONE,
+        TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED,
+        NULL);
+    }
+    if (now.abs_value_us < dk->meta.start.abs_value_us)
+    {
+      /* This denomination is not yet valid */
+      TEH_KS_release (key_state);
+      return TALER_MHD_reply_with_error (
+        connection,
+        MHD_HTTP_PRECONDITION_FAILED,
+        TALER_EC_EXCHANGE_GENERIC_DENOMINATION_VALIDITY_IN_FUTURE,
+        NULL);
+    }
+    if (! dk->recoup_possible)
+    {
+      /* This denomination is not eligible for recoup */
+      TEH_KS_release (key_state);
+      return TALER_MHD_reply_with_error (
+        connection,
+        MHD_HTTP_NOT_FOUND,
+        TALER_EC_EXCHANGE_RECOUP_NOT_ELIGIBLE,
+        NULL);
+    }
+
+    pc.value = dk->meta.value;
 
     /* check denomination signature */
     if (GNUNET_YES !=
         TALER_test_coin_valid (coin,
-                               &dki->denom_pub))
+                               &dk->denom_pub))
     {
       TALER_LOG_WARNING ("Invalid coin passed for recoup\n");
       TEH_KS_release (key_state);
@@ -416,7 +448,7 @@ verify_and_execute_recoup (struct MHD_Connection 
*connection,
         .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_RECOUP),
         .purpose.size = htonl (sizeof (struct TALER_RecoupRequestPS)),
         .coin_pub = coin->coin_pub,
-        .h_denom_pub = dki->issue.properties.denom_hash,
+        .h_denom_pub = coin->denom_pub_hash,
         .coin_blind = *coin_bks
       };
 
@@ -440,7 +472,7 @@ verify_and_execute_recoup (struct MHD_Connection 
*connection,
     if (GNUNET_YES !=
         TALER_rsa_blind (&c_hash,
                          &coin_bks->bks,
-                         dki->denom_pub.rsa_public_key,
+                         dk->denom_pub.rsa_public_key,
                          &coin_ev,
                          &coin_ev_size))
     {
diff --git a/src/exchange/taler-exchange-httpd_refreshes_reveal.c 
b/src/exchange/taler-exchange-httpd_refreshes_reveal.c
index 6440f6dd..9b3a42f9 100644
--- a/src/exchange/taler-exchange-httpd_refreshes_reveal.c
+++ b/src/exchange/taler-exchange-httpd_refreshes_reveal.c
@@ -29,6 +29,7 @@
 #include "taler-exchange-httpd_refreshes_reveal.h"
 #include "taler-exchange-httpd_responses.h"
 #include "taler-exchange-httpd_keystate.h"
+#include "taler-exchange-httpd_keys.h"
 
 
 /**
@@ -132,7 +133,7 @@ struct RevealContext
   /**
    * Denominations being requested.
    */
-  const struct TALER_EXCHANGEDB_DenominationKey **dkis;
+  const struct TEH_DenominationKey **dks;
 
   /**
    * Envelopes to be signed.
@@ -151,7 +152,7 @@ struct RevealContext
   struct TALER_DenominationSignature *ev_sigs;
 
   /**
-   * Size of the @e dkis, @e rcds and @e ev_sigs arrays (if non-NULL).
+   * Size of the @e dks, @e rcds and @e ev_sigs arrays (if non-NULL).
    */
   unsigned int num_fresh_coins;
 
@@ -367,7 +368,7 @@ refreshes_reveal_transaction (void *cls,
           struct TALER_PlanchetDetail pd;
           struct GNUNET_HashCode c_hash;
 
-          rcd->dk = &rctx->dkis[j]->denom_pub;
+          rcd->dk = &rctx->dks[j]->denom_pub;
           TALER_planchet_setup_refresh (&ts,
                                         j,
                                         &ps);
@@ -432,18 +433,12 @@ refreshes_reveal_transaction (void *cls,
     refresh_cost = melt.melt_fee;
     for (unsigned int i = 0; i<rctx->num_fresh_coins; i++)
     {
-      struct TALER_Amount fee_withdraw;
-      struct TALER_Amount value;
       struct TALER_Amount total;
 
-      TALER_amount_ntoh (&fee_withdraw,
-                         &rctx->dkis[i]->issue.properties.fee_withdraw);
-      TALER_amount_ntoh (&value,
-                         &rctx->dkis[i]->issue.properties.value);
       if ( (0 >
             TALER_amount_add (&total,
-                              &fee_withdraw,
-                              &value)) ||
+                              &rctx->dks[i]->meta.fee_withdraw,
+                              &rctx->dks[i]->meta.value)) ||
            (0 >
             TALER_amount_add (&refresh_cost,
                               &refresh_cost,
@@ -499,7 +494,7 @@ refreshes_reveal_persist (void *cls,
     {
       struct TALER_EXCHANGEDB_RefreshRevealedCoin *rrc = &rrcs[i];
 
-      rrc->denom_pub = rctx->dkis[i]->denom_pub;
+      rrc->denom_pub = rctx->dks[i]->denom_pub;
       rrc->orig_coin_link_sig = rctx->link_sigs[i];
       rrc->coin_ev = rctx->rcds[i].coin_ev;
       rrc->coin_ev_size = rctx->rcds[i].coin_ev_size;
@@ -546,20 +541,31 @@ resolve_refreshes_reveal_denominations (struct 
TEH_KS_StateHandle *key_state,
 {
   unsigned int num_fresh_coins = json_array_size (new_denoms_h_json);
   /* We know num_fresh_coins is bounded by #MAX_FRESH_COINS, so this is safe */
-  const struct TALER_EXCHANGEDB_DenominationKey *dkis[num_fresh_coins];
-  struct GNUNET_HashCode dki_h[num_fresh_coins];
+  const struct TEH_DenominationKey *dks[num_fresh_coins];
+  struct GNUNET_HashCode dk_h[num_fresh_coins];
   struct TALER_RefreshCoinData rcds[num_fresh_coins];
   struct TALER_CoinSpendSignatureP link_sigs[num_fresh_coins];
   struct TALER_EXCHANGEDB_Melt melt;
   enum GNUNET_GenericReturnValue res;
   MHD_RESULT ret;
+  struct TEH_KeyStateHandle *ksh;
+  struct GNUNET_TIME_Absolute now;
 
+  ksh = TEH_get_key_state ();
+  if (NULL == ksh)
+  {
+    return TALER_MHD_reply_with_error (connection,
+                                       MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                       TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING,
+                                       NULL);
+  }
   /* Parse denomination key hashes */
+  now = GNUNET_TIME_absolute_get ();
   for (unsigned int i = 0; i<num_fresh_coins; i++)
   {
     struct GNUNET_JSON_Specification spec[] = {
       GNUNET_JSON_spec_fixed_auto (NULL,
-                                   &dki_h[i]),
+                                   &dk_h[i]),
       GNUNET_JSON_spec_end ()
     };
     unsigned int hc;
@@ -574,21 +580,45 @@ resolve_refreshes_reveal_denominations (struct 
TEH_KS_StateHandle *key_state,
     {
       return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
     }
-    dkis[i] = TEH_KS_denomination_key_lookup_by_hash (key_state,
-                                                      &dki_h[i],
-                                                      TEH_KS_DKU_WITHDRAW,
-                                                      &ec,
-                                                      &hc);
-    if (NULL == dkis[i])
+    dks[i] = TEH_keys_denomination_by_hash2 (ksh,
+                                             &dk_h[i],
+                                             &ec,
+                                             &hc);
+    if (NULL == dks[i])
     {
       return TALER_MHD_reply_with_error (connection,
                                          hc,
                                          ec,
                                          NULL);
     }
-    /* #TEH_KS_DKU_WITHDRAW should warrant that we only get denomination
-       keys where we did not yet forget the private key */
-    GNUNET_assert (NULL != dkis[i]->denom_priv.rsa_private_key);
+
+    if (now.abs_value_us >= dks[i]->meta.expire_withdraw.abs_value_us)
+    {
+      /* This denomination is past the expiration time for withdraws */
+      return TALER_MHD_reply_with_error (
+        connection,
+        MHD_HTTP_GONE,
+        TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED,
+        NULL);
+    }
+    if (now.abs_value_us < dks[i]->meta.start.abs_value_us)
+    {
+      /* This denomination is not yet valid */
+      return TALER_MHD_reply_with_error (
+        connection,
+        MHD_HTTP_PRECONDITION_FAILED,
+        TALER_EC_EXCHANGE_GENERIC_DENOMINATION_VALIDITY_IN_FUTURE,
+        NULL);
+    }
+    if (dks[i]->recoup_possible)
+    {
+      /* This denomination has been revoked */
+      return TALER_MHD_reply_with_error (
+        connection,
+        MHD_HTTP_GONE,
+        TALER_EC_EXCHANGE_GENERIC_DENOMINATION_REVOKED,
+        NULL);
+    }
   }
 
   /* Parse coin envelopes */
@@ -613,7 +643,7 @@ resolve_refreshes_reveal_denominations (struct 
TEH_KS_StateHandle *key_state,
         GNUNET_free (rcds[j].coin_ev);
       return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
     }
-    rcd->dk = &dkis[i]->denom_pub;
+    rcd->dk = &dks[i]->denom_pub;
   }
 
   /* lookup old_coin_pub in database */
@@ -672,7 +702,7 @@ resolve_refreshes_reveal_denominations (struct 
TEH_KS_StateHandle *key_state,
       struct TALER_LinkDataPS ldp = {
         .purpose.size = htonl (sizeof (ldp)),
         .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_LINK),
-        .h_denom_pub = dki_h[i],
+        .h_denom_pub = dk_h[i],
         .old_coin_pub = melt.session.coin.coin_pub,
         .transfer_pub = rctx->gamma_tp
       };
@@ -699,7 +729,7 @@ resolve_refreshes_reveal_denominations (struct 
TEH_KS_StateHandle *key_state,
 
   rctx->num_fresh_coins = num_fresh_coins;
   rctx->rcds = rcds;
-  rctx->dkis = dkis;
+  rctx->dks = dks;
   rctx->link_sigs = link_sigs;
 
   /* sign _early_ (optimistic!) to keep out of transaction scope! */
@@ -707,18 +737,20 @@ resolve_refreshes_reveal_denominations (struct 
TEH_KS_StateHandle *key_state,
                                     struct TALER_DenominationSignature);
   for (unsigned int i = 0; i<rctx->num_fresh_coins; i++)
   {
-    rctx->ev_sigs[i].rsa_signature
-      = GNUNET_CRYPTO_rsa_sign_blinded (
-          rctx->dkis[i]->denom_priv.rsa_private_key,
+    enum TALER_ErrorCode ec;
+
+    rctx->ev_sigs[i]
+      = TEH_keys_denomination_sign (
+          &dk_h[i],
           rctx->rcds[i].coin_ev,
-          rctx->rcds[i].coin_ev_size);
+          rctx->rcds[i].coin_ev_size,
+          &ec);
     if (NULL == rctx->ev_sigs[i].rsa_signature)
     {
       GNUNET_break (0);
-      ret = TALER_MHD_reply_with_error (connection,
-                                        MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                        
TALER_EC_EXCHANGE_REFRESHES_REVEAL_SIGNING_ERROR,
-                                        NULL);
+      ret = TALER_MHD_reply_with_ec (connection,
+                                     ec,
+                                     NULL);
       goto cleanup;
     }
   }
diff --git a/src/exchange/taler-exchange-httpd_refund.c 
b/src/exchange/taler-exchange-httpd_refund.c
index 1dfd8931..6bb94348 100644
--- a/src/exchange/taler-exchange-httpd_refund.c
+++ b/src/exchange/taler-exchange-httpd_refund.c
@@ -33,6 +33,7 @@
 #include "taler-exchange-httpd_refund.h"
 #include "taler-exchange-httpd_responses.h"
 #include "taler-exchange-httpd_keystate.h"
+#include "taler-exchange-httpd_keys.h"
 
 
 /**
@@ -58,18 +59,18 @@ reply_refund_success (struct MHD_Connection *connection,
     .merchant = refund->merchant_pub,
     .rtransaction_id = GNUNET_htonll (refund->rtransaction_id)
   };
+  enum TALER_ErrorCode ec;
 
   TALER_amount_hton (&rc.refund_amount,
                      &refund->refund_amount);
-  if (GNUNET_OK !=
-      TEH_KS_sign (&rc,
-                   &pub,
-                   &sig))
+  if (TALER_EC_NONE !=
+      (ec = TEH_keys_exchange_sign (&rc,
+                                    &pub,
+                                    &sig)))
   {
-    return TALER_MHD_reply_with_error (connection,
-                                       MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                       
TALER_EC_EXCHANGE_GENERIC_BAD_CONFIGURATION,
-                                       "no online signing key");
+    return TALER_MHD_reply_with_ec (connection,
+                                    ec,
+                                    NULL);
   }
   return TALER_MHD_reply_json_pack (
     connection,
@@ -460,16 +461,14 @@ verify_and_execute_refund (struct MHD_Connection 
*connection,
     }
     /* Obtain information about the coin's denomination! */
     {
-      struct TALER_EXCHANGEDB_DenominationKey *dki;
+      struct TEH_DenominationKey *dk;
       unsigned int hc;
       enum TALER_ErrorCode ec;
 
-      dki = TEH_KS_denomination_key_lookup_by_hash (key_state,
-                                                    &denom_hash,
-                                                    TEH_KS_DKU_DEPOSIT,
-                                                    &ec,
-                                                    &hc);
-      if (NULL == dki)
+      dk = TEH_keys_denomination_by_hash (&denom_hash,
+                                          &ec,
+                                          &hc);
+      if (NULL == dk)
       {
         /* DKI not found, but we do have a coin with this DK in our database;
            not good... */
@@ -480,8 +479,19 @@ verify_and_execute_refund (struct MHD_Connection 
*connection,
                                            ec,
                                            NULL);
       }
-      TALER_amount_ntoh (&refund->details.refund_fee,
-                         &dki->issue.properties.fee_refund);
+
+      if (GNUNET_TIME_absolute_get ().abs_value_us >=
+          dk->meta.expire_deposit.abs_value_us)
+      {
+        /* This denomination is past the expiration time for deposits, and 
thus refunds */
+        TEH_KS_release (key_state);
+        return TALER_MHD_reply_with_error (
+          connection,
+          MHD_HTTP_GONE,
+          TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED,
+          NULL);
+      }
+      refund->details.refund_fee = dk->meta.fee_refund;
     }
     TEH_KS_release (key_state);
   }
diff --git a/src/exchange/taler-exchange-httpd_responses.c 
b/src/exchange/taler-exchange-httpd_responses.c
index 32b44ffc..c0ec6d95 100644
--- a/src/exchange/taler-exchange-httpd_responses.c
+++ b/src/exchange/taler-exchange-httpd_responses.c
@@ -29,6 +29,7 @@
 #include "taler_json_lib.h"
 #include "taler_mhd_lib.h"
 #include "taler-exchange-httpd_keystate.h"
+#include "taler-exchange-httpd_keys.h"
 
 
 /**
@@ -256,10 +257,10 @@ TEH_RESPONSE_compile_transaction_history (
 
         TALER_amount_hton (&pc.recoup_amount,
                            &pr->value);
-        if (GNUNET_OK !=
-            TEH_KS_sign (&pc,
-                         &epub,
-                         &esig))
+        if (TALER_EC_NONE !=
+            TEH_keys_exchange_sign (&pc,
+                                    &epub,
+                                    &esig))
         {
           GNUNET_break (0);
           json_decref (history);
@@ -309,10 +310,10 @@ TEH_RESPONSE_compile_transaction_history (
 
         TALER_amount_hton (&pc.recoup_amount,
                            &recoup->value);
-        if (GNUNET_OK !=
-            TEH_KS_sign (&pc,
-                         &epub,
-                         &esig))
+        if (TALER_EC_NONE !=
+            TEH_keys_exchange_sign (&pc,
+                                    &epub,
+                                    &esig))
         {
           GNUNET_break (0);
           json_decref (history);
@@ -366,10 +367,10 @@ TEH_RESPONSE_compile_transaction_history (
 
         TALER_amount_hton (&pc.recoup_amount,
                            &pr->value);
-        if (GNUNET_OK !=
-            TEH_KS_sign (&pc,
-                         &epub,
-                         &esig))
+        if (TALER_EC_NONE !=
+            TEH_keys_exchange_sign (&pc,
+                                    &epub,
+                                    &esig))
         {
           GNUNET_break (0);
           json_decref (history);
@@ -610,10 +611,10 @@ TEH_RESPONSE_compile_reserve_history (
 
           TALER_amount_hton (&pc.recoup_amount,
                              &recoup->value);
-          if (GNUNET_OK !=
-              TEH_KS_sign (&pc,
-                           &pub,
-                           &sig))
+          if (TALER_EC_NONE !=
+              TEH_keys_exchange_sign (&pc,
+                                      &pub,
+                                      &sig))
           {
             GNUNET_break (0);
             json_decref (json_history);
@@ -686,10 +687,10 @@ TEH_RESPONSE_compile_reserve_history (
           GNUNET_CRYPTO_hash (closing->receiver_account_details,
                               strlen (closing->receiver_account_details) + 1,
                               &rcc.h_wire);
-          if (GNUNET_OK !=
-              TEH_KS_sign (&rcc,
-                           &pub,
-                           &sig))
+          if (TALER_EC_NONE !=
+              TEH_keys_exchange_sign (&rcc,
+                                      &pub,
+                                      &sig))
           {
             GNUNET_break (0);
             json_decref (json_history);
diff --git a/src/exchange/taler-exchange-httpd_transfers_get.c 
b/src/exchange/taler-exchange-httpd_transfers_get.c
index b5237df2..b7f24f23 100644
--- a/src/exchange/taler-exchange-httpd_transfers_get.c
+++ b/src/exchange/taler-exchange-httpd_transfers_get.c
@@ -24,6 +24,7 @@
 #include <microhttpd.h>
 #include <pthread.h>
 #include "taler_signatures.h"
+#include "taler-exchange-httpd_keys.h"
 #include "taler-exchange-httpd_keystate.h"
 #include "taler-exchange-httpd_transfers_get.h"
 #include "taler-exchange-httpd_responses.h"
@@ -99,6 +100,7 @@ reply_transfer_details (struct MHD_Connection *connection,
   struct TALER_ExchangePublicKeyP pub;
   struct TALER_ExchangeSignatureP sig;
 
+
   GNUNET_TIME_round_abs (&exec_time);
   deposits = json_array ();
   if (NULL == deposits)
@@ -158,16 +160,19 @@ reply_transfer_details (struct MHD_Connection *connection,
   wdp.h_wire = *h_wire;
   GNUNET_CRYPTO_hash_context_finish (hash_context,
                                      &wdp.h_details);
-  if (GNUNET_OK !=
-      TEH_KS_sign (&wdp,
-                   &pub,
-                   &sig))
   {
-    json_decref (deposits);
-    return TALER_MHD_reply_with_error (connection,
-                                       MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                       
TALER_EC_EXCHANGE_GENERIC_BAD_CONFIGURATION,
-                                       "no keys");
+    enum TALER_ErrorCode ec;
+
+    if (TALER_EC_NONE !=
+        (ec = TEH_keys_exchange_sign (&wdp,
+                                      &pub,
+                                      &sig)))
+    {
+      json_decref (deposits);
+      return TALER_MHD_reply_with_ec (connection,
+                                      ec,
+                                      NULL);
+    }
   }
 
   return TALER_MHD_reply_json_pack (connection,
diff --git a/src/exchange/taler-exchange-httpd_withdraw.c 
b/src/exchange/taler-exchange-httpd_withdraw.c
index 0b5eb737..035273bc 100644
--- a/src/exchange/taler-exchange-httpd_withdraw.c
+++ b/src/exchange/taler-exchange-httpd_withdraw.c
@@ -31,6 +31,7 @@
 #include "taler-exchange-httpd_withdraw.h"
 #include "taler-exchange-httpd_responses.h"
 #include "taler-exchange-httpd_keystate.h"
+#include "taler-exchange-httpd_keys.h"
 
 
 /**
@@ -133,11 +134,6 @@ struct WithdrawContext
    */
   size_t blinded_msg_len;
 
-  /**
-   * Details about denomination we are about to withdraw.
-   */
-  struct TALER_EXCHANGEDB_DenominationKey *dki;
-
   /**
    * Set to the resulting signed coin data to be returned to the client.
    */
@@ -291,17 +287,19 @@ withdraw_transaction (void *cls,
 #if ! OPTIMISTIC_SIGN
   if (NULL == wc->collectable.sig.rsa_signature)
   {
-    wc->collectable.sig.rsa_signature
-      = GNUNET_CRYPTO_rsa_sign_blinded (wc->dki->denom_priv.rsa_private_key,
-                                        wc->blinded_msg,
-                                        wc->blinded_msg_len);
+    enum TALER_ErrorCode ec;
+
+    wc->collectable.sig
+      = TEH_keys_denomination_sign (&wc->denom_pub_hash,
+                                    wc->blinded_msg,
+                                    wc->blinded_msg_len,
+                                    &ec);
     if (NULL == wc->collectable.sig.rsa_signature)
     {
       GNUNET_break (0);
-      *mhd_ret = TALER_MHD_reply_with_error (connection,
-                                             MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                             
TALER_EC_EXCHANGE_WITHDRAW_SIGNATURE_FAILED,
-                                             NULL);
+      *mhd_ret = TALER_MHD_reply_with_ec (connection,
+                                          ec,
+                                          NULL);
       return GNUNET_DB_STATUS_HARD_ERROR;
     }
   }
@@ -360,6 +358,8 @@ TEH_handler_withdraw (const struct TEH_RequestHandler *rh,
                                  &wc.denom_pub_hash),
     GNUNET_JSON_spec_end ()
   };
+  enum TALER_ErrorCode ec;
+  struct TEH_DenominationKey *dk;
 
   (void) rh;
   if (GNUNET_OK !=
@@ -397,13 +397,12 @@ TEH_handler_withdraw (const struct TEH_RequestHandler *rh,
   {
     unsigned int hc;
     enum TALER_ErrorCode ec;
+    struct GNUNET_TIME_Absolute now;
 
-    wc.dki = TEH_KS_denomination_key_lookup_by_hash (wc.key_state,
-                                                     &wc.denom_pub_hash,
-                                                     TEH_KS_DKU_WITHDRAW,
-                                                     &ec,
-                                                     &hc);
-    if (NULL == wc.dki)
+    dk = TEH_keys_denomination_by_hash (&wc.denom_pub_hash,
+                                        &ec,
+                                        &hc);
+    if (NULL == dk)
     {
       GNUNET_JSON_parse_free (spec);
       TEH_KS_release (wc.key_state);
@@ -412,20 +411,47 @@ TEH_handler_withdraw (const struct TEH_RequestHandler *rh,
                                          ec,
                                          NULL);
     }
+    now = GNUNET_TIME_absolute_get ();
+    if (now.abs_value_us >= dk->meta.expire_withdraw.abs_value_us)
+    {
+      /* This denomination is past the expiration time for withdraws */
+      TEH_KS_release (wc.key_state);
+      GNUNET_JSON_parse_free (spec);
+      return TALER_MHD_reply_with_error (
+        connection,
+        MHD_HTTP_GONE,
+        TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED,
+        NULL);
+    }
+    if (now.abs_value_us < dk->meta.start.abs_value_us)
+    {
+      /* This denomination is not yet valid */
+      TEH_KS_release (wc.key_state);
+      GNUNET_JSON_parse_free (spec);
+      return TALER_MHD_reply_with_error (
+        connection,
+        MHD_HTTP_PRECONDITION_FAILED,
+        TALER_EC_EXCHANGE_GENERIC_DENOMINATION_VALIDITY_IN_FUTURE,
+        NULL);
+    }
+    if (dk->recoup_possible)
+    {
+      /* This denomination has been revoked */
+      TEH_KS_release (wc.key_state);
+      GNUNET_JSON_parse_free (spec);
+      return TALER_MHD_reply_with_error (
+        connection,
+        MHD_HTTP_GONE,
+        TALER_EC_EXCHANGE_GENERIC_DENOMINATION_REVOKED,
+        NULL);
+    }
   }
-  GNUNET_assert (NULL != wc.dki->denom_priv.rsa_private_key);
-  {
-    struct TALER_Amount amount;
-    struct TALER_Amount fee_withdraw;
 
-    TALER_amount_ntoh (&amount,
-                       &wc.dki->issue.properties.value);
-    TALER_amount_ntoh (&fee_withdraw,
-                       &wc.dki->issue.properties.fee_withdraw);
+  {
     if (0 >
         TALER_amount_add (&wc.amount_required,
-                          &amount,
-                          &fee_withdraw))
+                          &dk->meta.value,
+                          &dk->meta.fee_withdraw))
     {
       GNUNET_JSON_parse_free (spec);
       TEH_KS_release (wc.key_state);
@@ -466,19 +492,19 @@ TEH_handler_withdraw (const struct TEH_RequestHandler *rh,
 
 #if OPTIMISTIC_SIGN
   /* Sign before transaction! */
-  wc.collectable.sig.rsa_signature
-    = GNUNET_CRYPTO_rsa_sign_blinded (wc.dki->denom_priv.rsa_private_key,
-                                      wc.blinded_msg,
-                                      wc.blinded_msg_len);
+  wc.collectable.sig
+    = TEH_keys_denomination_sign (&wc.denom_pub_hash,
+                                  wc.blinded_msg,
+                                  wc.blinded_msg_len,
+                                  &ec);
   if (NULL == wc.collectable.sig.rsa_signature)
   {
     GNUNET_break (0);
     GNUNET_JSON_parse_free (spec);
     TEH_KS_release (wc.key_state);
-    return TALER_MHD_reply_with_error (connection,
-                                       MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                       
TALER_EC_EXCHANGE_WITHDRAW_SIGNATURE_FAILED,
-                                       NULL);
+    return TALER_MHD_reply_with_ec (connection,
+                                    ec,
+                                    NULL);
   }
 #endif
 
diff --git a/src/exchange/test_taler_exchange_httpd.conf 
b/src/exchange/test_taler_exchange_httpd.conf
index 23307cd2..b0e3801f 100644
--- a/src/exchange/test_taler_exchange_httpd.conf
+++ b/src/exchange/test_taler_exchange_httpd.conf
@@ -18,6 +18,7 @@ TERMS_DIR = ../../contrib/tos
 # Etag / filename for the terms of service.
 TERMS_ETAG = 0
 
+SIGNKEY_LEGAL_DURATION = 2 years
 
 # Directory with our privacy policy.
 PRIVACY_DIR = ../../contrib/pp
diff --git a/src/exchange/test_taler_exchange_httpd.get 
b/src/exchange/test_taler_exchange_httpd.get
index 28a9e9bc..c9058c87 100644
--- a/src/exchange/test_taler_exchange_httpd.get
+++ b/src/exchange/test_taler_exchange_httpd.get
@@ -20,7 +20,7 @@
 #
 /
 /agpl
-/keys
+/seed
 /robots.txt
 /terms
 /privacy
diff --git a/src/exchange/test_taler_exchange_httpd.sh 
b/src/exchange/test_taler_exchange_httpd.sh
index dabe79cb..94348891 100755
--- a/src/exchange/test_taler_exchange_httpd.sh
+++ b/src/exchange/test_taler_exchange_httpd.sh
@@ -31,10 +31,6 @@ PREFIX=
 
 # Setup database
 taler-exchange-dbinit -c test_taler_exchange_httpd.conf &> /dev/null
-# Setup keys.
-taler-exchange-keyup -c test_taler_exchange_httpd.conf || exit 1
-# Setup wire accounts.
-taler-exchange-wire -c test_taler_exchange_httpd.conf > /dev/null || exit 1
 # Run Exchange HTTPD (in background)
 $PREFIX taler-exchange-httpd -c test_taler_exchange_httpd.conf 2> 
test-exchange.log &
 
@@ -45,7 +41,7 @@ do
     echo -n "."
     sleep 0.1
     OK=1
-    wget http://localhost:8081/ -o /dev/null -O /dev/null >/dev/null && break
+    wget http://localhost:8081/seed -o /dev/null -O /dev/null >/dev/null && 
break
     OK=0
 done
 if [ 1 != $OK ]
diff --git a/src/exchange/test_taler_exchange_httpd_restart.sh 
b/src/exchange/test_taler_exchange_httpd_restart.sh
index a8976fb0..2897127f 100755
--- a/src/exchange/test_taler_exchange_httpd_restart.sh
+++ b/src/exchange/test_taler_exchange_httpd_restart.sh
@@ -53,10 +53,6 @@ PREFIX=
 
 # Setup database
 taler-exchange-dbinit -c test_taler_exchange_unix.conf &> /dev/null
-# Setup keys.
-taler-exchange-keyup -c test_taler_exchange_unix.conf || exit 1
-# Setup wire accounts.
-taler-exchange-wire -c test_taler_exchange_unix.conf > /dev/null || exit 1
 # Run Exchange HTTPD (in background)
 $PREFIX taler-exchange-httpd -c test_taler_exchange_unix.conf 2> 
test-exchange.log &
 
@@ -79,13 +75,6 @@ then
 fi
 echo " DONE"
 
-# Finally run test...
-echo -n "Reloading keys ..."
-kill -SIGUSR1 $!
-sleep 1
-curl --unix-socket "${UNIXPATH}" "http://ignored/"; >/dev/null 2> /dev/null || 
exit_fail "SIGUSR1 killed HTTP service"
-echo " DONE"
-
 # Finally run test...
 echo -n "Restarting program ..."
 kill -SIGHUP $!
diff --git a/src/exchange/test_taler_exchange_unix.conf 
b/src/exchange/test_taler_exchange_unix.conf
index bc870d4b..1a65d801 100644
--- a/src/exchange/test_taler_exchange_unix.conf
+++ b/src/exchange/test_taler_exchange_unix.conf
@@ -15,6 +15,7 @@ TERMS_DIR = ../../contrib/tos
 # Etag / filename for the terms of service.
 TERMS_ETAG = 0
 
+SIGNKEY_LEGAL_DURATION = 2 years
 
 # Directory with our privacy policy.
 PRIVACY_DIR = ../../contrib/pp
diff --git a/src/include/taler_mhd_lib.h b/src/include/taler_mhd_lib.h
index 09e22f9e..c1b65126 100644
--- a/src/include/taler_mhd_lib.h
+++ b/src/include/taler_mhd_lib.h
@@ -143,6 +143,21 @@ TALER_MHD_reply_with_error (struct MHD_Connection 
*connection,
                             const char *detail);
 
 
+/**
+ * Send a response indicating an error. The HTTP status code is
+ * to be derived from the @a ec.
+ *
+ * @param connection the MHD connection to use
+ * @param ec error code uniquely identifying the error
+ * @param detail additional optional detail about the error
+ * @return a MHD result code
+ */
+MHD_RESULT
+TALER_MHD_reply_with_ec (struct MHD_Connection *connection,
+                         enum TALER_ErrorCode ec,
+                         const char *detail);
+
+
 /**
  * Make JSON response object.
  *
diff --git a/src/mhd/mhd_responses.c b/src/mhd/mhd_responses.c
index 5ed82cd2..9e6986a3 100644
--- a/src/mhd/mhd_responses.c
+++ b/src/mhd/mhd_responses.c
@@ -442,6 +442,37 @@ TALER_MHD_reply_with_error (struct MHD_Connection 
*connection,
 }
 
 
+/**
+ * Send a response indicating an error. The HTTP status code is
+ * to be derived from the @a ec.
+ *
+ * @param connection the MHD connection to use
+ * @param ec error code uniquely identifying the error
+ * @param detail additional optional detail about the error
+ * @return a MHD result code
+ */
+MHD_RESULT
+TALER_MHD_reply_with_ec (struct MHD_Connection *connection,
+                         enum TALER_ErrorCode ec,
+                         const char *detail)
+{
+  unsigned int hc = TALER_ErrorCode_get_http_status (ec);
+
+  if ( (0 == hc) ||
+       (UINT_MAX == hc) )
+  {
+    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                "Invalid Taler error code %d provided for response!\n",
+                (int) ec);
+    hc = MHD_HTTP_INTERNAL_SERVER_ERROR;
+  }
+  return TALER_MHD_reply_with_error (connection,
+                                     hc,
+                                     ec,
+                                     detail);
+}
+
+
 /**
  * Send a response indicating that the request was too big.
  *
diff --git a/src/testing/Makefile.am b/src/testing/Makefile.am
index 0d50d95c..ceba6220 100644
--- a/src/testing/Makefile.am
+++ b/src/testing/Makefile.am
@@ -131,7 +131,6 @@ check_PROGRAMS = \
   test_bank_api_with_pybank \
   test_bank_api_with_nexus \
   test_exchange_api \
-  test_exchange_api_keys_cherry_picking \
   test_exchange_api_revocation \
   test_exchange_api_overlapping_keys_bug \
   test_exchange_management_api \
@@ -144,6 +143,10 @@ if HAVE_TWISTER
     test_bank_api_with_pybank_twisted
 endif
 
+# test_exchange_api_keys_cherry_picking disabled for now:
+# needs to be rewritten as we no longer support /keys timetravel!
+
+
 TESTS = \
   $(check_PROGRAMS)
 
diff --git a/src/testing/test_auditor_api.c b/src/testing/test_auditor_api.c
index f0184143..48f03119 100644
--- a/src/testing/test_auditor_api.c
+++ b/src/testing/test_auditor_api.c
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  Copyright (C) 2014-2018 Taler Systems SA
+  Copyright (C) 2014-2020 Taler Systems SA
 
   TALER is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as
@@ -630,8 +630,8 @@ run (void *cls,
     TALER_TESTING_cmd_exec_offline_sign_keys ("offline-sign-future-keys",
                                               CONFIG_FILE),
     TALER_TESTING_cmd_check_keys_pull_all_keys ("refetch /keys",
-                                                1,
-                                                5 /* FIXME: wrong number... 
*/),
+                                                2,
+                                                270 /* FIXME: wrong number... 
*/),
     CMD_RUN_AUDITOR ("virgin-auditor"),
     TALER_TESTING_cmd_exchanges_with_url ("check-exchange",
                                           MHD_HTTP_OK,
diff --git a/src/testing/test_exchange_api.c b/src/testing/test_exchange_api.c
index d2e18c41..f24a3ff7 100644
--- a/src/testing/test_exchange_api.c
+++ b/src/testing/test_exchange_api.c
@@ -880,11 +880,11 @@ run (void *cls,
                                "{\"items\":[{\"name\":\"more ice 
cream\",\"value\":1}]}",
                                GNUNET_TIME_UNIT_ZERO,
                                "EUR:1",
-                               MHD_HTTP_NOT_FOUND),
+                               MHD_HTTP_GONE),
     /* Test deposit fails after recoup, with proof in recoup */
 
     /* Note that, the exchange will never return the coin's transaction
-     * history with recoup data, as we get a 404 on the DK! */
+     * history with recoup data, as we get a 410 on the DK! */
     TALER_TESTING_cmd_deposit ("recoup-deposit-partial-after-recoup",
                                "recoup-withdraw-coin-2a",
                                0,
@@ -892,7 +892,7 @@ run (void *cls,
                                "{\"items\":[{\"name\":\"extra ice 
cream\",\"value\":1}]}",
                                GNUNET_TIME_UNIT_ZERO,
                                "EUR:0.5",
-                               MHD_HTTP_NOT_FOUND),
+                               MHD_HTTP_GONE),
     /* Test that revoked coins cannot be withdrawn */
     CMD_TRANSFER_TO_EXCHANGE ("recoup-create-reserve-3",
                               "EUR:1.01"),
@@ -906,7 +906,7 @@ run (void *cls,
     TALER_TESTING_cmd_withdraw_amount ("recoup-withdraw-coin-3-revoked",
                                        "recoup-create-reserve-3",
                                        "EUR:1",
-                                       MHD_HTTP_NOT_FOUND),
+                                       MHD_HTTP_GONE),
     /* check that we are empty before the rejection test */
     TALER_TESTING_cmd_check_bank_empty ("check-empty-again"),
 
@@ -958,7 +958,7 @@ run (void *cls,
                                                 CONFIG_FILE),
       TALER_TESTING_cmd_check_keys_pull_all_keys ("refetch /keys",
                                                   1,
-                                                  5 /* FIXME: wrong number... 
*/),
+                                                  270 /* FIXME: wrong 
number... */),
       TALER_TESTING_cmd_batch ("wire",
                                wire),
       TALER_TESTING_cmd_batch ("withdraw",
diff --git a/src/testing/test_exchange_api_keys_cherry_picking.c 
b/src/testing/test_exchange_api_keys_cherry_picking.c
index aab94d68..588ef752 100644
--- a/src/testing/test_exchange_api_keys_cherry_picking.c
+++ b/src/testing/test_exchange_api_keys_cherry_picking.c
@@ -129,8 +129,8 @@ run (void *cls,
      * Make sure we have the same keys situation as
      * it was before the serialization.
      */
-    TALER_TESTING_cmd_check_keys_with_now
-      ("check-keys-after-deserialization",
+    TALER_TESTING_cmd_check_keys_with_now (
+      "check-keys-after-deserialization",
       4,
       NDKS_RIGHT_BEFORE_SERIALIZATION,
       /**
@@ -198,8 +198,8 @@ run (void *cls,
      * ----
      *   40
      *///
-    TALER_TESTING_cmd_check_keys_with_now
-      ("check-keys-3",
+    TALER_TESTING_cmd_check_keys_with_now (
+      "check-keys-3",
       3 /* generation */,
       NDKS_RIGHT_BEFORE_SERIALIZATION,
       TTH_parse_time (JAN2030)),
diff --git a/src/testing/test_exchange_api_revocation.c 
b/src/testing/test_exchange_api_revocation.c
index a952d360..55589149 100644
--- a/src/testing/test_exchange_api_revocation.c
+++ b/src/testing/test_exchange_api_revocation.c
@@ -73,7 +73,7 @@ run (void *cls,
                                               CONFIG_FILE),
     TALER_TESTING_cmd_check_keys_pull_all_keys ("refetch /keys",
                                                 1,
-                                                5 /* FIXME: wrong number... 
*/),
+                                                270 /* FIXME: wrong number... 
*/),
     /**
      * Fill reserve with EUR:10.02, as withdraw fee is 1 ct per
      * config.
diff --git a/src/testing/test_exchange_api_twisted.c 
b/src/testing/test_exchange_api_twisted.c
index 446bdccc..05867f89 100644
--- a/src/testing/test_exchange_api_twisted.c
+++ b/src/testing/test_exchange_api_twisted.c
@@ -222,7 +222,7 @@ run (void *cls,
     TALER_TESTING_cmd_check_keys_pull_all_keys (
       "check-keys-expiration-0",
       2,
-      5),
+      270),
     /**
      * Run some normal commands after this to make sure everything is fine.
      */
@@ -237,6 +237,15 @@ run (void *cls,
   };
 
   struct TALER_TESTING_Command commands[] = {
+    TALER_TESTING_cmd_wire_add ("add-wire-account",
+                                "payto://x-taler-bank/localhost/2",
+                                MHD_HTTP_NO_CONTENT,
+                                false),
+    TALER_TESTING_cmd_exec_offline_sign_keys ("offline-sign-future-keys",
+                                              CONFIG_FILE),
+    TALER_TESTING_cmd_check_keys_pull_all_keys ("refetch /keys",
+                                                1,
+                                                270 /* FIXME: wrong number... 
*/),
     TALER_TESTING_cmd_batch ("refresh-reveal-409-conflict",
                              refresh_409_conflict),
     TALER_TESTING_cmd_batch ("refund",
diff --git a/src/testing/test_exchange_management_api.c 
b/src/testing/test_exchange_management_api.c
index 26c6cae8..18f6dedf 100644
--- a/src/testing/test_exchange_management_api.c
+++ b/src/testing/test_exchange_management_api.c
@@ -144,7 +144,7 @@ run (void *cls,
                                               CONFIG_FILE),
     TALER_TESTING_cmd_check_keys_pull_all_keys ("refetch /keys",
                                                 1,
-                                                5 /* FIXME: wrong number... 
*/),
+                                                270 /* FIXME: wrong number... 
*/),
     TALER_TESTING_cmd_end ()
   };
 
diff --git a/src/testing/test_taler_exchange_wirewatch.c 
b/src/testing/test_taler_exchange_wirewatch.c
index b16a9e76..169c959b 100644
--- a/src/testing/test_taler_exchange_wirewatch.c
+++ b/src/testing/test_taler_exchange_wirewatch.c
@@ -93,7 +93,7 @@ run (void *cls,
                                               config_filename),
     TALER_TESTING_cmd_check_keys_pull_all_keys ("refetch /keys",
                                                 1,
-                                                5 /* FIXME: wrong number... 
*/),
+                                                58 /* FIXME: wrong number... 
*/),
     TALER_TESTING_cmd_check_bank_empty ("expect-empty-transactions-on-start"),
     CMD_EXEC_AGGREGATOR ("run-aggregator-on-empty"),
     TALER_TESTING_cmd_exec_wirewatch ("run-wirewatch-on-empty",
diff --git a/src/testing/testing_api_cmd_offline_sign_keys.c 
b/src/testing/testing_api_cmd_offline_sign_keys.c
index 70654ea1..dd6170d9 100644
--- a/src/testing/testing_api_cmd_offline_sign_keys.c
+++ b/src/testing/testing_api_cmd_offline_sign_keys.c
@@ -70,6 +70,7 @@ offlinesign_run (void *cls,
         "taler-exchange-offline",
         "taler-exchange-offline",
         "-c", ks->config_filename,
+        "-L", "INFO",
         "download",
         "sign",
         "upload",
@@ -80,8 +81,6 @@ offlinesign_run (void *cls,
     TALER_TESTING_interpreter_fail (is);
     return;
   }
-  /* This function does not tell whether the command
-   * succeeded or not!  */
   TALER_TESTING_wait_for_sigchld (is);
 }
 
diff --git a/src/testing/testing_api_cmd_revoke.c 
b/src/testing/testing_api_cmd_revoke.c
index c43f5372..8863110b 100644
--- a/src/testing/testing_api_cmd_revoke.c
+++ b/src/testing/testing_api_cmd_revoke.c
@@ -79,8 +79,9 @@ revoke_cleanup (void *cls,
 
   if (NULL != rs->revoke_proc)
   {
-    GNUNET_break (0 == GNUNET_OS_process_kill
-                    (rs->revoke_proc, SIGKILL));
+    GNUNET_break (0 ==
+                  GNUNET_OS_process_kill (rs->revoke_proc,
+                                          SIGKILL));
     GNUNET_OS_process_wait (rs->revoke_proc);
     GNUNET_OS_process_destroy (rs->revoke_proc);
     rs->revoke_proc = NULL;
@@ -163,13 +164,13 @@ revoke_run (void *cls,
   rs->dhks = GNUNET_STRINGS_data_to_string_alloc (
     &denom_pub->h_key,
     sizeof (struct GNUNET_HashCode));
-
   rs->revoke_proc = GNUNET_OS_start_process (GNUNET_OS_INHERIT_STD_ALL,
                                              NULL, NULL, NULL,
-                                             "taler-exchange-keyup",
-                                             "taler-exchange-keyup",
+                                             "taler-exchange-offline",
+                                             "taler-exchange-offline",
                                              "-c", rs->config_filename,
-                                             "-r", rs->dhks,
+                                             "revoke-denomination", rs->dhks,
+                                             "upload",
                                              NULL);
 
   if (NULL == rs->revoke_proc)
diff --git a/src/testing/testing_api_helpers_exchange.c 
b/src/testing/testing_api_helpers_exchange.c
index 95ba71b7..a12998e2 100644
--- a/src/testing/testing_api_helpers_exchange.c
+++ b/src/testing/testing_api_helpers_exchange.c
@@ -617,7 +617,7 @@ TALER_TESTING_wait_exchange_ready (const char *base_url)
   unsigned int iter;
 
   GNUNET_asprintf (&wget_cmd,
-                   "wget -q -t 1 -T 1 %skeys -o /dev/null -O /dev/null",
+                   "wget -q -t 1 -T 1 %sseed -o /dev/null -O /dev/null",
                    base_url); // make sure ends with '/'
   /* give child time to start and bind against the socket */
   fprintf (stderr,
diff --git a/src/testing/testing_api_loop.c b/src/testing/testing_api_loop.c
index 88af481e..f269274e 100644
--- a/src/testing/testing_api_loop.c
+++ b/src/testing/testing_api_loop.c
@@ -388,24 +388,25 @@ maint_child_death (void *cls)
   struct TALER_TESTING_Interpreter *is = cls;
   struct TALER_TESTING_Command *cmd = &is->commands[is->ip];
   const struct GNUNET_DISK_FileHandle *pr;
-
   struct GNUNET_OS_Process **processp;
   char c[16];
+  enum GNUNET_OS_ProcessStatusType type;
+  unsigned long code;
 
   if (TALER_TESTING_cmd_is_batch (cmd))
   {
     struct TALER_TESTING_Command *batch_cmd;
 
-    GNUNET_assert
-      (GNUNET_OK == TALER_TESTING_get_trait_cmd
-        (cmd, 0, &batch_cmd)); /* bad? */
+    GNUNET_assert (GNUNET_OK ==
+                   TALER_TESTING_get_trait_cmd (cmd,
+                                                0,
+                                                &batch_cmd));
     cmd = batch_cmd;
   }
 
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Got SIGCHLD for `%s'.\n",
               cmd->label);
-
   is->child_death_task = NULL;
   pr = GNUNET_DISK_pipe_handle (sigpipe,
                                 GNUNET_DISK_PIPE_END_READ);
@@ -424,16 +425,45 @@ maint_child_death (void *cls)
   }
 
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Got the dead child process handle"
-              ", waiting for termination ...\n");
-
-  GNUNET_OS_process_wait (*processp);
+              "Got the dead child process handle, waiting for termination 
...\n");
+  GNUNET_OS_process_wait_status (*processp,
+                                 &type,
+                                 &code);
   GNUNET_OS_process_destroy (*processp);
   *processp = NULL;
-
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "... definitively terminated\n");
+  switch (type)
+  {
+  case GNUNET_OS_PROCESS_UNKNOWN:
+    GNUNET_break (0);
+    TALER_TESTING_interpreter_fail (is);
+    return;
+  case GNUNET_OS_PROCESS_RUNNING:
+    GNUNET_break (0);
+    TALER_TESTING_interpreter_fail (is);
+    return;
+  case GNUNET_OS_PROCESS_STOPPED:
+    GNUNET_break (0);
+    TALER_TESTING_interpreter_fail (is);
+    return;
+  case GNUNET_OS_PROCESS_EXITED:
+    if (0 != code)
+    {
+      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+                  "Process exited with unexpected status %u\n",
+                  (unsigned int) code);
+      TALER_TESTING_interpreter_fail (is);
+      return;
+    }
+    break;
+  case GNUNET_OS_PROCESS_SIGNALED:
+    GNUNET_break (0);
+    TALER_TESTING_interpreter_fail (is);
+    return;
+  }
 
+  // FIXME: remove reload_keys, obsolete!
   if (GNUNET_OK == is->reload_keys)
   {
     if (NULL == is->exchanged)
@@ -444,8 +474,9 @@ maint_child_death (void *cls)
     {
       GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
                   "Triggering key state reload at exchange\n");
-      GNUNET_break (0 == GNUNET_OS_process_kill
-                      (is->exchanged, SIGUSR1));
+      GNUNET_break (0 ==
+                    GNUNET_OS_process_kill (is->exchanged,
+                                            SIGUSR1));
       sleep (5); /* make sure signal was received and processed */
     }
   }
@@ -643,19 +674,8 @@ TALER_TESTING_cert_cb (void *cls,
    * the interpreter is already running. */
   if (GNUNET_YES == is->working)
     return;
-
   is->working = GNUNET_YES;
-
-  /* Very first start of tests, call "run()" */
-  if (1 == is->key_generation)
-  {
-    main_ctx->main_cb (main_ctx->main_cb_cls,
-                       is);
-    return;
-  }
-
-  /* Tests already started, just trigger the
-   * next command. */
+  /* Trigger the next command. */
   TALER_LOG_DEBUG ("Cert_cb, scheduling CMD (ip: %d)\n",
                    is->ip);
   GNUNET_SCHEDULER_add_now (&interpreter_run,
@@ -740,6 +760,7 @@ main_wrapper_exchange_connect (void *cls)
   main_ctx->exchange_url = exchange_url;
   is->timeout_task = GNUNET_SCHEDULER_add_shutdown (&do_abort,
                                                     main_ctx);
+  is->working = GNUNET_YES;
   GNUNET_break
     (NULL != (is->exchange =
                 TALER_EXCHANGE_connect (is->ctx,
@@ -747,6 +768,10 @@ main_wrapper_exchange_connect (void *cls)
                                         &TALER_TESTING_cert_cb,
                                         main_ctx,
                                         TALER_EXCHANGE_OPTION_END)));
+  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+              "Starting main test loop\n");
+  main_ctx->main_cb (main_ctx->main_cb_cls,
+                     is);
 }
 
 
@@ -842,10 +867,10 @@ static int
 load_urls (struct TALER_TESTING_Interpreter *is)
 {
   if (GNUNET_OK !=
-      GNUNET_CONFIGURATION_get_value_filename (is->cfg,
-                                               "auditor",
-                                               "BASE_URL",
-                                               &is->auditor_url))
+      GNUNET_CONFIGURATION_get_value_string (is->cfg,
+                                             "auditor",
+                                             "BASE_URL",
+                                             &is->auditor_url))
   {
     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
                                "auditor",
diff --git a/src/util/taler-helper-crypto-rsa.c 
b/src/util/taler-helper-crypto-rsa.c
index ec1158ee..32f0b44d 100644
--- a/src/util/taler-helper-crypto-rsa.c
+++ b/src/util/taler-helper-crypto-rsa.c
@@ -1112,17 +1112,16 @@ read_job (void *cls)
  * Create a new denomination key (we do not have enough).
  *
  * @param denom denomination key to create
+ * @param now current time to use (to get many keys to use the exact same time)
  * @return #GNUNET_OK on success
  */
 static int
-create_key (struct Denomination *denom)
+create_key (struct Denomination *denom,
+            struct GNUNET_TIME_Absolute now)
 {
   struct DenominationKey *dk;
   struct GNUNET_TIME_Absolute anchor;
-  struct GNUNET_TIME_Absolute now;
 
-  now = GNUNET_TIME_absolute_get ();
-  (void) GNUNET_TIME_round_abs (&now);
   if (NULL == denom->keys_tail)
   {
     anchor = now;
@@ -1237,9 +1236,11 @@ purge_key (struct DenominationKey *dk)
  * correct location sorted by next maintenance activity.
  *
  * @param[in,out] denom denomination to update material for
+ * @param now current time to use (to get many keys to use the exact same time)
  */
 static void
-update_keys (struct Denomination *denom)
+update_keys (struct Denomination *denom,
+             struct GNUNET_TIME_Absolute now)
 {
   /* create new denomination keys */
   while ( (NULL == denom->keys_tail) ||
@@ -1252,7 +1253,8 @@ update_keys (struct Denomination *denom)
                  lookahead_sign),
                overlap_duration)).rel_value_us) )
     if (GNUNET_OK !=
-        create_key (denom))
+        create_key (denom,
+                    now))
     {
       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                   "Failed to create keys for `%s'\n",
@@ -1273,18 +1275,19 @@ update_keys (struct Denomination *denom)
     struct GNUNET_TIME_Absolute at;
 
     at = denomination_action_time (denom);
-    before = NULL;
     GNUNET_CONTAINER_DLL_remove (denom_head,
                                  denom_tail,
                                  denom);
+    before = NULL;
     for (struct Denomination *pos = denom_head;
          NULL != pos;
          pos = pos->next)
     {
-      if (denomination_action_time (pos).abs_value_us > at.abs_value_us)
+      if (denomination_action_time (pos).abs_value_us >= at.abs_value_us)
         break;
       before = pos;
     }
+
     GNUNET_CONTAINER_DLL_insert_after (denom_head,
                                        denom_tail,
                                        before,
@@ -1302,12 +1305,16 @@ static void
 update_denominations (void *cls)
 {
   struct Denomination *denom;
+  struct GNUNET_TIME_Absolute now;
 
   (void) cls;
+  now = GNUNET_TIME_absolute_get ();
+  (void) GNUNET_TIME_round_abs (&now);
   keygen_task = NULL;
   do {
     denom = denom_head;
-    update_keys (denom);
+    update_keys (denom,
+                 now);
   } while (denom != denom_head);
   keygen_task = GNUNET_SCHEDULER_add_at (denomination_action_time (denom),
                                          &update_denominations,
@@ -1609,18 +1616,35 @@ parse_denomination_cfg (const char *ct,
 }
 
 
+/**
+ * Closure for #load_denominations.
+ */
+struct LoadContext
+{
+  /**
+   * Current time to use.
+   */
+  struct GNUNET_TIME_Absolute now;
+
+  /**
+   * Status, to be set to #GNUNET_SYSERR on failure
+   */
+  int ret;
+};
+
+
 /**
  * Generate new denomination signing keys for the denomination type of the 
given @a
  * denomination_alias.
  *
- * @param cls a `int *`, to be set to #GNUNET_SYSERR on failure
+ * @param cls a `struct LoadContext`, with 'ret' to be set to #GNUNET_SYSERR 
on failure
  * @param denomination_alias name of the denomination's section in the 
configuration
  */
 static void
 load_denominations (void *cls,
                     const char *denomination_alias)
 {
-  int *ret = cls;
+  struct LoadContext *ctx = cls;
   struct Denomination *denom;
 
   if (0 != strncasecmp (denomination_alias,
@@ -1632,7 +1656,7 @@ load_denominations (void *cls,
       parse_denomination_cfg (denomination_alias,
                               denom))
   {
-    *ret = GNUNET_SYSERR;
+    ctx->ret = GNUNET_SYSERR;
     GNUNET_free (denom);
     return;
   }
@@ -1656,7 +1680,8 @@ load_denominations (void *cls,
   GNUNET_CONTAINER_DLL_insert (denom_head,
                                denom_tail,
                                denom);
-  update_keys (denom);
+  update_keys (denom,
+               ctx->now);
 }
 
 
@@ -1905,13 +1930,16 @@ run (void *cls,
   keys = GNUNET_CONTAINER_multihashmap_create (65536,
                                                GNUNET_YES);
   {
-    int ok;
+    struct LoadContext lc = {
+      .ret = GNUNET_OK,
+      .now = GNUNET_TIME_absolute_get ()
+    };
 
-    ok = GNUNET_OK;
+    (void) GNUNET_TIME_round_abs (&lc.now);
     GNUNET_CONFIGURATION_iterate_sections (kcfg,
                                            &load_denominations,
-                                           &ok);
-    if (GNUNET_OK != ok)
+                                           &lc);
+    if (GNUNET_OK != lc.ret)
     {
       global_ret = 4;
       GNUNET_SCHEDULER_shutdown ();

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