gnunet-svn
[Top][All Lists]
Advanced

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

[taler-exchange] branch master updated (8bed4152 -> b5d88fc2)


From: gnunet
Subject: [taler-exchange] branch master updated (8bed4152 -> b5d88fc2)
Date: Mon, 14 Dec 2020 15:42:35 +0100

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

grothoff pushed a change to branch master
in repository exchange.

    from 8bed4152 allow empty signkeys array
     new 468fc9d1 add missing signkey_legal_duration option to test configs
     new b5d88fc2 activating implementation of #6175

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


Summary of changes:
 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 +-
 .../test-taler-exchange-wirewatch-postgres.conf    |   2 +
 src/testing/test_auditor_api.c                     |   6 +-
 src/testing/test_auditor_api.conf                  |   1 +
 src/testing/test_exchange_api.c                    |  10 +-
 src/testing/test_exchange_api.conf                 |   1 +
 .../test_exchange_api_keys_cherry_picking.c        |   8 +-
 .../test_exchange_api_keys_cherry_picking.conf     |   1 +
 src/testing/test_exchange_api_revocation.c         |   2 +-
 src/testing/test_exchange_api_twisted.c            |  11 +-
 src/testing/test_exchange_api_twisted.conf         |   1 +
 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 ++-
 48 files changed, 1044 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-taler-exchange-wirewatch-postgres.conf 
b/src/testing/test-taler-exchange-wirewatch-postgres.conf
index 420a46f1..f7c4f290 100644
--- a/src/testing/test-taler-exchange-wirewatch-postgres.conf
+++ b/src/testing/test-taler-exchange-wirewatch-postgres.conf
@@ -20,6 +20,8 @@ MASTER_PUBLIC_KEY = 
98NJW3CQHZQGQXTY3K85K531XKPAPAVV4Q5V8PYYRR00NJGZWNVG
 # Expected base URL of the exchange.
 BASE_URL = "http://localhost:8081/";
 
+signkey_legal_duration = 2 years
+
 [exchangedb]
 # After how long do we close idle reserves?  The exchange
 # and the auditor must agree on this value.  We currently
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_auditor_api.conf 
b/src/testing/test_auditor_api.conf
index 890812c5..1d1ad8c0 100644
--- a/src/testing/test_auditor_api.conf
+++ b/src/testing/test_auditor_api.conf
@@ -25,6 +25,7 @@ signkey_duration = 4 weeks
 
 # how long are the signatures with the signkey valid?
 legal_duration = 2 years
+signkey_legal_duration = 2 years
 
 # how long do we provide to clients denomination and signing keys
 # ahead of time?
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.conf 
b/src/testing/test_exchange_api.conf
index e555907b..be2703f1 100644
--- a/src/testing/test_exchange_api.conf
+++ b/src/testing/test_exchange_api.conf
@@ -29,6 +29,7 @@ signkey_duration = 4 weeks
 
 # how long are the signatures with the signkey valid?
 legal_duration = 2 years
+signkey_legal_duration = 2 years
 
 # how long do we provide to clients denomination and signing keys
 # ahead of time?
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_keys_cherry_picking.conf 
b/src/testing/test_exchange_api_keys_cherry_picking.conf
index d81f2c96..437f32ee 100644
--- a/src/testing/test_exchange_api_keys_cherry_picking.conf
+++ b/src/testing/test_exchange_api_keys_cherry_picking.conf
@@ -32,6 +32,7 @@ signkey_duration = 5 seconds
 
 # how long are the signatures with the signkey valid?
 legal_duration = 2 years
+signkey_legal_duration = 2 years
 
 # This value causes keys to be *RETURNED* in a /keys response.
 # It's a relative time that materializes always in now+itsvalue.
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_api_twisted.conf 
b/src/testing/test_exchange_api_twisted.conf
index 3f5b6f88..7865d0b3 100644
--- a/src/testing/test_exchange_api_twisted.conf
+++ b/src/testing/test_exchange_api_twisted.conf
@@ -19,6 +19,7 @@ SIGNKEY_DURATION = 4 weeks
 
 # how long are the signatures with the signkey valid?
 LEGAL_DURATION = 2 years
+signkey_legal_duration = 2 years
 
 # how long do we provide to clients denomination and signing keys
 # ahead of time?
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]