gnunet-svn
[Top][All Lists]
Advanced

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

[taler-exchange] 01/02: (start to) remove logic no longer needed with ne


From: gnunet
Subject: [taler-exchange] 01/02: (start to) remove logic no longer needed with new key management
Date: Mon, 14 Dec 2020 17:17:19 +0100

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

grothoff pushed a commit to branch master
in repository exchange.

commit 04f2e9a4d5b64000b5e29b4603c9bc57c732c4f7
Author: Christian Grothoff <christian@grothoff.org>
AuthorDate: Mon Dec 14 17:12:25 2020 +0100

    (start to) remove logic no longer needed with new key management
---
 src/exchange/Makefile.am                           |    4 +-
 src/exchange/taler-exchange-httpd.c                |   27 +-
 src/exchange/taler-exchange-httpd_db.c             |    2 +-
 src/exchange/taler-exchange-httpd_deposit.c        |   20 +-
 src/exchange/taler-exchange-httpd_deposits_get.c   |    1 -
 src/exchange/taler-exchange-httpd_keystate.c       | 2587 --------------------
 src/exchange/taler-exchange-httpd_keystate.h       |  229 --
 src/exchange/taler-exchange-httpd_link.c           |    1 -
 src/exchange/taler-exchange-httpd_loop.c           |  209 ++
 ...ge-httpd_wire.h => taler-exchange-httpd_loop.h} |   41 +-
 .../taler-exchange-httpd_management_auditors.c     |    2 +-
 ...exchange-httpd_management_auditors_AP_disable.c |    2 +-
 ...nge-httpd_management_denominations_HDP_revoke.c |    1 -
 ...r-exchange-httpd_management_signkey_EP_revoke.c |    1 -
 .../taler-exchange-httpd_management_wire.c         |    2 +
 .../taler-exchange-httpd_management_wire_disable.c |    4 +-
 .../taler-exchange-httpd_management_wire_fees.c    |    2 +
 src/exchange/taler-exchange-httpd_melt.c           |  206 +-
 src/exchange/taler-exchange-httpd_recoup.c         |  194 +-
 .../taler-exchange-httpd_refreshes_reveal.c        |   34 +-
 src/exchange/taler-exchange-httpd_refund.c         |   67 +-
 src/exchange/taler-exchange-httpd_reserves_get.c   |    1 -
 src/exchange/taler-exchange-httpd_responses.c      |    1 -
 src/exchange/taler-exchange-httpd_transfers_get.c  |    1 -
 src/exchange/taler-exchange-httpd_wire.c           |  399 ---
 src/exchange/taler-exchange-httpd_wire.h           |   19 +-
 src/exchange/taler-exchange-httpd_wire2.c          |   59 +-
 src/exchange/taler-exchange-httpd_withdraw.c       |   25 -
 src/exchange/test_taler_exchange_httpd.sh          |    2 +-
 src/include/taler_testing_lib.h                    |  102 +-
 src/testing/Makefile.am                            |    2 +-
 src/testing/test_auditor_api.c                     |    3 +-
 src/testing/test_exchange_api.c                    |    7 +-
 .../test_exchange_api_overlapping_keys_bug.c       |    5 +-
 src/testing/test_exchange_api_revocation.c         |    3 +-
 src/testing/test_exchange_api_twisted.c            |    6 +-
 src/testing/test_exchange_management_api.c         |    3 +-
 src/testing/test_taler_exchange_wirewatch.c        |    3 +-
 src/testing/testing_api_cmd_check_keys.c           |  127 +-
 src/testing/testing_api_cmd_offline_sign_fees.c    |  192 ++
 src/testing/testing_api_cmd_revoke.c               |    2 -
 src/testing/testing_api_loop.c                     |   18 -
 42 files changed, 759 insertions(+), 3857 deletions(-)

diff --git a/src/exchange/Makefile.am b/src/exchange/Makefile.am
index bea212ed..03723752 100644
--- a/src/exchange/Makefile.am
+++ b/src/exchange/Makefile.am
@@ -83,8 +83,8 @@ taler_exchange_httpd_SOURCES = \
   taler-exchange-httpd_deposit.c taler-exchange-httpd_deposit.h \
   taler-exchange-httpd_deposits_get.c taler-exchange-httpd_deposits_get.h \
   taler-exchange-httpd_keys.c taler-exchange-httpd_keys.h \
-  taler-exchange-httpd_keystate.c taler-exchange-httpd_keystate.h \
   taler-exchange-httpd_link.c taler-exchange-httpd_link.h \
+  taler-exchange-httpd_loop.c taler-exchange-httpd_loop.h \
   taler-exchange-httpd_management.h \
   taler-exchange-httpd_management_auditors.c \
   taler-exchange-httpd_management_auditors_AP_disable.c \
@@ -103,7 +103,7 @@ taler_exchange_httpd_SOURCES = \
   taler-exchange-httpd_responses.c taler-exchange-httpd_responses.h \
   taler-exchange-httpd_terms.c taler-exchange-httpd_terms.h \
   taler-exchange-httpd_transfers_get.c taler-exchange-httpd_transfers_get.h \
-  taler-exchange-httpd_wire.c taler-exchange-httpd_wire.h \
+  taler-exchange-httpd_wire2.c taler-exchange-httpd_wire.h \
   taler-exchange-httpd_withdraw.c taler-exchange-httpd_withdraw.h
 
 #  taler-exchange-httpd_management_post_keys.c
diff --git a/src/exchange/taler-exchange-httpd.c 
b/src/exchange/taler-exchange-httpd.c
index 8ee82ce1..19bba0b8 100644
--- a/src/exchange/taler-exchange-httpd.c
+++ b/src/exchange/taler-exchange-httpd.c
@@ -31,9 +31,9 @@
 #include "taler-exchange-httpd_auditors.h"
 #include "taler-exchange-httpd_deposit.h"
 #include "taler-exchange-httpd_deposits_get.h"
-#include "taler-exchange-httpd_keystate.h"
 #include "taler-exchange-httpd_keys.h"
 #include "taler-exchange-httpd_link.h"
+#include "taler-exchange-httpd_loop.h"
 #include "taler-exchange-httpd_management.h"
 #include "taler-exchange-httpd_melt.h"
 #include "taler-exchange-httpd_mhd.h"
@@ -45,6 +45,7 @@
 #include "taler-exchange-httpd_transfers_get.h"
 #include "taler-exchange-httpd_wire.h"
 #include "taler-exchange-httpd_withdraw.h"
+#include "taler_exchangedb_lib.h"
 #include "taler_exchangedb_plugin.h"
 #include <gnunet/gnunet_mhd_compat.h>
 
@@ -1158,21 +1159,11 @@ exchange_serve_process_config (void)
               "Launching exchange with public key `%s'...\n",
               GNUNET_p2s (&TEH_master_public_key.eddsa_pub));
 
-  if (GNUNET_OK !=
-      TEH_WIRE_init (TEH_cfg))
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Failed to setup wire subsystem\n");
-    return GNUNET_SYSERR;
-  }
-
-
   if (NULL ==
       (TEH_plugin = TALER_EXCHANGEDB_plugin_load (TEH_cfg)))
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                 "Failed to initialize DB subsystem\n");
-    TEH_WIRE_done ();
     return GNUNET_SYSERR;
   }
 
@@ -1185,7 +1176,6 @@ exchange_serve_process_config (void)
   {
     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
                 "Failed to setup HTTPd subsystem\n");
-    TEH_WIRE_done ();
     return GNUNET_SYSERR;
   }
   return GNUNET_OK;
@@ -1489,8 +1479,8 @@ run_main_loop (int fh,
   ret = TEH_keys_init ();
   if (GNUNET_OK == ret)
   {
-    ret = TEH_KS_loop ();
-    TEH_keys_done ();
+    ret = TEH_loop_run ();
+    TEH_loop_done ();
   }
   switch (ret)
   {
@@ -1703,7 +1693,10 @@ main (int argc,
   }
 
   /* initialize #internal_key_state with an RC of 1 */
-  ret = TEH_KS_init ();
+  if (GNUNET_OK !=
+      TEH_WIRE_init ())
+    return 42;
+  ret = TEH_loop_init ();
   if (GNUNET_OK == ret)
   {
 #if HAVE_DEVELOPER
@@ -1726,8 +1719,8 @@ main (int argc,
       ret = run_main_loop (fh,
                            argv);
     }
-    /* release #internal_key_state */
-    TEH_KS_free ();
+    /* release signal handlers */
+    TEH_loop_done ();
   }
   TALER_EXCHANGEDB_plugin_unload (TEH_plugin);
   TEH_WIRE_done ();
diff --git a/src/exchange/taler-exchange-httpd_db.c 
b/src/exchange/taler-exchange-httpd_db.c
index 14a5b5d5..8574224a 100644
--- a/src/exchange/taler-exchange-httpd_db.c
+++ b/src/exchange/taler-exchange-httpd_db.c
@@ -25,7 +25,7 @@
 #include "taler_json_lib.h"
 #include "taler_mhd_lib.h"
 #include "taler-exchange-httpd_responses.h"
-#include "taler-exchange-httpd_keystate.h"
+
 
 /**
  * How often should we retry a transaction before giving up
diff --git a/src/exchange/taler-exchange-httpd_deposit.c 
b/src/exchange/taler-exchange-httpd_deposit.c
index e8ca04f8..2f7d49c9 100644
--- a/src/exchange/taler-exchange-httpd_deposit.c
+++ b/src/exchange/taler-exchange-httpd_deposit.c
@@ -32,7 +32,7 @@
 #include "taler_mhd_lib.h"
 #include "taler-exchange-httpd_deposit.h"
 #include "taler-exchange-httpd_responses.h"
-#include "taler-exchange-httpd_keystate.h"
+#include "taler_exchangedb_lib.h"
 #include "taler-exchange-httpd_keys.h"
 
 
@@ -430,29 +430,17 @@ TEH_handler_deposit (struct MHD_Connection *connection,
   (void) GNUNET_TIME_round_abs (&dc.exchange_timestamp);
   /* check denomination exists and is valid */
   {
-    struct TEH_KS_StateHandle *key_state;
     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)
-    {
-      TALER_LOG_ERROR ("Lacking keys to operate\n");
-      GNUNET_JSON_parse_free (spec);
-      return TALER_MHD_reply_with_error (connection,
-                                         MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                         
TALER_EC_EXCHANGE_GENERIC_BAD_CONFIGURATION,
-                                         "no keys");
-    }
     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);
       GNUNET_JSON_parse_free (spec);
       return TALER_MHD_reply_with_error (connection,
                                          hc,
@@ -463,7 +451,6 @@ TEH_handler_deposit (struct MHD_Connection *connection,
     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,
@@ -474,7 +461,6 @@ TEH_handler_deposit (struct MHD_Connection *connection,
     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,
@@ -485,7 +471,6 @@ TEH_handler_deposit (struct MHD_Connection *connection,
     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,
@@ -500,7 +485,6 @@ TEH_handler_deposit (struct MHD_Connection *connection,
                                    &deposit.deposit_fee) )
     {
       GNUNET_break_op (0);
-      TEH_KS_release (key_state);
       GNUNET_JSON_parse_free (spec);
       return TALER_MHD_reply_with_error (connection,
                                          MHD_HTTP_BAD_REQUEST,
@@ -513,7 +497,6 @@ TEH_handler_deposit (struct MHD_Connection *connection,
                                &dk->denom_pub))
     {
       TALER_LOG_WARNING ("Invalid coin passed for /deposit\n");
-      TEH_KS_release (key_state);
       GNUNET_JSON_parse_free (spec);
       return TALER_MHD_reply_with_error (connection,
                                          MHD_HTTP_UNAUTHORIZED,
@@ -521,7 +504,6 @@ TEH_handler_deposit (struct MHD_Connection *connection,
                                          NULL);
     }
     dc.value = dk->meta.value;
-    TEH_KS_release (key_state);
   }
   if (0 < TALER_amount_cmp (&deposit.deposit_fee,
                             &deposit.amount_with_fee))
diff --git a/src/exchange/taler-exchange-httpd_deposits_get.c 
b/src/exchange/taler-exchange-httpd_deposits_get.c
index a4932a1e..90f28b4e 100644
--- a/src/exchange/taler-exchange-httpd_deposits_get.c
+++ b/src/exchange/taler-exchange-httpd_deposits_get.c
@@ -26,7 +26,6 @@
 #include "taler_json_lib.h"
 #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"
diff --git a/src/exchange/taler-exchange-httpd_keystate.c 
b/src/exchange/taler-exchange-httpd_keystate.c
deleted file mode 100644
index 8d5a1851..00000000
--- a/src/exchange/taler-exchange-httpd_keystate.c
+++ /dev/null
@@ -1,2587 +0,0 @@
-/*
-  This file is part of TALER
-  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 Affero General Public License as published by the Free 
Software
-  Foundation; either version 3, or (at your option) any later version.
-
-  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
-  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
-  A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more 
details.
-
-  You should have received a copy of the GNU Affero General Public License 
along with
-  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
-*/
-/**
- * @file taler-exchange-httpd_keystate.c
- * @brief management of our coin signing keys
- * @author Florian Dold
- * @author Benedikt Mueller
- * @author Christian Grothoff
- */
-#include "platform.h"
-#include <pthread.h>
-#include "taler_json_lib.h"
-#include "taler_mhd_lib.h"
-#include "taler-exchange-httpd_keystate.h"
-#include "taler-exchange-httpd_responses.h"
-#include "taler_exchangedb_plugin.h"
-
-
-/**
- * Taler protocol version in the format CURRENT:REVISION:AGE
- * as used by GNU libtool.  See
- * 
https://www.gnu.org/software/libtool/manual/html_node/Libtool-versioning.html
- *
- * Please be very careful when updating and follow
- * 
https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html#Updating-version-info
- * precisely.  Note that this version has NOTHING to do with the
- * release version, and the format is NOT the same that semantic
- * versioning uses either.
- *
- * When changing this version, you likely want to also update
- * #TALER_PROTOCOL_CURRENT and #TALER_PROTOCOL_AGE in
- * exchange_api_handle.c!
- */
-#define EXCHANGE_PROTOCOL_VERSION "8:0:0"
-
-
-/**
- * Signatures of an auditor over a denomination key of this exchange.
- */
-struct AuditorSignature
-{
-  /**
-   * We store the signatures in a DLL.
-   */
-  struct AuditorSignature *prev;
-
-  /**
-   * We store the signatures in a DLL.
-   */
-  struct AuditorSignature *next;
-
-  /**
-   * A signature from the auditor.
-   */
-  struct TALER_AuditorSignatureP asig;
-
-  /**
-   * Public key of the auditor.
-   */
-  struct TALER_AuditorPublicKeyP apub;
-
-  /**
-   * URL of the auditor. Allocated at the end of this struct.
-   */
-  const char *auditor_url;
-
-};
-
-
-/**
- * Entry in sorted array of denomination keys.  Sorted by starting
- * "start" time (validity period) of the `struct
- * TALER_DenominationKeyValidityPS`.
- */
-struct DenominationKeyEntry
-{
-
-  /**
-   * Reference to the public key.
-   * (Must also be in the `denomkey_map`).
-   */
-  const struct TALER_EXCHANGEDB_DenominationKey *dki;
-
-  /**
-   * Head of DLL of signatures for this @e dki.
-   */
-  struct AuditorSignature *as_head;
-
-  /**
-   * Tail of DLL of signatures for this @e dki.
-   */
-  struct AuditorSignature *as_tail;
-
-  /**
-   * Hash of the public denomination key.
-   */
-  struct GNUNET_HashCode denom_key_hash;
-
-};
-
-
-/**
- * Entry in (sorted) array with possible pre-build responses for /keys.
- * We keep pre-build responses for the various (valid) cherry-picking
- * values around.
- */
-struct KeysResponseData
-{
-
-  /**
-   * Response to return if the client supports (deflate) compression.
-   */
-  struct MHD_Response *response_compressed;
-
-  /**
-   * Response to return if the client does not support compression.
-   */
-  struct MHD_Response *response_uncompressed;
-
-  /**
-   * Cherry-picking timestamp the client must have set for this
-   * response to be valid.  0 if this is the "full" response.
-   * The client's request must include this date or a higher one
-   * for this response to be applicable.
-   */
-  struct GNUNET_TIME_Absolute cherry_pick_date;
-
-};
-
-
-/**
- * State we keep around while building an individual entry in the
- * `struct KeysResponseData` array, i.e. the global state for ONE of
- * the responses.
- */
-struct ResponseBuilderContext
-{
-
-  /**
-   * Hash context we used to combine the hashes of all denomination
-   * keys into one big hash for signing.
-   */
-  struct GNUNET_HashContext *hash_context;
-
-  /**
-   * JSON array with denomination key information.
-   */
-  json_t *denom_keys_array;
-
-  /**
-   * JSON array with auditor information.
-   */
-  json_t *auditors_array;
-
-  /**
-   * Keys after what issue date do we care about?
-   */
-  struct GNUNET_TIME_Absolute last_issue_date;
-
-  /**
-   * Flag set to #GNUNET_SYSERR on internal errors
-   */
-  int error;
-
-};
-
-
-/**
- * State we keep around while building the `struct KeysResponseData`
- * array, i.e. the global state for all of the responses.
- */
-struct ResponseFactoryContext
-{
-
-  /**
-   * JSON array with revoked denomination keys.  Every response
-   * always returns the full list (cherry picking does not apply
-   * for key revocations, as we cannot sort those by issue date).
-   */
-  json_t *recoup_array;
-
-  /**
-   * JSON array with signing keys.  Every response includes the full
-   * list, as it should be quite short anyway, and for simplicity the
-   * client only communicates the one time stamp of the last
-   * denomination key it knows when cherry picking.
-   */
-  json_t *sign_keys_array;
-
-  /**
-   * Sorted array of denomination keys.  Length is @e denomkey_array_length.
-   * Entries are sorted by the validity period's starting time.
-   */
-  struct DenominationKeyEntry *denomkey_array;
-
-  /**
-   * The main key state we are building everything for.
-   */
-  struct TEH_KS_StateHandle *key_state;
-
-  /**
-   * Time stamp used as "now".
-   */
-  struct GNUNET_TIME_Absolute now;
-
-  /**
-   * Length of the @e denomkey_array.
-   */
-  unsigned int denomkey_array_length;
-};
-
-
-/**
- * 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_KS_StateHandle
-{
-
-  /**
-   * Mapping from denomination keys to denomination key issue struct.
-   * Used to lookup the key by hash.
-   */
-  struct GNUNET_CONTAINER_MultiHashMap *denomkey_map;
-
-  /**
-   * Mapping from revoked denomination keys to denomination key issue struct.
-   * Used to lookup the key by hash.
-   */
-  struct GNUNET_CONTAINER_MultiHashMap *revoked_map;
-
-  /**
-   * Sorted array of responses to /keys (MUST be sorted by cherry-picking 
date) of
-   * length @e krd_array_length;
-   */
-  struct KeysResponseData *krd_array;
-
-  /**
-   * When did we initiate the key reloading?
-   */
-  struct GNUNET_TIME_Absolute reload_time;
-
-  /**
-   * When is the next key invalid and we have to reload? (We also
-   * reload on SIGUSR1.)
-   */
-  struct GNUNET_TIME_Absolute next_reload;
-
-  /**
-   * When does the first active denomination key expire (for deposit)?
-   */
-  struct GNUNET_TIME_Absolute min_dk_expire;
-
-  /**
-   * Exchange signing key that should be used currently.
-   */
-  struct TALER_EXCHANGEDB_PrivateSigningKeyInformationP current_sign_key_issue;
-
-  /**
-   * Reference count.  The struct is released when the RC hits zero.  Once
-   * this object is aliased, the reference counter must only be changed while
-   * holding the #internal_key_state_mutex.
-   */
-  unsigned int refcnt;
-
-  /**
-   * Length of the @e krd_array.
-   */
-  unsigned int krd_array_length;
-};
-
-
-/**
- * Exchange key state.  This is the long-term, read-only internal global state,
- * which the various threads "lock" to use in read-only ways.  We eventually
- * create a completely new object "on the side" and then start to return
- * the new read-only object to threads that ask. Once none of the threads
- * use the previous object (RC drops to zero), we discard it.
- *
- * Thus, this instance should never be used directly, instead reserve
- * access via #TEH_KS_acquire() and release it via #TEH_KS_release().
- *
- * As long as MHD threads are running, access to this field requires
- * locking the #internal_key_state_mutex.
- */
-static struct TEH_KS_StateHandle *internal_key_state;
-
-/**
- * Mutex protecting access to #internal_key_state.
- */
-static pthread_mutex_t internal_key_state_mutex = PTHREAD_MUTEX_INITIALIZER;
-
-/**
- * Configuration value LOOKAHEAD_PROVIDE that says for how far in the
- * future we want to provide exchange key information to clients.
- */
-static struct GNUNET_TIME_Relative conf_duration_provide;
-
-
-/* ************************** Clean up logic *********************** */
-
-
-/**
- * Release memory used by @a rfc.
- *
- * @param rfc factory to release (but do not #GNUNET_free() rfc itself!)
- */
-static void
-destroy_response_factory (struct ResponseFactoryContext *rfc)
-{
-  if (NULL != rfc->recoup_array)
-  {
-    json_decref (rfc->recoup_array);
-    rfc->recoup_array = NULL;
-  }
-  if (NULL != rfc->sign_keys_array)
-  {
-    json_decref (rfc->sign_keys_array);
-    rfc->sign_keys_array = NULL;
-  }
-  for (unsigned int i = 0; i<rfc->denomkey_array_length; i++)
-  {
-    struct DenominationKeyEntry *dke = &rfc->denomkey_array[i];
-    struct AuditorSignature *as;
-
-    while (NULL != (as = dke->as_head))
-    {
-      GNUNET_CONTAINER_DLL_remove (dke->as_head,
-                                   dke->as_tail,
-                                   as);
-      GNUNET_free (as);
-    }
-  }
-  GNUNET_array_grow (rfc->denomkey_array,
-                     rfc->denomkey_array_length,
-                     0);
-}
-
-
-/**
- * Release memory used by @a rbc.
- *
- * @param rbc memory to release, excluding @a rbc itself
- */
-static void
-destroy_response_builder (struct ResponseBuilderContext *rbc)
-{
-  if (NULL != rbc->denom_keys_array)
-  {
-    json_decref (rbc->denom_keys_array);
-    rbc->denom_keys_array = NULL;
-  }
-  if (NULL != rbc->auditors_array)
-  {
-    json_decref (rbc->auditors_array);
-    rbc->auditors_array = NULL;
-  }
-  if (NULL != rbc->hash_context)
-  {
-    GNUNET_CRYPTO_hash_context_abort (rbc->hash_context);
-    rbc->hash_context = NULL;
-  }
-}
-
-
-/**
- * Iterator for freeing denomination keys.
- *
- * @param cls closure with the `struct TEH_KS_StateHandle` (unused)
- * @param key hash of the denomination key (unused)
- * @param value coin details, a `struct TALER_EXCHANGEDB_DenominationKey`
- * @return #GNUNET_OK to continue to iterate,
- *  #GNUNET_NO to stop iteration with no error,
- *  #GNUNET_SYSERR to abort iteration with error!
- */
-static int
-free_denom_key (void *cls,
-                const struct GNUNET_HashCode *key,
-                void *value)
-{
-  struct TALER_EXCHANGEDB_DenominationKey *dki = value;
-
-  (void) cls;
-  (void) key;
-  if (NULL != dki->denom_priv.rsa_private_key)
-    GNUNET_CRYPTO_rsa_private_key_free (dki->denom_priv.rsa_private_key);
-  GNUNET_CRYPTO_rsa_public_key_free (dki->denom_pub.rsa_public_key);
-  GNUNET_free (dki);
-  return GNUNET_OK;
-}
-
-
-/**
- * Internal function to free key state. Reference count must be at zero.
- *
- * @param key_state the key state to free
- */
-static void
-ks_free (struct TEH_KS_StateHandle *key_state)
-{
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "KS release called (%p)\n",
-              key_state);
-  GNUNET_assert (0 == key_state->refcnt);
-  if (NULL != key_state->denomkey_map)
-  {
-    GNUNET_CONTAINER_multihashmap_iterate (key_state->denomkey_map,
-                                           &free_denom_key,
-                                           key_state);
-    GNUNET_CONTAINER_multihashmap_destroy (key_state->denomkey_map);
-    key_state->denomkey_map = NULL;
-  }
-  if (NULL != key_state->revoked_map)
-  {
-    GNUNET_CONTAINER_multihashmap_iterate (key_state->revoked_map,
-                                           &free_denom_key,
-                                           key_state);
-    GNUNET_CONTAINER_multihashmap_destroy (key_state->revoked_map);
-    key_state->revoked_map = NULL;
-  }
-  for (unsigned int i = 0; i<key_state->krd_array_length; i++)
-  {
-    struct KeysResponseData *krd = &key_state->krd_array[i];
-
-    if (NULL != krd->response_compressed)
-      MHD_destroy_response (krd->response_compressed);
-    if (NULL != krd->response_uncompressed)
-      MHD_destroy_response (krd->response_uncompressed);
-  }
-  GNUNET_array_grow (key_state->krd_array,
-                     key_state->krd_array_length,
-                     0);
-  GNUNET_free (key_state);
-}
-
-
-/* ************************* Signal logic ************************** */
-
-/**
- * Pipe used for signaling reloading of our key state.
- */
-static int reload_pipe[2] = { -1, -1 };
-
-
-/**
- * Handle a signal, writing relevant signal numbers to the pipe.
- *
- * @param signal_number the signal number
- */
-static void
-handle_signal (int signal_number)
-{
-  char c = (char) signal_number; /* never seen a signal_number > 127 on any 
platform */
-
-  (void) ! write (reload_pipe[1],
-                  &c,
-                  1);
-  /* While one might like to "handle errors" here, even logging via fprintf()
-     isn't safe inside of a signal handler. So there is nothing we safely CAN
-     do. OTOH, also very little that can go wrong in practice. Calling _exit()
-     on errors might be a possibility, but that might do more harm than good. 
*///
-}
-
-
-/* ************************** State builder ************************ */
-
-
-/**
- * Convert the public part of denomination key data to a JSON object.
- *
- * @param pk public key of the denomination
- * @param dki the denomination key issue information
- * @return a JSON object describing the denomination key issue (public part)
- */
-static json_t *
-denom_key_issue_to_json (
-  const struct TALER_DenominationPublicKey *pk,
-  const struct TALER_EXCHANGEDB_DenominationKeyInformationP *dki)
-{
-  struct TALER_Amount value;
-  struct TALER_Amount fee_withdraw;
-  struct TALER_Amount fee_deposit;
-  struct TALER_Amount fee_refresh;
-  struct TALER_Amount fee_refund;
-
-  TALER_amount_ntoh (&value,
-                     &dki->properties.value);
-  TALER_amount_ntoh (&fee_withdraw,
-                     &dki->properties.fee_withdraw);
-  TALER_amount_ntoh (&fee_deposit,
-                     &dki->properties.fee_deposit);
-  TALER_amount_ntoh (&fee_refresh,
-                     &dki->properties.fee_refresh);
-  TALER_amount_ntoh (&fee_refund,
-                     &dki->properties.fee_refund);
-  return
-    json_pack ("{s:o, s:o, s:o, s:o, s:o,"
-               " s:o, s:o, s:o, s:o, s:o,"
-               " s:o}",
-               "master_sig",
-               GNUNET_JSON_from_data_auto (&dki->signature),
-               "stamp_start",
-               GNUNET_JSON_from_time_abs (GNUNET_TIME_absolute_ntoh (
-                                            dki->properties.start)),
-               "stamp_expire_withdraw",
-               GNUNET_JSON_from_time_abs (GNUNET_TIME_absolute_ntoh (
-                                            dki->properties.expire_withdraw)),
-               "stamp_expire_deposit",
-               GNUNET_JSON_from_time_abs (GNUNET_TIME_absolute_ntoh (
-                                            dki->properties.expire_deposit)),
-               "stamp_expire_legal",
-               GNUNET_JSON_from_time_abs (GNUNET_TIME_absolute_ntoh (
-                                            dki->properties.expire_legal)),
-               /* 5 entries until here */
-               "denom_pub",
-               GNUNET_JSON_from_rsa_public_key (pk->rsa_public_key),
-               "value",
-               TALER_JSON_from_amount (&value),
-               "fee_withdraw",
-               TALER_JSON_from_amount (&fee_withdraw),
-               "fee_deposit",
-               TALER_JSON_from_amount (&fee_deposit),
-               "fee_refresh",
-               TALER_JSON_from_amount (&fee_refresh),
-               /* 10 entries until here */
-               "fee_refund",
-               TALER_JSON_from_amount (&fee_refund));
-}
-
-
-/**
- * Store a copy of @a dki in @a map.
- *
- * @param map hash map to store @a dki in
- * @param dki information to store in @a map
- * @return #GNUNET_OK on success,
- *         #GNUNET_NO if such an entry already exists
- */
-static int
-store_in_map (struct GNUNET_CONTAINER_MultiHashMap *map,
-              const struct TALER_EXCHANGEDB_DenominationKey *dki)
-{
-  /* First, we verify that the @a dki is actually well-formed.  While it comes
-     from our own hard disk, there is the possibility of misconfiguration
-     (i.e. bogus file in the directory), or that the administrator used the
-     wrong master public key, and we should not accept entries that are not
-     well-formed. *///
-  {
-    const struct TALER_EXCHANGEDB_DenominationKeyInformationP *dkip;
-    struct TALER_DenominationKeyValidityPS denom_key_issue;
-
-    dkip = &dki->issue;
-    denom_key_issue = dkip->properties;
-    /* The above is straight from our disk. We should not trust
-       that it is well-formed, so we setup crucial fields ourselves. */
-    denom_key_issue.purpose.purpose
-      = htonl (TALER_SIGNATURE_MASTER_DENOMINATION_KEY_VALIDITY);
-    denom_key_issue.purpose.size
-      = htonl (sizeof (struct TALER_DenominationKeyValidityPS));
-    denom_key_issue.master = TEH_master_public_key;
-
-    /* Check that the data we read matches our expectations */
-    if (0 !=
-        GNUNET_memcmp (&denom_key_issue,
-                       &dkip->properties))
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                  "Invalid fields in denomination key `%s'\n",
-                  GNUNET_h2s (&dkip->properties.denom_hash));
-      return GNUNET_SYSERR;
-    }
-
-    /* Also check the signature by the master public key */
-    if (GNUNET_SYSERR ==
-        GNUNET_CRYPTO_eddsa_verify (
-          TALER_SIGNATURE_MASTER_DENOMINATION_KEY_VALIDITY,
-          &denom_key_issue,
-          &dkip->signature.eddsa_signature,
-          &TEH_master_public_key.eddsa_pub))
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                  "Invalid signature on denomination key `%s'\n",
-                  GNUNET_h2s (&dkip->properties.denom_hash));
-      return GNUNET_SYSERR;
-    }
-  }
-
-  /* We need to make a deep copy of the @a dki, as the original was allocated
-     elsewhere and will be freed by the caller. */
-  {
-    struct TALER_EXCHANGEDB_DenominationKey *d2;
-
-    d2 = GNUNET_new (struct TALER_EXCHANGEDB_DenominationKey);
-    d2->issue = dki->issue;
-    if (GNUNET_OK !=
-        GNUNET_CONTAINER_multihashmap_put (map,
-                                           &d2->issue.properties.denom_hash,
-                                           d2,
-                                           
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                  "Duplicate denomination key `%s'\n",
-                  GNUNET_h2s (&d2->issue.properties.denom_hash));
-      GNUNET_free (d2);
-      return GNUNET_NO;
-    }
-
-    /* finish *deep* part of deep copy */
-    if (NULL != dki->denom_priv.rsa_private_key)
-    {
-      /* we might be past the withdraw period, so private key could have been 
deleted,
-         so only try to (deep) copy if non-NULL. */
-      d2->denom_priv.rsa_private_key
-        = GNUNET_CRYPTO_rsa_private_key_dup (dki->denom_priv.rsa_private_key);
-    }
-    d2->denom_pub.rsa_public_key
-      = GNUNET_CRYPTO_rsa_public_key_dup (dki->denom_pub.rsa_public_key);
-  }
-  return GNUNET_OK;
-}
-
-
-/**
- * Closure for #add_revocations_transaction().
- */
-struct AddRevocationContext
-{
-  /**
-   * Denomination key that is revoked.
-   */
-  const struct TALER_EXCHANGEDB_DenominationKey *dki;
-
-  /**
-   * Signature affirming the revocation.
-   */
-  const struct TALER_MasterSignatureP *revocation_master_sig;
-};
-
-
-/**
- * Execute transaction to add revocations.
- *
- * @param cls closure with the `struct AddRevocationContext *`
- * @param connection NULL
- * @param session database session to use
- * @param[out] mhd_ret not used
- * @return transaction status
- */
-static enum GNUNET_DB_QueryStatus
-add_revocations_transaction (void *cls,
-                             struct MHD_Connection *connection,
-                             struct TALER_EXCHANGEDB_Session *session,
-                             MHD_RESULT *mhd_ret)
-{
-  struct AddRevocationContext *arc = cls;
-  enum GNUNET_DB_QueryStatus qs;
-  struct TALER_MasterSignatureP master_sig;
-  uint64_t rowid;
-
-  (void) connection;
-  (void) mhd_ret;
-  qs = TEH_plugin->get_denomination_revocation (TEH_plugin->cls,
-                                                session,
-                                                &arc->dki->issue.properties.
-                                                denom_hash,
-                                                &master_sig,
-                                                &rowid);
-  if (0 > qs)
-    return qs; /* failure */
-  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
-    return qs; /* already exists == success */
-  return TEH_plugin->insert_denomination_revocation (TEH_plugin->cls,
-                                                     session,
-                                                     
&arc->dki->issue.properties
-                                                     .denom_hash,
-                                                     
arc->revocation_master_sig);
-}
-
-
-/**
- * Execute transaction to add a denomination to the DB.
- *
- * @param cls closure with the `const struct TALER_EXCHANGEDB_DenominationKey 
*`
- * @param connection NULL
- * @param session database session to use
- * @param[out] mhd_ret not used
- * @return transaction status
- */
-static enum GNUNET_DB_QueryStatus
-add_denomination_transaction (void *cls,
-                              struct MHD_Connection *connection,
-                              struct TALER_EXCHANGEDB_Session *session,
-                              MHD_RESULT *mhd_ret)
-{
-  const struct TALER_EXCHANGEDB_DenominationKey *dki = cls;
-  enum GNUNET_DB_QueryStatus qs;
-  struct TALER_EXCHANGEDB_DenominationKeyInformationP issue_exists;
-
-  (void) connection;
-  (void) mhd_ret;
-  qs = TEH_plugin->get_denomination_info (TEH_plugin->cls,
-                                          session,
-                                          &dki->issue.properties.denom_hash,
-                                          &issue_exists);
-  if (0 > qs)
-    return qs;
-  if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
-    return qs;
-  return TEH_plugin->insert_denomination_info (TEH_plugin->cls,
-                                               session,
-                                               &dki->denom_pub,
-                                               &dki->issue);
-}
-
-
-/**
- * Iterator for (re)loading/initializing denomination keys.
- *
- * @param cls closure with a `struct ResponseFactoryContext *`
- * @param dki the denomination key issue
- * @param alias coin alias
- * @return #GNUNET_OK to continue to iterate,
- *  #GNUNET_NO to stop iteration with no error,
- *  #GNUNET_SYSERR to abort iteration with error!
- */
-static int
-reload_keys_denom_iter (void *cls,
-                        const char *alias,
-                        const struct TALER_EXCHANGEDB_DenominationKey *dki)
-{
-  struct ResponseFactoryContext *rfc = cls;
-  struct TEH_KS_StateHandle *key_state = rfc->key_state;
-  struct GNUNET_TIME_Absolute start;
-  struct GNUNET_TIME_Absolute horizon;
-  struct GNUNET_TIME_Absolute expire_deposit;
-
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Loading denomination key `%s' (%s)\n",
-              alias,
-              GNUNET_h2s (&dki->issue.properties.denom_hash));
-  expire_deposit = GNUNET_TIME_absolute_ntoh (
-    dki->issue.properties.expire_deposit);
-  if (expire_deposit.abs_value_us < rfc->now.abs_value_us)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "Skipping expired denomination key `%s'\n",
-                alias);
-    return GNUNET_OK;
-  }
-  if (0 != GNUNET_memcmp (&dki->issue.properties.master,
-                          &TEH_master_public_key))
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Master key in denomination key file `%s' does not match! 
Skipping it.\n",
-                alias);
-    return GNUNET_OK;
-  }
-
-  horizon = GNUNET_TIME_absolute_add (rfc->now,
-                                      conf_duration_provide);
-  start = GNUNET_TIME_absolute_ntoh (dki->issue.properties.start);
-  if (start.abs_value_us > horizon.abs_value_us)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "Skipping future denomination key `%s' (%s), validity starts 
at %s\n",
-                alias,
-                GNUNET_h2s (&dki->issue.properties.denom_hash),
-                GNUNET_STRINGS_absolute_time_to_string (start));
-    return GNUNET_OK;
-  }
-
-  if (GNUNET_OK !=
-      TEH_DB_run_transaction (NULL,
-                              "add denomination key",
-                              NULL,
-                              &add_denomination_transaction,
-                              (void *) dki))
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Could not persist denomination key %s in DB. Committing 
suicide via SIGTERM.\n",
-                GNUNET_h2s (&dki->issue.properties.denom_hash));
-    handle_signal (SIGTERM);
-    return GNUNET_SYSERR;
-  }
-  GNUNET_assert (NULL != dki->denom_priv.rsa_private_key);
-  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              "Adding denomination key `%s' (%s) to active set\n",
-              alias,
-              GNUNET_h2s (&dki->issue.properties.denom_hash));
-  if (GNUNET_NO /* entry already exists */ ==
-      store_in_map (key_state->denomkey_map,
-                    dki))
-    return GNUNET_OK; /* do not update expiration if entry exists */
-  key_state->min_dk_expire = GNUNET_TIME_absolute_min 
(key_state->min_dk_expire,
-                                                       expire_deposit);
-  return GNUNET_OK;
-}
-
-
-/**
- * Iterator for revocation of denomination keys.
- *
- * @param cls closure with a `struct ResponseFactoryContext *`
- * @param denom_hash hash of revoked denomination public key
- * @param revocation_master_sig signature showing @a denom_hash was revoked
- * @return #GNUNET_OK to continue to iterate,
- *  #GNUNET_NO to stop iteration with no error,
- *  #GNUNET_SYSERR to abort iteration with error!
- */
-static int
-revocations_iter (void *cls,
-                  const struct GNUNET_HashCode *denom_hash,
-                  const struct TALER_MasterSignatureP *revocation_master_sig)
-{
-  struct ResponseFactoryContext *rfc = cls;
-  struct TEH_KS_StateHandle *key_state = rfc->key_state;
-  struct AddRevocationContext arc;
-  struct TALER_EXCHANGEDB_DenominationKey *dki;
-
-  dki = GNUNET_CONTAINER_multihashmap_get (key_state->denomkey_map,
-                                           denom_hash);
-  if (NULL == dki)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                "Revoked denomination `%s' unknown (or duplicate file), 
ignoring revocation\n",
-                GNUNET_h2s (denom_hash));
-    return GNUNET_OK;
-
-  }
-  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              "Adding denomination key `%s' to revocation set\n",
-              GNUNET_h2s (denom_hash));
-  GNUNET_assert (GNUNET_YES ==
-                 GNUNET_CONTAINER_multihashmap_remove (key_state->denomkey_map,
-                                                       denom_hash,
-                                                       dki));
-  GNUNET_assert (GNUNET_YES ==
-                 GNUNET_CONTAINER_multihashmap_put (key_state->revoked_map,
-                                                    &dki->issue.properties.
-                                                    denom_hash,
-                                                    dki,
-                                                    
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
-  /* Try to insert revocation into DB */
-  arc.dki = dki;
-  arc.revocation_master_sig = revocation_master_sig;
-  if (GNUNET_OK !=
-      TEH_DB_run_transaction (NULL,
-                              "add denomination key revocation",
-                              NULL,
-                              &add_revocations_transaction,
-                              &arc))
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Failed to add revocation to database. This is fatal. 
Committing suicide via SIGTERM.\n");
-    handle_signal (SIGTERM);
-    return GNUNET_SYSERR;
-  }
-
-  {
-    json_t *obj;
-
-    obj = json_pack ("{s:o}",
-                     "h_denom_pub",
-                     GNUNET_JSON_from_data_auto (denom_hash));
-    if (NULL == obj)
-    {
-      GNUNET_break (0);
-      return GNUNET_SYSERR;
-    }
-    if (0 !=
-        json_array_append_new (rfc->recoup_array,
-                               obj))
-    {
-      GNUNET_break (0);
-      return GNUNET_SYSERR;
-    }
-  }
-  return GNUNET_OK;
-}
-
-
-/**
- * Convert the public part of a sign key issue to a JSON object.
- *
- * @param ski the sign key issue
- * @param ski_sig signature over @a ski
- * @return a JSON object describing the sign key issue (public part)
- */
-static json_t *
-sign_key_issue_to_json (const struct TALER_ExchangeSigningKeyValidityPS *ski,
-                        const struct TALER_MasterSignatureP *ski_sig)
-{
-  return
-    json_pack ("{s:o, s:o, s:o, s:o, s:o}",
-               "stamp_start",
-               GNUNET_JSON_from_time_abs (GNUNET_TIME_absolute_ntoh (
-                                            ski->start)),
-               "stamp_expire",
-               GNUNET_JSON_from_time_abs (GNUNET_TIME_absolute_ntoh (
-                                            ski->expire)),
-               "stamp_end",
-               GNUNET_JSON_from_time_abs (GNUNET_TIME_absolute_ntoh 
(ski->end)),
-               "master_sig",
-               GNUNET_JSON_from_data_auto (ski_sig),
-               "key",
-               GNUNET_JSON_from_data_auto (&ski->signkey_pub));
-}
-
-
-/**
- * Iterator for sign keys.  Adds current and near-future signing keys
- * to the `sign_keys_array` and stores the current one in the
- * `key_state`.
- *
- * @param cls closure with the `struct ResponseFactoryContext *`
- * @param filename name of the file the key came from
- * @param ski the sign key issue
- * @return #GNUNET_OK to continue to iterate,
- *  #GNUNET_NO to stop iteration with no error,
- *  #GNUNET_SYSERR to abort iteration with error!
- */
-static int
-reload_keys_sign_iter (
-  void *cls,
-  const char *filename,
-  const struct TALER_EXCHANGEDB_PrivateSigningKeyInformationP *ski)
-{
-  struct ResponseFactoryContext *rfc = cls;
-  struct TEH_KS_StateHandle *key_state = rfc->key_state;
-  struct GNUNET_TIME_Absolute now;
-  struct GNUNET_TIME_Absolute horizon;
-
-  horizon = GNUNET_TIME_relative_to_absolute (conf_duration_provide);
-  if (GNUNET_TIME_absolute_ntoh (ski->issue.start).abs_value_us >
-      horizon.abs_value_us)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "Skipping future signing key `%s'\n",
-                filename);
-    return GNUNET_OK;
-  }
-  now = GNUNET_TIME_absolute_get ();
-  if (GNUNET_TIME_absolute_ntoh (ski->issue.expire).abs_value_us <
-      now.abs_value_us)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "Skipping expired signing key `%s'\n",
-                filename);
-    return GNUNET_OK;
-  }
-
-  if (0 != GNUNET_memcmp (&ski->issue.master_public_key,
-                          &TEH_master_public_key))
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Master key in signing key file `%s' does not match! Skipping 
it.\n",
-                filename);
-    return GNUNET_OK;
-  }
-
-  /* The signkey is valid at this time, check if it's more recent than
-     what we have so far! */
-  if ( (GNUNET_TIME_absolute_ntoh (
-          key_state->current_sign_key_issue.issue.start).abs_value_us <
-        GNUNET_TIME_absolute_ntoh (ski->issue.start).abs_value_us) &&
-       (GNUNET_TIME_absolute_ntoh (ski->issue.start).abs_value_us <
-        now.abs_value_us) )
-  {
-    /* We use the most recent one, if it is valid now (not just in the near 
future) */
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "Found signing key valid until `%s'\n",
-                GNUNET_STRINGS_absolute_time_to_string (
-                  GNUNET_TIME_absolute_ntoh (
-                    key_state->current_sign_key_issue.issue.end)));
-    key_state->current_sign_key_issue = *ski;
-  }
-  if (0 !=
-      json_array_append_new (rfc->sign_keys_array,
-                             sign_key_issue_to_json (&ski->issue,
-                                                     &ski->master_sig)))
-  {
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
-  }
-  return GNUNET_OK;
-}
-
-
-/**
- * @brief Iterator called with auditor information.
- * Check that the @a mpub actually matches this exchange, and then
- * add the auditor information to our /keys response (if it is
- * (still) applicable).
- *
- * @param cls closure with the `struct ResponseFactoryContext *`
- * @param apub the auditor's public key
- * @param auditor_url URL of the auditor
- * @param mpub the exchange's public key (as expected by the auditor)
- * @param dki_len length of @a dki and @a asigs
- * @param asigs array with the auditor's signatures, of length @a dki_len
- * @param dki array of denomination coin data signed by the auditor
- * @return #GNUNET_OK to continue to iterate,
- *  #GNUNET_NO to stop iteration with no error,
- *  #GNUNET_SYSERR to abort iteration with error!
- */
-static int
-reload_auditor_iter (void *cls,
-                     const struct TALER_AuditorPublicKeyP *apub,
-                     const char *auditor_url,
-                     const struct TALER_MasterPublicKeyP *mpub,
-                     unsigned int dki_len,
-                     const struct TALER_AuditorSignatureP *asigs,
-                     const struct TALER_DenominationKeyValidityPS *dki)
-{
-  struct ResponseFactoryContext *rfc = cls;
-  struct TEH_KS_StateHandle *key_state = rfc->key_state;
-
-  /* Check if the signature is at least for this exchange. */
-  if (0 != memcmp (&mpub->eddsa_pub,
-                   &TEH_master_public_key,
-                   sizeof (struct GNUNET_CRYPTO_EddsaPublicKey)))
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                "Auditing information provided for a different exchange, 
ignored\n");
-    return GNUNET_OK;
-  }
-  /* Filter the auditor information for those for which the
-     keys actually match the denomination keys that are active right now */
-  for (unsigned int i = 0; i<dki_len; i++)
-  {
-    int matched;
-
-    if (GNUNET_YES !=
-        GNUNET_CONTAINER_multihashmap_contains (key_state->denomkey_map,
-                                                &dki[i].denom_hash))
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  "Found auditor signature for DK `%s', but key is not in 
active map\n",
-                  GNUNET_h2s (&dki[i].denom_hash));
-      continue;
-    }
-    GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                "Found auditor signature for DK `%s'\n",
-                GNUNET_h2s (&dki[i].denom_hash));
-    /* Note: the array is sorted, we could theoretically
-       speed this up using a binary search. */
-    matched = GNUNET_NO;
-    for (unsigned int j = 0; j<rfc->denomkey_array_length; j++)
-    {
-      struct DenominationKeyEntry *dke = &rfc->denomkey_array[j];
-      struct AuditorSignature *as;
-
-      if (0 !=
-          memcmp (&dki[i].denom_hash,
-                  &dke->dki->issue.properties.denom_hash,
-                  sizeof (struct GNUNET_HashCode)))
-        continue;
-      if (0 !=
-          memcmp (&dki[i],
-                  &dke->dki->issue.properties,
-                  sizeof (struct TALER_DenominationKeyValidityPS)))
-      {
-        /* if the hash is the same, the properties should also match! */
-        GNUNET_break (0);
-        continue;
-      }
-      as = GNUNET_malloc (sizeof (struct AuditorSignature)
-                          + strlen (auditor_url) + 1);
-      as->asig = asigs[i];
-      as->apub = *apub;
-      as->auditor_url = (const char *) &as[1];
-      memcpy (&as[1],
-              auditor_url,
-              strlen (auditor_url) + 1);
-      GNUNET_CONTAINER_DLL_insert (dke->as_head,
-                                   dke->as_tail,
-                                   as);
-      matched = GNUNET_YES;
-      break;
-    }
-    if (GNUNET_NO == matched)
-    {
-      GNUNET_break (0);
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                  "DK `%s' is in active map, but not in array!?\n",
-                  GNUNET_h2s (&dki[i].denom_hash));
-    }
-  }
-  return GNUNET_OK;
-}
-
-
-/**
- * Initialize the `denomkey_array`.  We are called once per
- * array index, which is tracked in `denomkey_array_length` (the
- * array will be of sufficient size).  Set the pointer to the
- * denomination key and increment the `denomkey_array_length`.
- *
- * @param cls a `struct ResponseFactoryContext`
- * @param denom_hash hash of a denomination key
- * @param value a `struct TALER_EXCHANGEDB_DenominationKey *`
- * @return #GNUNET_OK
- */
-static int
-initialize_denomkey_array (void *cls,
-                           const struct GNUNET_HashCode *denom_hash,
-                           void *value)
-{
-  struct ResponseFactoryContext *rfc = cls;
-  struct TALER_EXCHANGEDB_DenominationKey *dki = value;
-
-  rfc->denomkey_array[rfc->denomkey_array_length].denom_key_hash = *denom_hash;
-  rfc->denomkey_array[rfc->denomkey_array_length++].dki = dki;
-  return GNUNET_OK;
-}
-
-
-/**
- * Comparator used to sort the `struct DenominationKeyEntry` array
- * by the validity period's starting time of the keys.  Must match
- * the expected sorting by cherry_pick_date (which is based on the
- * issue.properties.start) used in #krd_search_comparator.
- *
- * @param k1 a `struct DenominationKeyEntry *`
- * @param k2 a `struct DenominationKeyEntry *`
- * @return -1 if k1 starts before k2,
- *          1 if k2 starts before k1,
- *          0 if they start at the same time
- */
-static int
-denomkey_array_sort_comparator (const void *k1,
-                                const void *k2)
-{
-  const struct DenominationKeyEntry *dke1 = k1;
-  const struct DenominationKeyEntry *dke2 = k2;
-  struct GNUNET_TIME_Absolute d1
-    = GNUNET_TIME_absolute_ntoh (dke1->dki->issue.properties.start);
-  struct GNUNET_TIME_Absolute d2
-    = GNUNET_TIME_absolute_ntoh (dke2->dki->issue.properties.start);
-
-  if (d1.abs_value_us < d2.abs_value_us)
-    return -1;
-  if (d1.abs_value_us > d2.abs_value_us)
-    return 1;
-  return 0;
-}
-
-
-/**
- * Produce HTTP "Date:" header.
- *
- * @param at time to write to @a date
- * @param[out] date where to write the header, with
- *        at least 128 bytes available space.
- */
-static void
-get_date_string (struct GNUNET_TIME_Absolute at,
-                 char date[128])
-{
-  static const char *const days[] =
-  { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
-  static const char *const mons[] =
-  { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct",
-    "Nov", "Dec"};
-  struct tm now;
-  time_t t;
-#if ! defined(HAVE_C11_GMTIME_S) && ! defined(HAVE_W32_GMTIME_S) && \
-  ! defined(HAVE_GMTIME_R)
-  struct tm*pNow;
-#endif
-
-  date[0] = 0;
-  t = (time_t) (at.abs_value_us / 1000LL / 1000LL);
-#if defined(HAVE_C11_GMTIME_S)
-  if (NULL == gmtime_s (&t, &now))
-    return;
-#elif defined(HAVE_W32_GMTIME_S)
-  if (0 != gmtime_s (&now, &t))
-    return;
-#elif defined(HAVE_GMTIME_R)
-  if (NULL == gmtime_r (&t, &now))
-    return;
-#else
-  pNow = gmtime (&t);
-  if (NULL == pNow)
-    return;
-  now = *pNow;
-#endif
-  sprintf (date,
-           "%3s, %02u %3s %04u %02u:%02u:%02u GMT",
-           days[now.tm_wday % 7],
-           (unsigned int) now.tm_mday,
-           mons[now.tm_mon % 12],
-           (unsigned int) (1900 + now.tm_year),
-           (unsigned int) now.tm_hour,
-           (unsigned int) now.tm_min,
-           (unsigned int) now.tm_sec);
-}
-
-
-/**
- * Add the headers we want to set for every /keys response.
- *
- * @param key_state the key state to use
- * @param[in,out] response the response to modify
- * @return #GNUNET_OK on success
- */
-static int
-setup_general_response_headers (const struct TEH_KS_StateHandle *key_state,
-                                struct MHD_Response *response)
-{
-  char dat[128];
-
-  TALER_MHD_add_global_headers (response);
-  GNUNET_break (MHD_YES ==
-                MHD_add_response_header (response,
-                                         MHD_HTTP_HEADER_CONTENT_TYPE,
-                                         "application/json"));
-  get_date_string (key_state->reload_time,
-                   dat);
-  GNUNET_break (MHD_YES ==
-                MHD_add_response_header (response,
-                                         MHD_HTTP_HEADER_LAST_MODIFIED,
-                                         dat));
-  if (0 != key_state->next_reload.abs_value_us)
-  {
-    struct GNUNET_TIME_Absolute m;
-
-    m = GNUNET_TIME_relative_to_absolute (TEH_max_keys_caching);
-    m = GNUNET_TIME_absolute_min (m,
-                                  key_state->next_reload);
-    get_date_string (m,
-                     dat);
-    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                "Setting /keys 'Expires' header to '%s'\n",
-                dat);
-    GNUNET_break (MHD_YES ==
-                  MHD_add_response_header (response,
-                                           MHD_HTTP_HEADER_EXPIRES,
-                                           dat));
-  }
-  return GNUNET_OK;
-}
-
-
-/**
- * Information about an auditor to be added.
- */
-struct AuditorEntry
-{
-  /**
-   * URL of the auditor (allocated still as part of a
-   * `struct AuditorSignature`, do not free!).
-   */
-  const char *auditor_url;
-
-  /**
-   * Public key of the auditor (allocated still as part of a
-   * `struct AuditorSignature`, do not free!).
-   */
-  const struct TALER_AuditorPublicKeyP *apub;
-
-  /**
-   * Array of denomination keys and auditor signatures.
-   */
-  json_t *ar;
-
-};
-
-
-/**
- * Free auditor entry from the hash map.
- *
- * @param cls NULL
- * @param key unused
- * @param value a `struct AuditorEntry` to free
- * @return #GNUNET_OK (to continue to iterate)
- */
-static int
-free_auditor_entry (void *cls,
-                    const struct GNUNET_HashCode *key,
-                    void *value)
-{
-  struct AuditorEntry *ae = value;
-
-  (void) cls;
-  (void) key;
-  json_decref (ae->ar);
-  GNUNET_free (ae);
-  return GNUNET_OK;
-}
-
-
-/**
- * Convert auditor entries from the hash map to entries
- * in the auditor array, free the auditor entry as well.
- *
- * @param cls a `struct ResponseBuilderContext *`
- * @param key unused
- * @param value a `struct AuditorEntry` to add to the `auditors_array`
- * @return #GNUNET_OK (to continue to iterate), #GNUNET_SYSERR to terminate 
with error
- */
-static int
-add_auditor_entry (void *cls,
-                   const struct GNUNET_HashCode *key,
-                   void *value)
-{
-  struct ResponseBuilderContext *rbc = cls;
-  struct AuditorEntry *ae = value;
-  json_t *ao;
-
-  (void) key;
-  ao = json_pack ("{s:o, s:s, s:o}",
-                  "denomination_keys", ae->ar,
-                  "auditor_url", ae->auditor_url,
-                  "auditor_pub", GNUNET_JSON_from_data_auto (ae->apub));
-  if (NULL == ao)
-  {
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
-  }
-  if (0 !=
-      json_array_append_new (rbc->auditors_array,
-                             ao))
-  {
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
-  }
-  GNUNET_free (ae);
-  return GNUNET_OK;
-}
-
-
-/**
- * Initialize @a krd for the given @a cherry_pick_date using
- * the key data in @a rfc.  This function actually builds the
- * respective JSON replies (compressed and uncompressed).
- *
- * @param rfc factory with key material
- * @param[out] krd response object to initialize
- * @param denom_off offset in the @a rfc's `denomkey_array` at which
- *        keys beyond the @a cherry_pick_date are stored
- * @param cherry_pick_date cut-off date to use
- * @return #GNUNET_OK on success, #GNUNET_SYSERR on error
- */
-static int
-build_keys_response (const struct ResponseFactoryContext *rfc,
-                     struct KeysResponseData *krd,
-                     unsigned int denom_off,
-                     struct GNUNET_TIME_Absolute cherry_pick_date)
-{
-  struct ResponseBuilderContext rbc;
-  json_t *keys;
-  struct TALER_ExchangeKeySetPS ks;
-  struct TALER_ExchangeSignatureP sig;
-  char *keys_json;
-  struct GNUNET_TIME_Relative reserve_closing_delay;
-  void *keys_jsonz;
-  size_t keys_jsonz_size;
-  int comp;
-
-  krd->cherry_pick_date = cherry_pick_date;
-
-  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              "Creating /keys for cherry pick date %s\n",
-              GNUNET_STRINGS_absolute_time_to_string (cherry_pick_date));
-
-  /* Initialize `rbc` */
-  memset (&rbc,
-          0,
-          sizeof (rbc));
-  rbc.denom_keys_array = json_array ();
-  if (NULL == rbc.denom_keys_array)
-  {
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
-  }
-  rbc.auditors_array = json_array ();
-  if (NULL == rbc.auditors_array)
-  {
-    destroy_response_builder (&rbc);
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
-  }
-  rbc.hash_context = GNUNET_CRYPTO_hash_context_start ();
-
-  /* Go over relevant denomination keys. */
-  {
-    struct GNUNET_CONTAINER_MultiHashMap *auditors;
-
-    auditors = GNUNET_CONTAINER_multihashmap_create (4,
-                                                     GNUNET_NO);
-    for (unsigned int i = denom_off; i<rfc->denomkey_array_length; i++)
-    {
-      /* Add denomination key to the response */
-      const struct DenominationKeyEntry *dke
-        = &rfc->denomkey_array[i];
-      const struct GNUNET_HashCode *denom_key_hash
-        = &dke->denom_key_hash;
-
-      GNUNET_CRYPTO_hash_context_read (rbc.hash_context,
-                                       denom_key_hash,
-                                       sizeof (struct GNUNET_HashCode));
-      if (0 !=
-          json_array_append_new (rbc.denom_keys_array,
-                                 denom_key_issue_to_json (&dke->dki->denom_pub,
-                                                          &dke->dki->issue)))
-      {
-        GNUNET_break (0);
-        destroy_response_builder (&rbc);
-        return GNUNET_SYSERR;
-      }
-
-      /* Add auditor data */
-      for (const struct AuditorSignature *as = dke->as_head;
-           NULL != as;
-           as = as->next)
-      {
-        struct GNUNET_HashCode ahash;
-        struct AuditorEntry *ae;
-
-        GNUNET_CRYPTO_hash (&as->apub,
-                            sizeof (as->apub),
-                            &ahash);
-        ae = GNUNET_CONTAINER_multihashmap_get (auditors,
-                                                &ahash);
-        if (NULL == ae)
-        {
-          ae = GNUNET_new (struct AuditorEntry);
-          ae->auditor_url = as->auditor_url;
-          ae->ar = json_array ();
-          ae->apub = &as->apub;
-          GNUNET_assert (GNUNET_YES ==
-                         GNUNET_CONTAINER_multihashmap_put (auditors,
-                                                            &ahash,
-                                                            ae,
-                                                            
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
-        }
-        if (0 !=
-            json_array_append_new (ae->ar,
-                                   json_pack ("{s:o, s:o}",
-                                              "denom_pub_h",
-                                              GNUNET_JSON_from_data_auto (
-                                                denom_key_hash),
-                                              "auditor_sig",
-                                              GNUNET_JSON_from_data_auto (
-                                                &as->asig))))
-        {
-          destroy_response_builder (&rbc);
-          GNUNET_break (0);
-          GNUNET_CONTAINER_multihashmap_iterate (auditors,
-                                                 &free_auditor_entry,
-                                                 NULL);
-          GNUNET_CONTAINER_multihashmap_destroy (auditors);
-          return GNUNET_SYSERR;
-        }
-      }
-    }
-
-    GNUNET_CONTAINER_multihashmap_iterate (auditors,
-                                           &add_auditor_entry,
-                                           &rbc);
-    GNUNET_CONTAINER_multihashmap_destroy (auditors);
-  }
-
-  /* Sign hash over denomination keys */
-  ks.purpose.size = htonl (sizeof (ks));
-  ks.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_KEY_SET);
-  ks.list_issue_date = GNUNET_TIME_absolute_hton (rfc->key_state->reload_time);
-  GNUNET_CRYPTO_hash_context_finish (rbc.hash_context,
-                                     &ks.hc);
-  rbc.hash_context = NULL;
-  GNUNET_CRYPTO_eddsa_sign (
-    &rfc->key_state->current_sign_key_issue.signkey_priv.eddsa_priv,
-    &ks,
-    &sig.eddsa_signature);
-  if (GNUNET_OK !=
-      GNUNET_CONFIGURATION_get_value_time (TEH_cfg,
-                                           "exchangedb",
-                                           "IDLE_RESERVE_EXPIRATION_TIME",
-                                           &reserve_closing_delay))
-  {
-    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
-                               "exchangedb",
-                               "IDLE_RESERVE_EXPIRATION_TIME");
-    /* use default */
-    reserve_closing_delay = GNUNET_TIME_relative_multiply (
-      GNUNET_TIME_UNIT_WEEKS,
-      4);
-  }
-  /* Build /keys response */
-  keys = json_pack ("{s:s, 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),
-                    "reserve_closing_delay", GNUNET_JSON_from_time_rel (
-                      reserve_closing_delay),
-                    "signkeys", rfc->sign_keys_array,
-                    "recoup", rfc->recoup_array,
-                    /* 6-10 */
-                    "denoms", rbc.denom_keys_array,
-                    "auditors", rbc.auditors_array,
-                    "list_issue_date", GNUNET_JSON_from_time_abs (
-                      rfc->key_state->reload_time),
-                    "eddsa_pub", GNUNET_JSON_from_data_auto (
-                      
&rfc->key_state->current_sign_key_issue.issue.signkey_pub),
-                    "eddsa_sig", GNUNET_JSON_from_data_auto (&sig));
-  if (NULL == keys)
-  {
-    destroy_response_builder (&rbc);
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
-  }
-  rbc.denom_keys_array = NULL;
-  rbc.auditors_array = NULL;
-  destroy_response_builder (&rbc);
-
-  /* Convert /keys response to UTF8-String */
-  keys_json = json_dumps (keys,
-                          JSON_INDENT (2));
-  json_decref (keys);
-  if (NULL == keys_json)
-  {
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
-  }
-  /* Keep copy for later compression... */
-  keys_jsonz = GNUNET_strdup (keys_json);
-  keys_jsonz_size = strlen (keys_json);
-
-  /* Create uncompressed response */
-  krd->response_uncompressed
-    = MHD_create_response_from_buffer (keys_jsonz_size,
-                                       keys_json,
-                                       MHD_RESPMEM_MUST_FREE);
-  if (NULL == krd->response_uncompressed)
-  {
-    GNUNET_break (0);
-    GNUNET_free (keys_json);
-    GNUNET_free (keys_jsonz);
-    return GNUNET_SYSERR;
-  }
-  if (GNUNET_OK !=
-      setup_general_response_headers (rfc->key_state,
-                                      krd->response_uncompressed))
-  {
-    GNUNET_break (0);
-    GNUNET_free (keys_jsonz);
-    return GNUNET_SYSERR;
-  }
-
-  /* Also compute compressed version of /keys response */
-  comp = TALER_MHD_body_compress (&keys_jsonz,
-                                  &keys_jsonz_size);
-  krd->response_compressed
-    = MHD_create_response_from_buffer (keys_jsonz_size,
-                                       keys_jsonz,
-                                       MHD_RESPMEM_MUST_FREE);
-  if (NULL == krd->response_compressed)
-  {
-    GNUNET_break (0);
-    GNUNET_free (keys_jsonz);
-    return GNUNET_SYSERR;
-  }
-  /* If the response is actually compressed, set the
-     respective header. */
-  if ( (MHD_YES == comp) &&
-       (MHD_YES !=
-        MHD_add_response_header (krd->response_compressed,
-                                 MHD_HTTP_HEADER_CONTENT_ENCODING,
-                                 "deflate")) )
-  {
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
-  }
-  if (GNUNET_OK !=
-      setup_general_response_headers (rfc->key_state,
-                                      krd->response_compressed))
-  {
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
-  }
-  return GNUNET_OK;
-}
-
-
-/**
- * Function called with information about the exchange's denomination
- * keys based on what is known in the database. Used to learn our
- * public keys (after the private keys are deleted, we still need to
- * have the public keys around for a while to verify signatures).
- *
- * This function checks if the @a denom_pub is already known to us,
- * and if not adds it to our set.
- *
- * @param cls closure, a `struct ResponseFactoryContext *`
- * @param denom_pub public key of the denomination
- * @param issue detailed information about the denomination (value, expiration 
times, fees)
- */
-static void
-reload_public_denoms_cb (
-  void *cls,
-  const struct TALER_DenominationPublicKey *denom_pub,
-  const struct TALER_EXCHANGEDB_DenominationKeyInformationP *issue)
-{
-  struct ResponseFactoryContext *rfc = cls;
-  struct TALER_EXCHANGEDB_DenominationKey dki;
-  int ret;
-
-  if (rfc->now.abs_value_us > GNUNET_TIME_absolute_ntoh
-        (issue->properties.expire_legal).abs_value_us)
-  {
-    /* Expired key, discard.  */
-    return;
-  }
-
-  if (NULL !=
-      GNUNET_CONTAINER_multihashmap_get (rfc->key_state->denomkey_map,
-                                         &issue->properties.denom_hash))
-    return; /* exists / known */
-  if (NULL !=
-      GNUNET_CONTAINER_multihashmap_get (rfc->key_state->revoked_map,
-                                         &issue->properties.denom_hash))
-    return; /* exists / known */
-  /* zero-out, just for future-proofing */
-  memset (&dki,
-          0,
-          sizeof (dki));
-  dki.denom_priv.rsa_private_key = NULL; /* not available! */
-  dki.denom_pub.rsa_public_key   = denom_pub->rsa_public_key;
-  dki.issue = *issue;
-  ret = store_in_map (rfc->key_state->denomkey_map,
-                      &dki /* makes a deep copy of dki */);
-  if (GNUNET_SYSERR == ret)
-  {
-    GNUNET_break (0);
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Signature wrong on denomination key `%s' (skipping)!\n",
-                GNUNET_h2s (&issue->properties.denom_hash));
-    return;
-  }
-  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              "Loaded denomination key %s from DB, no private key (hopefully 
revoked!)\n",
-              GNUNET_h2s (&issue->properties.denom_hash));
-  /* we can assert here as we checked for duplicates just above */
-  GNUNET_assert (GNUNET_OK == ret);
-}
-
-
-/**
- * Actual "main" logic that builds the state which this module
- * evolves around.  This function will import the key data from
- * the exchangedb module and convert it into (1) internally used
- * lookup tables, and (2) HTTP responses to be returned from
- * /keys.
- *
- * State returned is to be freed with #ks_free() -- but only
- * once the reference counter has again hit zero.
- *
- * @return NULL on error (usually pretty fatal...)
- */
-static struct TEH_KS_StateHandle *
-make_fresh_key_state (struct GNUNET_TIME_Absolute now)
-{
-  struct TEH_KS_StateHandle *key_state;
-  struct ResponseFactoryContext rfc;
-  struct GNUNET_TIME_Absolute last;
-  unsigned int off;
-  enum GNUNET_DB_QueryStatus qs;
-
-  memset (&rfc,
-          0,
-          sizeof (rfc));
-  rfc.recoup_array = json_array ();
-  if (NULL == rfc.recoup_array)
-  {
-    GNUNET_break (0);
-    return NULL;
-  }
-  rfc.sign_keys_array = json_array ();
-  if (NULL == rfc.sign_keys_array)
-  {
-    GNUNET_break (0);
-    json_decref (rfc.recoup_array);
-    return NULL;
-  }
-
-  key_state = GNUNET_new (struct TEH_KS_StateHandle);
-  rfc.key_state = key_state;
-  rfc.now = now;
-  key_state->min_dk_expire = GNUNET_TIME_UNIT_FOREVER_ABS;
-  key_state->denomkey_map = GNUNET_CONTAINER_multihashmap_create (32,
-                                                                  GNUNET_NO);
-  key_state->revoked_map = GNUNET_CONTAINER_multihashmap_create (4,
-                                                                 GNUNET_NO);
-  key_state->reload_time = GNUNET_TIME_absolute_get ();
-  GNUNET_TIME_round_abs (&key_state->reload_time);
-
-  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              "Loading keys from `%s'\n",
-              TEH_exchange_directory);
-  /* Initialize the 'denomkey_map' and the 'revoked_map' and
-     'rfc.recoup_array' */
-  if (-1 ==
-      TALER_EXCHANGEDB_denomination_keys_iterate (TEH_exchange_directory,
-                                                  &reload_keys_denom_iter,
-                                                  &rfc))
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Failed to load denomination keys from `%s'.\n",
-                TEH_exchange_directory);
-    ks_free (key_state);
-    json_decref (rfc.recoup_array);
-    json_decref (rfc.sign_keys_array);
-    return NULL;
-  }
-
-  /* We do not get expired DKIs from
-     TALER_EXCHANGEDB_denomination_keys_iterate(), so we must fetch
-     the old keys (where we only have the public keys) from the
-     database! */
-  qs = TEH_plugin->iterate_denomination_info (TEH_plugin->cls,
-                                              &reload_public_denoms_cb,
-                                              &rfc);
-  GNUNET_break (0 <= qs); /* warn, but continue, fingers crossed */
-
-  /* process revocations */
-  if (-1 ==
-      TALER_EXCHANGEDB_revocations_iterate (TEH_revocation_directory,
-                                            &TEH_master_public_key,
-                                            &revocations_iter,
-                                            &rfc))
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Failed to load denomination keys from `%s'.\n",
-                TEH_exchange_directory);
-    ks_free (key_state);
-    json_decref (rfc.recoup_array);
-    json_decref (rfc.sign_keys_array);
-    return NULL;
-  }
-
-  /* Initialize `current_sign_key_issue` and `rfc.sign_keys_array` */
-  TALER_EXCHANGEDB_signing_keys_iterate (TEH_exchange_directory,
-                                         &reload_keys_sign_iter,
-                                         &rfc);
-  if (0 !=
-      memcmp (&key_state->current_sign_key_issue.issue.master_public_key,
-              &TEH_master_public_key,
-              sizeof (struct TALER_MasterPublicKeyP)))
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Have no signing key. Bad configuration.\n");
-    ks_free (key_state);
-    destroy_response_factory (&rfc);
-    return NULL;
-  }
-
-  /* sanity check */
-  if (0 == GNUNET_CONTAINER_multihashmap_size (key_state->denomkey_map))
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Have no denomination keys. Bad configuration.\n");
-    ks_free (key_state);
-    destroy_response_factory (&rfc);
-    return NULL;
-  }
-
-  /* Initialize and sort the `denomkey_array` */
-  rfc.denomkey_array
-    = GNUNET_new_array (GNUNET_CONTAINER_multihashmap_size (
-                          key_state->denomkey_map),
-                        struct DenominationKeyEntry);
-  GNUNET_CONTAINER_multihashmap_iterate (key_state->denomkey_map,
-                                         &initialize_denomkey_array,
-                                         &rfc);
-  GNUNET_assert (rfc.denomkey_array_length ==
-                 GNUNET_CONTAINER_multihashmap_size (key_state->denomkey_map));
-  qsort (rfc.denomkey_array,
-         rfc.denomkey_array_length,
-         sizeof (struct DenominationKeyEntry),
-         &denomkey_array_sort_comparator);
-
-  /* Complete `denomkey_array` by adding auditor signature data */
-  TALER_EXCHANGEDB_auditor_iterate (TEH_cfg,
-                                    &reload_auditor_iter,
-                                    &rfc);
-  /* Sanity check: do we have auditors for all denomination keys? */
-  for (unsigned int i = 0; i<rfc.denomkey_array_length; i++)
-  {
-    const struct DenominationKeyEntry *dke
-      = &rfc.denomkey_array[i];
-
-    if (NULL == dke->as_head)
-      GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
-                  "Denomination key `%s' at %p not signed by any auditor!\n",
-                  GNUNET_h2s (&dke->denom_key_hash),
-                  dke);
-  }
-
-  /* Determine size of `krd_array` by counting number of discrete
-     denomination key starting times. */
-  last = GNUNET_TIME_UNIT_ZERO_ABS;
-  key_state->krd_array_length = 0;
-  off = 1; /* reserve one slot for the "no keys" response */
-  for (unsigned int i = 0; i<rfc.denomkey_array_length; i++)
-  {
-    const struct DenominationKeyEntry *dke
-      = &rfc.denomkey_array[i];
-    struct GNUNET_TIME_Absolute d
-      = GNUNET_TIME_absolute_ntoh (dke->dki->issue.properties.start);
-
-    if (last.abs_value_us == d.abs_value_us)
-      continue;
-    /* must be monotonically increasing as per qsort() call above: */
-    GNUNET_assert (last.abs_value_us < d.abs_value_us);
-    last = d;
-    off++;
-  }
-
-  /* Compute next automatic reload time */
-  key_state->next_reload =
-    GNUNET_TIME_absolute_min (GNUNET_TIME_absolute_ntoh (
-                                
key_state->current_sign_key_issue.issue.expire),
-                              key_state->min_dk_expire);
-  GNUNET_assert (0 != key_state->next_reload.abs_value_us);
-
-
-  /* Initialize `krd_array` */
-  key_state->krd_array_length = off;
-  key_state->krd_array
-    = GNUNET_new_array (key_state->krd_array_length,
-                        struct KeysResponseData);
-  off = 0;
-  last = GNUNET_TIME_UNIT_ZERO_ABS;
-  for (unsigned int i = 0; i<rfc.denomkey_array_length; i++)
-  {
-    const struct DenominationKeyEntry *dke
-      = &rfc.denomkey_array[i];
-    struct GNUNET_TIME_Absolute d
-      = GNUNET_TIME_absolute_ntoh (dke->dki->issue.properties.start);
-
-    if (last.abs_value_us == d.abs_value_us)
-      continue;
-    if (GNUNET_OK !=
-        build_keys_response (&rfc,
-                             &key_state->krd_array[off++],
-                             i,
-                             last))
-    {
-      /* Fail hard, will be caught via test on `off` below */
-      GNUNET_break (0);
-      off = key_state->krd_array_length; /* flag as 'invalid' */
-      break;
-    }
-    last = d;
-  }
-
-  /* Finally, build an `empty` response without denomination keys
-     for requests past the last known denomination key start date */
-  if ( (off + 1 < key_state->krd_array_length) ||
-       (GNUNET_OK !=
-        build_keys_response (&rfc,
-                             &key_state->krd_array[off++],
-                             rfc.denomkey_array_length,
-                             last)) )
-  {
-    GNUNET_break (0);
-    ks_free (key_state);
-    destroy_response_factory (&rfc);
-    return NULL;
-  }
-
-  /* Clean up intermediary state we don't need anymore and return
-     new key_state! */
-  destroy_response_factory (&rfc);
-  return key_state;
-}
-
-
-/* ************************** Persistent part ********************** */
-
-/**
- * Release key state, free if necessary (if reference count gets to zero).
- *
- * @param location name of the function in which the lock is acquired
- * @param key_state the key state to release
- */
-void
-TEH_KS_release_ (const char *location,
-                 struct TEH_KS_StateHandle *key_state)
-{
-  int do_free;
-
-  GNUNET_assert (0 == pthread_mutex_lock (&internal_key_state_mutex));
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "KS released at %s (%p/%d)\n",
-              location,
-              key_state,
-              key_state->refcnt);
-  GNUNET_assert (0 < key_state->refcnt);
-  key_state->refcnt--;
-  do_free = (0 == key_state->refcnt);
-  GNUNET_assert ( (! do_free) ||
-                  (key_state != internal_key_state) );
-  GNUNET_assert (0 == pthread_mutex_unlock (&internal_key_state_mutex));
-  if (do_free)
-    ks_free (key_state);
-}
-
-
-/**
- * Acquire the key state of the exchange.  Updates keys if necessary.
- * For every call to #TEH_KS_acquire(), a matching call
- * to #TEH_KS_release() must be made.
- *
- * @param now for what timestamp should we acquire the key state
- * @param location name of the function in which the lock is acquired
- * @return the key state, NULL on error (usually pretty fatal)
- */
-struct TEH_KS_StateHandle *
-TEH_KS_acquire_ (struct GNUNET_TIME_Absolute now,
-                 const char *location)
-{
-  struct TEH_KS_StateHandle *key_state;
-  struct TEH_KS_StateHandle *os;
-
-  os = NULL;
-  GNUNET_assert (0 == pthread_mutex_lock (&internal_key_state_mutex));
-  /* If the current internal key state is missing (failed to load one on
-     startup?) or expired, we try to setup a fresh one even without having
-     gotten SIGUSR1 */
-  if ( ( (NULL != internal_key_state) &&
-         (internal_key_state->next_reload.abs_value_us <= now.abs_value_us) ) 
||
-       (NULL == internal_key_state) )
-  {
-    os = internal_key_state;
-    internal_key_state = make_fresh_key_state (now);
-    if (NULL != internal_key_state)
-      internal_key_state->refcnt = 1; /* alias from #internal_key_state */
-    if (NULL != os)
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  "KS released in acquire due to expiration\n");
-      GNUNET_assert (0 < os->refcnt);
-      os->refcnt--; /* #internal_key_state alias dropped */
-      if (0 != os->refcnt)
-        os = NULL; /* do NOT release yet, otherwise release after unlocking */
-    }
-  }
-  if (NULL == internal_key_state)
-  {
-    /* We tried and failed to setup #internal_key_state */
-    GNUNET_assert (0 == pthread_mutex_unlock (&internal_key_state_mutex));
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Failed to initialize key state\n");
-    if (NULL != os)
-      ks_free (os);
-    return NULL;
-  }
-  key_state = internal_key_state;
-  key_state->refcnt++; /* returning an alias, increment RC */
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "KS acquired at %s (%p/%d)\n",
-              location,
-              key_state,
-              key_state->refcnt);
-  GNUNET_assert (0 == pthread_mutex_unlock (&internal_key_state_mutex));
-  if (NULL != os)
-    ks_free (os);
-  return key_state;
-}
-
-
-/**
- * Look up the issue for a denom public key.  Note that the result
- * is only valid while the @a key_state is not released!
- *
- * @param key_state state to look in
- * @param denom_pub_hash hash of denomination public key
- * @param use purpose for which the key is being located
- * @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 denom_pub could not be found (or is not valid at this 
time for the given @a use)
- */
-struct TALER_EXCHANGEDB_DenominationKey *
-TEH_KS_denomination_key_lookup_by_hash (
-  const struct TEH_KS_StateHandle *key_state,
-  const struct GNUNET_HashCode *denom_pub_hash,
-  enum TEH_KS_DenominationKeyUse use,
-  enum TALER_ErrorCode *ec,
-  unsigned int *hc)
-{
-  struct TALER_EXCHANGEDB_DenominationKey *dki;
-  struct GNUNET_TIME_Absolute now;
-  const struct GNUNET_CONTAINER_MultiHashMap *map;
-
-  map = (TEH_KS_DKU_RECOUP == use)
-        ? key_state->revoked_map
-        : key_state->denomkey_map;
-  dki = GNUNET_CONTAINER_multihashmap_get (map,
-                                           denom_pub_hash);
-  if ( (NULL == dki) &&
-       (TEH_KS_DKU_ZOMBIE == use) )
-    dki = GNUNET_CONTAINER_multihashmap_get (key_state->revoked_map,
-                                             denom_pub_hash);
-  if (NULL == dki)
-  {
-    *hc = MHD_HTTP_NOT_FOUND;
-    *ec = TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN;
-    return NULL;
-  }
-  now = GNUNET_TIME_absolute_get ();
-  if (now.abs_value_us <
-      GNUNET_TIME_absolute_ntoh (dki->issue.properties.start).abs_value_us)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                "Not returning DKI for %s, as start time is in the future\n",
-                GNUNET_h2s (denom_pub_hash));
-    *hc = MHD_HTTP_PRECONDITION_FAILED;
-    *ec = TALER_EC_EXCHANGE_GENERIC_DENOMINATION_VALIDITY_IN_FUTURE;
-    return NULL;
-  }
-  now = GNUNET_TIME_absolute_get ();
-  switch (use)
-  {
-  case TEH_KS_DKU_WITHDRAW:
-    if (now.abs_value_us >
-        GNUNET_TIME_absolute_ntoh (
-          dki->issue.properties.expire_withdraw).abs_value_us)
-    {
-      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_GENERIC_DENOMINATION_EXPIRED;
-      *hc = MHD_HTTP_GONE;
-      return NULL;
-    }
-    if (NULL == dki->denom_priv.rsa_private_key)
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                  "Not returning DKI of %s for WITHDRAW operation as we lack 
the private key, even though the withdraw period did not yet expire!\n",
-                  GNUNET_h2s (denom_pub_hash));
-      *ec = TALER_EC_EXCHANGE_WITHDRAW_DENOMINATION_KEY_LOST;
-      *hc = MHD_HTTP_SERVICE_UNAVAILABLE;
-      return NULL;
-    }
-    break;
-  case TEH_KS_DKU_DEPOSIT:
-    if (now.abs_value_us >
-        GNUNET_TIME_absolute_ntoh (
-          dki->issue.properties.expire_deposit).abs_value_us)
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                  "Not returning DKI for %s, as time to spend coin has 
passed\n",
-                  GNUNET_h2s (denom_pub_hash));
-      *ec = TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED;
-      *hc = MHD_HTTP_GONE;
-      return NULL;
-    }
-    break;
-  case TEH_KS_DKU_RECOUP:
-    if (now.abs_value_us >
-        GNUNET_TIME_absolute_ntoh (
-          dki->issue.properties.expire_deposit).abs_value_us)
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                  "Not returning DKI for %s, as time to recoup coin has 
passed\n",
-                  GNUNET_h2s (denom_pub_hash));
-      *ec = TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED;
-      *hc = MHD_HTTP_GONE;
-      return NULL;
-    }
-    break;
-  case TEH_KS_DKU_ZOMBIE:
-    if (now.abs_value_us >
-        GNUNET_TIME_absolute_ntoh (
-          dki->issue.properties.expire_legal).abs_value_us)
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                  "Not returning DKI for %s, as legal expiration of coin has 
passed\n",
-                  GNUNET_h2s (denom_pub_hash));
-      *ec = TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED;
-      *hc = MHD_HTTP_GONE;
-      return NULL;
-    }
-    break;
-  }
-  return dki;
-}
-
-
-/**
- * Call #handle_signal() to pass the received signal via
- * the control pipe.
- */
-static void
-handle_sigusr1 (void)
-{
-  handle_signal (SIGUSR1);
-}
-
-
-/**
- * Call #handle_signal() to pass the received signal via
- * the control pipe.
- */
-static void
-handle_sigint (void)
-{
-  handle_signal (SIGINT);
-}
-
-
-/**
- * Call #handle_signal() to pass the received signal via
- * the control pipe.
- */
-static void
-handle_sigterm (void)
-{
-  handle_signal (SIGTERM);
-}
-
-
-/**
- * Call #handle_signal() to pass the received signal via
- * the control pipe.
- */
-static void
-handle_sighup (void)
-{
-  handle_signal (SIGHUP);
-}
-
-
-/**
- * Call #handle_signal() to pass the received signal via
- * the control pipe.
- */
-static void
-handle_sigchld (void)
-{
-  handle_signal (SIGCHLD);
-}
-
-
-/**
- * Read signals from a pipe in a loop, and reload keys from disk if
- * SIGUSR1 is received, terminate if SIGTERM/SIGINT is received, and
- * restart if SIGHUP is received.
- *
- * @return #GNUNET_SYSERR on errors,
- *         #GNUNET_OK to terminate normally
- *         #GNUNET_NO to restart an update version of the binary
- */
-int
-TEH_KS_loop (void)
-{
-  int ret;
-
-  ret = 2;
-  while (2 == ret)
-  {
-    char c;
-    ssize_t res;
-
-    errno = 0;
-    res = read (reload_pipe[0],
-                &c,
-                1);
-    if ((res < 0) && (EINTR != errno))
-    {
-      GNUNET_break (0);
-      ret = GNUNET_SYSERR;
-      break;
-    }
-    if (EINTR == errno)
-      continue;
-    switch (c)
-    {
-    case SIGUSR1:
-      {
-        struct TEH_KS_StateHandle *fs;
-        struct TEH_KS_StateHandle *os;
-
-        GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                    "(re-)loading keys\n");
-        /* Create fresh key state before critical region */
-        fs = make_fresh_key_state (GNUNET_TIME_absolute_get ());
-        if (NULL == fs)
-        {
-          /* Ok, that went badly, terminate process */
-          ret = GNUNET_SYSERR;
-          break;
-        }
-        fs->refcnt = 1; /* we'll alias from #internal_key_state soon */
-        /* swap active key state in critical region */
-        GNUNET_assert (0 == pthread_mutex_lock (&internal_key_state_mutex));
-        os = internal_key_state;
-        internal_key_state = fs;
-        if (NULL != os)
-        {
-          GNUNET_assert (0 < os->refcnt);
-          os->refcnt--; /* removed #internal_key_state reference */
-          if (0 != os->refcnt)
-            os = NULL; /* other aliases are still active, do not yet free */
-        }
-        GNUNET_assert (0 == pthread_mutex_unlock (&internal_key_state_mutex));
-        if (NULL != os)
-          ks_free (os); /* RC did hit zero, free */
-      }
-      break;
-    case SIGTERM:
-    case SIGINT:
-      /* terminate */
-      ret = GNUNET_OK;
-      break;
-    case SIGHUP:
-      /* restart updated binary */
-      ret = GNUNET_NO;
-      break;
-#if HAVE_DEVELOPER
-    case SIGCHLD:
-      /* running in test-mode, test finished, terminate */
-      ret = GNUNET_OK;
-      break;
-#endif
-    default:
-      /* unexpected character */
-      GNUNET_break (0);
-      break;
-    }
-  }
-  return ret;
-}
-
-
-static struct GNUNET_SIGNAL_Context *sigusr1;
-static struct GNUNET_SIGNAL_Context *sigterm;
-static struct GNUNET_SIGNAL_Context *sigint;
-static struct GNUNET_SIGNAL_Context *sighup;
-static struct GNUNET_SIGNAL_Context *sigchld;
-
-
-/**
- * Setup initial #internal_key_state and our signal handlers.
- */
-int
-TEH_KS_init (void)
-{
-  if (GNUNET_OK !=
-      GNUNET_CONFIGURATION_get_value_time (TEH_cfg,
-                                           "exchange",
-                                           "LOOKAHEAD_PROVIDE",
-                                           &conf_duration_provide))
-  {
-    GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
-                               "exchange",
-                               "LOOKAHEAD_PROVIDE",
-                               "time value required");
-    return GNUNET_SYSERR;
-  }
-  if (0 == conf_duration_provide.rel_value_us)
-  {
-    GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
-                               "exchange",
-                               "LOOKAHEAD_PROVIDE",
-                               "value cannot be zero");
-    return GNUNET_SYSERR;
-  }
-  if (0 != pipe (reload_pipe))
-  {
-    GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
-                         "pipe");
-    return GNUNET_SYSERR;
-  }
-  sigusr1 = GNUNET_SIGNAL_handler_install (SIGUSR1,
-                                           &handle_sigusr1);
-  sigterm = GNUNET_SIGNAL_handler_install (SIGTERM,
-                                           &handle_sigterm);
-  sigint = GNUNET_SIGNAL_handler_install (SIGINT,
-                                          &handle_sigint);
-  sighup = GNUNET_SIGNAL_handler_install (SIGHUP,
-                                          &handle_sighup);
-  sigchld = GNUNET_SIGNAL_handler_install (SIGCHLD,
-                                           &handle_sigchld);
-  /* no need to lock here, as we are still single-threaded */
-  internal_key_state = make_fresh_key_state (GNUNET_TIME_absolute_get ());
-  if (NULL == internal_key_state)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Failed to setup initial key state. This exchange cannot 
work.\n");
-    return GNUNET_SYSERR;
-  }
-  internal_key_state->refcnt = 1;
-  return GNUNET_OK;
-}
-
-
-/**
- * Finally release #internal_key_state and our signal handlers.
- */
-void
-TEH_KS_free (void)
-{
-  struct TEH_KS_StateHandle *ks;
-
-  /* Note: locking is no longer be required, as we are again
-     single-threaded. */
-  ks = internal_key_state;
-  if (NULL != ks)
-  {
-    GNUNET_assert (1 == ks->refcnt);
-    ks->refcnt--;
-    ks_free (ks);
-  }
-  if (NULL != sigusr1)
-  {
-    GNUNET_SIGNAL_handler_uninstall (sigusr1);
-    sigusr1 = NULL;
-  }
-  if (NULL != sigterm)
-  {
-    GNUNET_SIGNAL_handler_uninstall (sigterm);
-    sigterm = NULL;
-  }
-  if (NULL != sigint)
-  {
-    GNUNET_SIGNAL_handler_uninstall (sigint);
-    sigint = NULL;
-  }
-  if (NULL != sighup)
-  {
-    GNUNET_SIGNAL_handler_uninstall (sighup);
-    sighup = NULL;
-  }
-  if (NULL != sigchld)
-  {
-    GNUNET_SIGNAL_handler_uninstall (sigchld);
-    sigchld = NULL;
-  }
-  if (-1 != reload_pipe[0])
-  {
-    GNUNET_break (0 == close (reload_pipe[0]));
-    GNUNET_break (0 == close (reload_pipe[1]));
-    reload_pipe[0] = reload_pipe[1] = -1;
-  }
-}
-
-
-/**
- * Sign the message in @a purpose with the exchange's signing key.
- *
- * The @a purpose data is the beginning of the data of which the signature is
- * to be created. The `size` field in @a purpose must correctly indicate the
- * number of bytes of the data structure, including its header.  Use
- * #TEH_KS_sign() instead of calling this function directly!
- *
- * @param purpose the message to sign
- * @param[out] pub set to the current public signing key of the exchange
- * @param[out] sig signature over purpose using current signing key
- * @return #GNUNET_OK on success, #GNUNET_SYSERR if we lack key material
- */
-int
-TEH_KS_sign_ (const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose,
-              struct TALER_ExchangePublicKeyP *pub,
-              struct TALER_ExchangeSignatureP *sig)
-{
-  struct TEH_KS_StateHandle *key_state;
-
-  key_state = TEH_KS_acquire (GNUNET_TIME_absolute_get ());
-  if (NULL == key_state)
-  {
-    /* This *can* happen if the exchange's keys are not properly maintained
-       (i.e. administrator forgets to provision us with non-expired signing
-       keys or to send signal to reload keys after provisioning). */
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Cannot sign request, no valid signing keys available. Were 
they properly provisioned and did you signal the exchange to reload the 
keys?\n");
-    return GNUNET_SYSERR;
-  }
-  *pub = key_state->current_sign_key_issue.issue.signkey_pub;
-  GNUNET_assert (GNUNET_OK ==
-                 GNUNET_CRYPTO_eddsa_sign_ (
-                   &key_state->current_sign_key_issue.signkey_priv.eddsa_priv,
-                   purpose,
-                   &sig->eddsa_signature));
-  TEH_KS_release (key_state);
-  return GNUNET_OK;
-}
-
-
-/**
- * Comparator used for a binary search by cherry_pick_date for @a key in the
- * `struct KeysResponseData` array. See libc's qsort() and bsearch() functions.
- *
- * @param key pointer to a `struct GNUNET_TIME_Absolute`
- * @param value pointer to a `struct KeysResponseData` array entry
- * @return 0 if time matches, -1 if key is smaller, 1 if key is larger
- */
-static int
-krd_search_comparator (const void *key,
-                       const void *value)
-{
-  const struct GNUNET_TIME_Absolute *kd = key;
-  const struct KeysResponseData *krd = value;
-
-  if (kd->abs_value_us > krd->cherry_pick_date.abs_value_us)
-    return 1;
-  if (kd->abs_value_us < krd->cherry_pick_date.abs_value_us)
-    return -1;
-  return 0;
-}
-
-
-/**
- * Function to call to handle the request by sending
- * back static data from the @a rh.
- *
- * @param rh context of the handler
- * @param connection the MHD connection to handle
- * @param args array of additional options (must be empty for this function)
- * @return MHD result code
- */
-MHD_RESULT
-TEH_handler_keys (const struct TEH_RequestHandler *rh,
-                  struct MHD_Connection *connection,
-                  const char *const args[])
-{
-  MHD_RESULT ret;
-  const char *have_cherrypick;
-  const char *have_fakenow;
-  struct GNUNET_TIME_Absolute last_issue_date;
-  struct GNUNET_TIME_Absolute now;
-  const struct KeysResponseData *krd;
-
-  (void) rh;
-  (void) args;
-  have_cherrypick = MHD_lookup_connection_value (connection,
-                                                 MHD_GET_ARGUMENT_KIND,
-                                                 "last_issue_date");
-  if (NULL != have_cherrypick)
-  {
-    unsigned long long cherrypickn;
-
-    if (1 !=
-        sscanf (have_cherrypick,
-                "%llu",
-                &cherrypickn))
-    {
-      GNUNET_break_op (0);
-      return TALER_MHD_reply_with_error (connection,
-                                         MHD_HTTP_BAD_REQUEST,
-                                         TALER_EC_GENERIC_PARAMETER_MALFORMED,
-                                         have_cherrypick);
-    }
-    /* The following multiplication may overflow; but this should not really
-       be a problem, as giving back 'older' data than what the client asks for
-       (given that the client asks for data in the distant future) is not
-       problematic */
-    last_issue_date.abs_value_us = (uint64_t) cherrypickn * 1000000LLU;
-  }
-  else
-  {
-    last_issue_date.abs_value_us = 0LLU;
-  }
-  now = GNUNET_TIME_absolute_get ();
-  have_fakenow = MHD_lookup_connection_value (connection,
-                                              MHD_GET_ARGUMENT_KIND,
-                                              "now");
-  if (NULL != have_fakenow)
-  {
-    unsigned long long fakenown;
-
-    if (1 !=
-        sscanf (have_fakenow,
-                "%llu",
-                &fakenown))
-    {
-      GNUNET_break_op (0);
-      return TALER_MHD_reply_with_error (connection,
-                                         MHD_HTTP_FORBIDDEN,
-                                         TALER_EC_GENERIC_PARAMETER_MALFORMED,
-                                         have_fakenow);
-    }
-    if (TEH_allow_keys_timetravel)
-    {
-      /* The following multiplication may overflow; but this should not really
-         be a problem, as giving back 'older' data than what the client asks 
for
-         (given that the client asks for data in the distant future) is not
-         problematic */
-      now.abs_value_us = (uint64_t) fakenown * 1000000LLU;
-    }
-    else
-    {
-      /* Option not allowed by configuration */
-      return TALER_MHD_reply_with_error (connection,
-                                         MHD_HTTP_FORBIDDEN,
-                                         
TALER_EC_EXCHANGE_KEYS_TIMETRAVEL_FORBIDDEN,
-                                         NULL);
-    }
-  }
-  GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-              "Handling request for /keys (%s/%s)\n",
-              have_cherrypick,
-              have_fakenow);
-  {
-    struct TEH_KS_StateHandle *key_state;
-
-    key_state = TEH_KS_acquire (now);
-    if (NULL == key_state)
-    {
-      /* Maybe client picked time stamp too far in the future?  In that case,
-         #MHD_HTTP_INTERNAL_SERVER_ERROR might be misleading, could be more 
like a
-         NOT_FOUND situation. But, OTOH, for 'sane' clients it is more likely
-         to be our fault, so let's speculatively assume we are to blame ;-) */
-      return TALER_MHD_reply_with_error (connection,
-                                         MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                         
TALER_EC_EXCHANGE_GENERIC_BAD_CONFIGURATION,
-                                         NULL);
-    }
-    krd = bsearch (&last_issue_date,
-                   key_state->krd_array,
-                   key_state->krd_array_length,
-                   sizeof (struct KeysResponseData),
-                   &krd_search_comparator);
-
-    GNUNET_log (GNUNET_ERROR_TYPE_INFO,
-                "Filtering /keys by cherry pick date %s found entry %u/%u\n",
-                GNUNET_STRINGS_absolute_time_to_string (last_issue_date),
-                (unsigned int) (krd - key_state->krd_array),
-                key_state->krd_array_length);
-    if ( (NULL == krd) &&
-         (key_state->krd_array_length > 0) )
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  "Client provided invalid cherry picking timestamp %s, 
returning full response\n",
-                  GNUNET_STRINGS_absolute_time_to_string (last_issue_date));
-      krd = &key_state->krd_array[0];
-    }
-    if (NULL == krd)
-    {
-      /* Maybe client picked time stamp too far in the future?  In that case,
-         "INTERNAL_SERVER_ERROR" might be misleading, could be more like a
-         NOT_FOUND situation. But, OTOH, for 'sane' clients it is more likely
-         to be our fault, so let's speculatively assume we are to blame ;-) 
*///
-      GNUNET_break (0);
-      TEH_KS_release (key_state);
-      return TALER_MHD_reply_with_error (connection,
-                                         MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                         
TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING,
-                                         NULL);
-    }
-    ret = MHD_queue_response (connection,
-                              MHD_HTTP_OK,
-                              (MHD_YES == TALER_MHD_can_compress (connection))
-                              ? krd->response_compressed
-                              : krd->response_uncompressed);
-    TEH_KS_release (key_state);
-  }
-  return ret;
-}
-
-
-/* end of taler-exchange-httpd_keystate.c */
diff --git a/src/exchange/taler-exchange-httpd_keystate.h 
b/src/exchange/taler-exchange-httpd_keystate.h
deleted file mode 100644
index 86bdc59b..00000000
--- a/src/exchange/taler-exchange-httpd_keystate.h
+++ /dev/null
@@ -1,229 +0,0 @@
-/*
-  This file is part of TALER
-  Copyright (C) 2014, 2015 Taler Systems SA
-
-  TALER is free software; you can redistribute it and/or modify it under the
-  terms of the GNU Affero General Public License as published by the Free 
Software
-  Foundation; either version 3, or (at your option) any later version.
-
-  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
-  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
-  A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more 
details.
-
-  You should have received a copy of the GNU Affero General Public License 
along with
-  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
-*/
-/**
- * @file exchange/taler-exchange-httpd_keystate.h
- * @brief management of our private signing keys (denomination keys)
- * @author Florian Dold
- * @author Benedikt Mueller
- * @author Christian Grothoff
- */
-#ifndef TALER_EXCHANGE_HTTPD_KEYSTATE_H
-#define TALER_EXCHANGE_HTTPD_KEYSTATE_H
-
-#include <gnunet/gnunet_util_lib.h>
-#include <microhttpd.h>
-#include "taler-exchange-httpd.h"
-#include "taler_error_codes.h"
-#include "taler_exchangedb_lib.h"
-
-
-/**
- * Snapshot of the (coin and signing)
- * keys (including private keys) of the exchange.
- */
-struct TEH_KS_StateHandle;
-
-
-/**
- * Acquire the key state of the exchange.  Updates keys if necessary.
- * For every call to #TEH_KS_acquire(), a matching call
- * to #TEH_KS_release() must be made.
- *
- * @param now for what timestamp should we acquire the key state
- * @param location name of the function in which the lock is acquired
- * @return the key state, NULL on error (usually pretty fatal)
- */
-struct TEH_KS_StateHandle *
-TEH_KS_acquire_ (struct GNUNET_TIME_Absolute now,
-                 const char *location);
-
-
-/**
- * Release key state, free if necessary (if reference count gets to zero).
- *
- * @param location name of the function in which the lock is acquired
- * @param key_state the key state to release
- */
-void
-TEH_KS_release_ (const char *location,
-                 struct TEH_KS_StateHandle *key_state);
-
-
-/**
- * Acquire the key state of the exchange.  Updates keys if necessary.
- * For every call to #TEH_KS_acquire(), a matching call
- * to #TEH_KS_release() must be made.
- *
- * @param now current time snapshot; either true, or given by the
- *        client via the "now" URL parameter of "/keys".
- * @return the key state
- */
-#define TEH_KS_acquire(now) TEH_KS_acquire_ (now, __FUNCTION__)
-
-
-/**
- * Release key state, free if necessary (if reference count gets to zero).
- *
- * @param key_state the key state to release
- */
-#define TEH_KS_release(key_state) TEH_KS_release_ (__FUNCTION__, key_state)
-
-
-/**
- * Setup initial #internal_key_state and our signal handlers.
- *
- * @return #GNUNET_OK on success
- */
-int
-TEH_KS_init (void);
-
-
-/**
- * Finally, release #internal_key_state and our signal handlers.
- */
-void
-TEH_KS_free (void);
-
-
-/**
- * Denomination key lookups can be for signing of fresh coins
- * or to validate signatures on existing coins.  As the validity
- * periods for a key differ, the caller must specify which
- * use is relevant for the current operation.
- */
-enum TEH_KS_DenominationKeyUse
-{
-
-  /**
-   * The denomination key is to be used for a withdraw or reveal operation.
-   */
-  TEH_KS_DKU_WITHDRAW,
-
-  /**
-   * The denomination key is to be used for a deposit or melt operation.
-   */
-  TEH_KS_DKU_DEPOSIT,
-
-  /**
-   * The denomination key is to be used for a recoup operation, or to
-   * melt a coin that was deposited (or melted) before the revocation.
-   */
-  TEH_KS_DKU_RECOUP,
-
-  /**
-   * The key is to be used for a refresh + recoup operation,
-   * i.e. it is an old coin that regained value from a
-   * recoup on a new coin derived from the old coin.
-   */
-  TEH_KS_DKU_ZOMBIE
-
-};
-
-
-/**
- * Look up the issue for a denom public key.  Note that the result
- * is only valid while the @a key_state is not released!
- *
- * @param key_state state to look in
- * @param denom_pub_hash hash of denomination public key
- * @param use purpose for which the key is being located
- * @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 denom_pub could not be found (or is not valid at this 
time for the given @a use)
- */
-struct TALER_EXCHANGEDB_DenominationKey *
-TEH_KS_denomination_key_lookup_by_hash (
-  const struct TEH_KS_StateHandle *key_state,
-  const struct GNUNET_HashCode *denom_pub_hash,
-  enum TEH_KS_DenominationKeyUse use,
-  enum TALER_ErrorCode *ec,
-  unsigned int *hc);
-
-
-/**
- * Read signals from a pipe in a loop, and reload keys from disk if
- * SIGUSR1 is received, terminate if SIGTERM/SIGINT is received, and
- * restart if SIGHUP is received.
- *
- * @return #GNUNET_SYSERR on errors,
- *         #GNUNET_OK to terminate normally
- *         #GNUNET_NO to restart an update version of the binary
- */
-int
-TEH_KS_loop (void);
-
-
-/**
- * Sign the message in @a purpose with the exchange's signing
- * key.
- *
- * The @a purpose data is the beginning of the data of which the signature is
- * to be created. The `size` field in @a purpose must correctly indicate the
- * number of bytes of the data structure, including its header.  Use
- * #TEH_KS_sign() instead of calling this function directly!
- *
- * @param purpose the message to sign
- * @param[out] pub set to the current public signing key of the exchange
- * @param[out] sig signature over purpose using current signing key
- * @return #GNUNET_OK on success, #GNUNET_SYSERR if we lack key material
- */
-int
-TEH_KS_sign_ (const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose,
-              struct TALER_ExchangePublicKeyP *pub,
-              struct TALER_ExchangeSignatureP *sig);
-
-/**
- * @ingroup crypto
- * @brief EdDSA sign a given block.
- *
- * The @a ps data must be a fixed-size struct for which the signature is to be
- * created. The `size` field in @a ps->purpose must correctly indicate the
- * number of bytes of the data structure, including its header.
- *
- * @param ps packed struct with what to sign, MUST begin with a purpose
- * @param[out] pub where to store the public key to use for the signing
- * @param[out] sig where to write the signature
- */
-#define TEH_KS_sign(ps,pub,sig) \
-  ({                                                  \
-    /* check size is set correctly */                 \
-    GNUNET_assert (htonl ((ps)->purpose.size) ==      \
-                   sizeof (*ps));                     \
-    /* check 'ps' begins with the purpose */          \
-    GNUNET_static_assert (((void*) (ps)) ==           \
-                          ((void*) &(ps)->purpose));  \
-    TEH_KS_sign_ (&(ps)->purpose,                     \
-                  pub,                                \
-                  sig);                               \
-  })
-
-
-/**
- * Handle a "/keys" request
- *
- * @param rh context of the handler
- * @param connection the MHD connection to handle
- * @param args array of additional options (must be empty for this function)
- * @return MHD result code
- */
-MHD_RESULT
-TEH_handler_keys (const struct TEH_RequestHandler *rh,
-                  struct MHD_Connection *connection,
-                  const char *const args[]);
-
-
-#endif
diff --git a/src/exchange/taler-exchange-httpd_link.c 
b/src/exchange/taler-exchange-httpd_link.c
index 1ad5eff0..3edb25b2 100644
--- a/src/exchange/taler-exchange-httpd_link.c
+++ b/src/exchange/taler-exchange-httpd_link.c
@@ -28,7 +28,6 @@
 #include "taler-exchange-httpd_mhd.h"
 #include "taler-exchange-httpd_link.h"
 #include "taler-exchange-httpd_responses.h"
-#include "taler-exchange-httpd_keystate.h"
 
 
 /**
diff --git a/src/exchange/taler-exchange-httpd_loop.c 
b/src/exchange/taler-exchange-httpd_loop.c
new file mode 100644
index 00000000..086fbc87
--- /dev/null
+++ b/src/exchange/taler-exchange-httpd_loop.c
@@ -0,0 +1,209 @@
+/*
+  This file is part of TALER
+  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 Affero General Public License as published by the Free 
Software
+  Foundation; either version 3, or (at your option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+  A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more 
details.
+
+  You should have received a copy of the GNU Affero General Public License 
along with
+  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file taler-exchange-httpd_loop.c
+ * @brief management of our main loop
+ * @author Florian Dold
+ * @author Benedikt Mueller
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <pthread.h>
+#include "taler-exchange-httpd_loop.h"
+
+
+/* ************************* Signal logic ************************** */
+
+/**
+ * Pipe used for signaling reloading of our key state.
+ */
+static int reload_pipe[2] = { -1, -1 };
+
+
+/**
+ * Handle a signal, writing relevant signal numbers to the pipe.
+ *
+ * @param signal_number the signal number
+ */
+static void
+handle_signal (int signal_number)
+{
+  char c = (char) signal_number; /* never seen a signal_number > 127 on any 
platform */
+
+  (void) ! write (reload_pipe[1],
+                  &c,
+                  1);
+  /* While one might like to "handle errors" here, even logging via fprintf()
+     isn't safe inside of a signal handler. So there is nothing we safely CAN
+     do. OTOH, also very little that can go wrong in practice. Calling _exit()
+     on errors might be a possibility, but that might do more harm than good. 
*///
+}
+
+
+/**
+ * Call #handle_signal() to pass the received signal via
+ * the control pipe.
+ */
+static void
+handle_sigint (void)
+{
+  handle_signal (SIGINT);
+}
+
+
+/**
+ * Call #handle_signal() to pass the received signal via
+ * the control pipe.
+ */
+static void
+handle_sigterm (void)
+{
+  handle_signal (SIGTERM);
+}
+
+
+/**
+ * Call #handle_signal() to pass the received signal via
+ * the control pipe.
+ */
+static void
+handle_sighup (void)
+{
+  handle_signal (SIGHUP);
+}
+
+
+/**
+ * Call #handle_signal() to pass the received signal via
+ * the control pipe.
+ */
+static void
+handle_sigchld (void)
+{
+  handle_signal (SIGCHLD);
+}
+
+
+int
+TEH_loop_run (void)
+{
+  int ret;
+
+  ret = 2;
+  while (2 == ret)
+  {
+    char c;
+    ssize_t res;
+
+    errno = 0;
+    res = read (reload_pipe[0],
+                &c,
+                1);
+    if ((res < 0) && (EINTR != errno))
+    {
+      GNUNET_break (0);
+      ret = GNUNET_SYSERR;
+      break;
+    }
+    if (EINTR == errno)
+      continue;
+    switch (c)
+    {
+    case SIGTERM:
+    case SIGINT:
+      /* terminate */
+      ret = GNUNET_OK;
+      break;
+    case SIGHUP:
+      /* restart updated binary */
+      ret = GNUNET_NO;
+      break;
+#if HAVE_DEVELOPER
+    case SIGCHLD:
+      /* running in test-mode, test finished, terminate */
+      ret = GNUNET_OK;
+      break;
+#endif
+    default:
+      /* unexpected character */
+      GNUNET_break (0);
+      break;
+    }
+  }
+  return ret;
+}
+
+
+static struct GNUNET_SIGNAL_Context *sigterm;
+static struct GNUNET_SIGNAL_Context *sigint;
+static struct GNUNET_SIGNAL_Context *sighup;
+static struct GNUNET_SIGNAL_Context *sigchld;
+
+
+int
+TEH_loop_init (void)
+{
+  if (0 != pipe (reload_pipe))
+  {
+    GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
+                         "pipe");
+    return GNUNET_SYSERR;
+  }
+  sigterm = GNUNET_SIGNAL_handler_install (SIGTERM,
+                                           &handle_sigterm);
+  sigint = GNUNET_SIGNAL_handler_install (SIGINT,
+                                          &handle_sigint);
+  sighup = GNUNET_SIGNAL_handler_install (SIGHUP,
+                                          &handle_sighup);
+  sigchld = GNUNET_SIGNAL_handler_install (SIGCHLD,
+                                           &handle_sigchld);
+  return GNUNET_OK;
+}
+
+
+void
+TEH_loop_done (void)
+{
+  if (NULL != sigterm)
+  {
+    GNUNET_SIGNAL_handler_uninstall (sigterm);
+    sigterm = NULL;
+  }
+  if (NULL != sigint)
+  {
+    GNUNET_SIGNAL_handler_uninstall (sigint);
+    sigint = NULL;
+  }
+  if (NULL != sighup)
+  {
+    GNUNET_SIGNAL_handler_uninstall (sighup);
+    sighup = NULL;
+  }
+  if (NULL != sigchld)
+  {
+    GNUNET_SIGNAL_handler_uninstall (sigchld);
+    sigchld = NULL;
+  }
+  if (-1 != reload_pipe[0])
+  {
+    GNUNET_break (0 == close (reload_pipe[0]));
+    GNUNET_break (0 == close (reload_pipe[1]));
+    reload_pipe[0] = reload_pipe[1] = -1;
+  }
+}
+
+
+/* end of taler-exchange-httpd_loop.c */
diff --git a/src/exchange/taler-exchange-httpd_wire.h 
b/src/exchange/taler-exchange-httpd_loop.h
similarity index 50%
copy from src/exchange/taler-exchange-httpd_wire.h
copy to src/exchange/taler-exchange-httpd_loop.h
index 7df87432..700f5677 100644
--- a/src/exchange/taler-exchange-httpd_wire.h
+++ b/src/exchange/taler-exchange-httpd_loop.h
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  Copyright (C) 2014--2020 Taler Systems SA
+  Copyright (C) 2014, 2015 Taler Systems SA
 
   TALER is free software; you can redistribute it and/or modify it under the
   terms of the GNU Affero General Public License as published by the Free 
Software
@@ -14,12 +14,14 @@
   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
 */
 /**
- * @file taler-exchange-httpd_wire.h
- * @brief Handle /wire requests
+ * @file exchange/taler-exchange-httpd_loop.h
+ * @brief management of our main loop
+ * @author Florian Dold
+ * @author Benedikt Mueller
  * @author Christian Grothoff
  */
-#ifndef TALER_EXCHANGE_HTTPD_WIRE_H
-#define TALER_EXCHANGE_HTTPD_WIRE_H
+#ifndef TALER_EXCHANGE_HTTPD_LOOP_H
+#define TALER_EXCHANGE_HTTPD_LOOP_H
 
 #include <gnunet/gnunet_util_lib.h>
 #include <microhttpd.h>
@@ -27,35 +29,32 @@
 
 
 /**
- * Initialize wire subsystem.
+ * Setup our signal handlers.
  *
- * @param cfg configuration to use
- * @return #GNUNET_OK on success, #GNUNET_SYSERR if we found no valid
- *         wire methods
+ * @return #GNUNET_OK on success
  */
 int
-TEH_WIRE_init (const struct GNUNET_CONFIGURATION_Handle *cfg);
+TEH_loop_init (void);
 
 
 /**
- * Clean up wire subsystem.
+ * Finally, tear down our signal handlers.
  */
 void
-TEH_WIRE_done (void);
+TEH_loop_done (void);
 
 
 /**
- * Handle a "/wire" request.
+ * Read signals from a pipe in a loop, and reload keys from disk if
+ * SIGUSR1 is received, terminate if SIGTERM/SIGINT is received, and
+ * restart if SIGHUP is received.
  *
- * @param rh context of the handler
- * @param connection the MHD connection to handle
- * @param args array of additional options (must be empty for this function)
- * @return MHD result code
+ * @return #GNUNET_SYSERR on errors,
+ *         #GNUNET_OK to terminate normally
+ *         #GNUNET_NO to restart an update version of the binary
  */
-MHD_RESULT
-TEH_handler_wire (const struct TEH_RequestHandler *rh,
-                  struct MHD_Connection *connection,
-                  const char *const args[]);
+int
+TEH_loop_run (void);
 
 
 #endif
diff --git a/src/exchange/taler-exchange-httpd_management_auditors.c 
b/src/exchange/taler-exchange-httpd_management_auditors.c
index 1a2494da..acb8f2c5 100644
--- a/src/exchange/taler-exchange-httpd_management_auditors.c
+++ b/src/exchange/taler-exchange-httpd_management_auditors.c
@@ -28,7 +28,7 @@
 #include "taler_mhd_lib.h"
 #include "taler-exchange-httpd_management.h"
 #include "taler-exchange-httpd_responses.h"
-#include "taler-exchange-httpd_keystate.h"
+
 
 /**
  * Closure for the #add_auditor transaction.
diff --git a/src/exchange/taler-exchange-httpd_management_auditors_AP_disable.c 
b/src/exchange/taler-exchange-httpd_management_auditors_AP_disable.c
index e360c1a5..222af60e 100644
--- a/src/exchange/taler-exchange-httpd_management_auditors_AP_disable.c
+++ b/src/exchange/taler-exchange-httpd_management_auditors_AP_disable.c
@@ -28,7 +28,7 @@
 #include "taler_mhd_lib.h"
 #include "taler-exchange-httpd_management.h"
 #include "taler-exchange-httpd_responses.h"
-#include "taler-exchange-httpd_keystate.h"
+
 
 /**
  * Closure for the #del_auditor transaction.
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 8fb5b083..23e6cfb4 100644
--- a/src/exchange/taler-exchange-httpd_management_denominations_HDP_revoke.c
+++ b/src/exchange/taler-exchange-httpd_management_denominations_HDP_revoke.c
@@ -28,7 +28,6 @@
 #include "taler_mhd_lib.h"
 #include "taler-exchange-httpd_management.h"
 #include "taler-exchange-httpd_responses.h"
-#include "taler-exchange-httpd_keystate.h"
 #include "taler-exchange-httpd_keys.h"
 
 
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 3a84296c..273c05d5 100644
--- a/src/exchange/taler-exchange-httpd_management_signkey_EP_revoke.c
+++ b/src/exchange/taler-exchange-httpd_management_signkey_EP_revoke.c
@@ -28,7 +28,6 @@
 #include "taler_mhd_lib.h"
 #include "taler-exchange-httpd_management.h"
 #include "taler-exchange-httpd_responses.h"
-#include "taler-exchange-httpd_keystate.h"
 #include "taler-exchange-httpd_keys.h"
 
 
diff --git a/src/exchange/taler-exchange-httpd_management_wire.c 
b/src/exchange/taler-exchange-httpd_management_wire.c
index c462bfc3..5454125f 100644
--- a/src/exchange/taler-exchange-httpd_management_wire.c
+++ b/src/exchange/taler-exchange-httpd_management_wire.c
@@ -29,6 +29,7 @@
 #include "taler_signatures.h"
 #include "taler-exchange-httpd_management.h"
 #include "taler-exchange-httpd_responses.h"
+#include "taler-exchange-httpd_wire.h"
 
 
 /**
@@ -202,6 +203,7 @@ TEH_handler_management_denominations_wire (
                                &awc);
   if (qs < 0)
     return ret;
+  TEH_wire_update_state ();
   return TALER_MHD_reply_static (
     connection,
     MHD_HTTP_NO_CONTENT,
diff --git a/src/exchange/taler-exchange-httpd_management_wire_disable.c 
b/src/exchange/taler-exchange-httpd_management_wire_disable.c
index 51b81160..67cf3015 100644
--- a/src/exchange/taler-exchange-httpd_management_wire_disable.c
+++ b/src/exchange/taler-exchange-httpd_management_wire_disable.c
@@ -28,7 +28,8 @@
 #include "taler_mhd_lib.h"
 #include "taler-exchange-httpd_management.h"
 #include "taler-exchange-httpd_responses.h"
-#include "taler-exchange-httpd_keystate.h"
+#include "taler-exchange-httpd_wire.h"
+
 
 /**
  * Closure for the #del_wire transaction.
@@ -182,6 +183,7 @@ TEH_handler_management_denominations_wire_disable (
                                &awc);
   if (qs < 0)
     return ret;
+  TEH_wire_update_state ();
   return TALER_MHD_reply_static (
     connection,
     MHD_HTTP_NO_CONTENT,
diff --git a/src/exchange/taler-exchange-httpd_management_wire_fees.c 
b/src/exchange/taler-exchange-httpd_management_wire_fees.c
index 9878821c..0a011bc0 100644
--- a/src/exchange/taler-exchange-httpd_management_wire_fees.c
+++ b/src/exchange/taler-exchange-httpd_management_wire_fees.c
@@ -29,6 +29,7 @@
 #include "taler_signatures.h"
 #include "taler-exchange-httpd_management.h"
 #include "taler-exchange-httpd_responses.h"
+#include "taler-exchange-httpd_wire.h"
 
 
 /**
@@ -247,6 +248,7 @@ TEH_handler_management_post_wire_fees (
                                &afc);
   if (qs < 0)
     return ret;
+  TEH_wire_update_state ();
   return TALER_MHD_reply_static (
     connection,
     MHD_HTTP_NO_CONTENT,
diff --git a/src/exchange/taler-exchange-httpd_melt.c 
b/src/exchange/taler-exchange-httpd_melt.c
index 76cf52eb..143e3f3a 100644
--- a/src/exchange/taler-exchange-httpd_melt.c
+++ b/src/exchange/taler-exchange-httpd_melt.c
@@ -29,8 +29,8 @@
 #include "taler-exchange-httpd_mhd.h"
 #include "taler-exchange-httpd_melt.h"
 #include "taler-exchange-httpd_responses.h"
-#include "taler-exchange-httpd_keystate.h"
 #include "taler-exchange-httpd_keys.h"
+#include "taler_exchangedb_lib.h"
 
 
 /**
@@ -463,136 +463,114 @@ static MHD_RESULT
 check_for_denomination_key (struct MHD_Connection *connection,
                             struct MeltContext *rmc)
 {
-  struct TEH_KS_StateHandle *key_state;
+  /* Baseline: check if deposits/refreshs are generally
+     simply still allowed for this denomination */
+  struct TEH_DenominationKey *dk;
+  unsigned int hc;
+  enum TALER_ErrorCode ec;
+  struct GNUNET_TIME_Absolute now;
 
-  key_state = TEH_KS_acquire (GNUNET_TIME_absolute_get ());
-  if (NULL == key_state)
+  dk = TEH_keys_denomination_by_hash (
+    &rmc->refresh_session.coin.denom_pub_hash,
+    &ec,
+    &hc);
+  if (NULL == dk)
   {
-    TALER_LOG_ERROR ("Lacking keys to operate\n");
-    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_error (
+      connection,
+      MHD_HTTP_NOT_FOUND,
+      TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN,
+      NULL);
   }
-
+  now = GNUNET_TIME_absolute_get ();
+  if (now.abs_value_us >= dk->meta.expire_legal.abs_value_us)
   {
-    /* Baseline: check if deposits/refreshs are generally
-       simply still allowed for this denomination */
-    struct TEH_DenominationKey *dk;
-    unsigned int hc;
-    enum TALER_ErrorCode ec;
-    struct GNUNET_TIME_Absolute now;
-
-    dk = TEH_keys_denomination_by_hash (
-      &rmc->refresh_session.coin.denom_pub_hash,
-      &ec,
-      &hc);
-    if (NULL == dk)
+    /* Way too late now, even zombies have expired */
+    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 */
+    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);
-      return TALER_MHD_reply_with_error (
-        connection,
-        MHD_HTTP_NOT_FOUND,
-        TALER_EC_EXCHANGE_GENERIC_DENOMINATION_KEY_UNKNOWN,
-        NULL);
+      /* 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");
     }
-    now = GNUNET_TIME_absolute_get ();
-    if (now.abs_value_us >= dk->meta.expire_legal.abs_value_us)
+    /* sanity check */
+    GNUNET_break (0 ==
+                  GNUNET_memcmp (&denom_hash,
+                                 &rmc->refresh_session.coin.denom_pub_hash));
+    if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
     {
-      /* Way too late now, even zombies have expired */
-      TEH_KS_release (key_state);
+      /* We never saw this coin before, so _this_ justification is not OK */
       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 (now.abs_value_us >= dk->meta.expire_deposit.abs_value_us)
+    else
     {
-      /* 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 */
+      /* 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 */
+  }
 
-    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,
-                                   &rmc->coin_value) )
-    {
-      GNUNET_break_op (0);
-      TEH_KS_release (key_state);
-      return TALER_MHD_reply_with_error (
-        connection,
-        MHD_HTTP_BAD_REQUEST,
-        TALER_EC_GENERIC_CURRENCY_MISMATCH,
-        rmc->refresh_session.amount_with_fee.currency);
-    }
-    /* check coin is actually properly signed */
-    if (GNUNET_OK !=
-        TALER_test_coin_valid (&rmc->refresh_session.coin,
-                               &dk->denom_pub))
-    {
-      GNUNET_break_op (0);
-      TEH_KS_release (key_state);
-      return TALER_MHD_reply_with_error (connection,
-                                         MHD_HTTP_FORBIDDEN,
-                                         
TALER_EC_EXCHANGE_DENOMINATION_SIGNATURE_INVALID,
-                                         NULL);
-    }
+  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,
+                                 &rmc->coin_value) )
+  {
+    GNUNET_break_op (0);
+    return TALER_MHD_reply_with_error (
+      connection,
+      MHD_HTTP_BAD_REQUEST,
+      TALER_EC_GENERIC_CURRENCY_MISMATCH,
+      rmc->refresh_session.amount_with_fee.currency);
+  }
+  /* check coin is actually properly signed */
+  if (GNUNET_OK !=
+      TALER_test_coin_valid (&rmc->refresh_session.coin,
+                             &dk->denom_pub))
+  {
+    GNUNET_break_op (0);
+    return TALER_MHD_reply_with_error (connection,
+                                       MHD_HTTP_FORBIDDEN,
+                                       
TALER_EC_EXCHANGE_DENOMINATION_SIGNATURE_INVALID,
+                                       NULL);
   }
-  TEH_KS_release (key_state);
 
   /* sanity-check that "total melt amount > melt fee" */
   if (0 <
diff --git a/src/exchange/taler-exchange-httpd_recoup.c 
b/src/exchange/taler-exchange-httpd_recoup.c
index aa521d66..f63bf072 100644
--- a/src/exchange/taler-exchange-httpd_recoup.c
+++ b/src/exchange/taler-exchange-httpd_recoup.c
@@ -30,9 +30,8 @@
 #include "taler_mhd_lib.h"
 #include "taler-exchange-httpd_recoup.h"
 #include "taler-exchange-httpd_responses.h"
-#include "taler-exchange-httpd_keystate.h"
 #include "taler-exchange-httpd_keys.h"
-
+#include "taler_exchangedb_lib.h"
 
 /**
  * Closure for #recoup_transaction.
@@ -366,124 +365,103 @@ verify_and_execute_recoup (struct MHD_Connection 
*connection,
   size_t coin_ev_size;
   enum TALER_ErrorCode ec;
   unsigned int hc;
+  struct GNUNET_TIME_Absolute now;
 
   /* check denomination exists and is in recoup mode */
+  dk = TEH_keys_denomination_by_hash (&coin->denom_pub_hash,
+                                      &ec,
+                                      &hc);
+  if (NULL == dk)
   {
-    struct TEH_KS_StateHandle *key_state;
-    struct GNUNET_TIME_Absolute now;
+    TALER_LOG_WARNING (
+      "Denomination key in recoup request not in recoup mode\n");
+    return TALER_MHD_reply_with_error (connection,
+                                       hc,
+                                       ec,
+                                       NULL);
+  }
 
-    key_state = TEH_KS_acquire (GNUNET_TIME_absolute_get ());
-    if (NULL == key_state)
-    {
-      TALER_LOG_ERROR ("Lacking keys to operate\n");
-      return TALER_MHD_reply_with_error (connection,
-                                         MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                         
TALER_EC_EXCHANGE_GENERIC_BAD_CONFIGURATION,
-                                         "no keys");
-    }
-    dk = TEH_keys_denomination_by_hash (&coin->denom_pub_hash,
-                                        &ec,
-                                        &hc);
-    if (NULL == dk)
-    {
-      TEH_KS_release (key_state);
-      TALER_LOG_WARNING (
-        "Denomination key in recoup request not in recoup mode\n");
-      return TALER_MHD_reply_with_error (connection,
-                                         hc,
-                                         ec,
-                                         NULL);
-    }
+  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 */
+    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 */
+    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 */
+    return TALER_MHD_reply_with_error (
+      connection,
+      MHD_HTTP_NOT_FOUND,
+      TALER_EC_EXCHANGE_RECOUP_NOT_ELIGIBLE,
+      NULL);
+  }
 
-    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;
 
-    pc.value = dk->meta.value;
+  /* check denomination signature */
+  if (GNUNET_YES !=
+      TALER_test_coin_valid (coin,
+                             &dk->denom_pub))
+  {
+    TALER_LOG_WARNING ("Invalid coin passed for recoup\n");
+    return TALER_MHD_reply_with_error (connection,
+                                       MHD_HTTP_FORBIDDEN,
+                                       
TALER_EC_EXCHANGE_DENOMINATION_SIGNATURE_INVALID,
+                                       NULL);
+  }
 
-    /* check denomination signature */
-    if (GNUNET_YES !=
-        TALER_test_coin_valid (coin,
-                               &dk->denom_pub))
-    {
-      TALER_LOG_WARNING ("Invalid coin passed for recoup\n");
-      TEH_KS_release (key_state);
-      return TALER_MHD_reply_with_error (connection,
-                                         MHD_HTTP_FORBIDDEN,
-                                         
TALER_EC_EXCHANGE_DENOMINATION_SIGNATURE_INVALID,
-                                         NULL);
-    }
+  /* check recoup request signature */
+  {
+    struct TALER_RecoupRequestPS pr = {
+      .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_RECOUP),
+      .purpose.size = htonl (sizeof (struct TALER_RecoupRequestPS)),
+      .coin_pub = coin->coin_pub,
+      .h_denom_pub = coin->denom_pub_hash,
+      .coin_blind = *coin_bks
+    };
 
-    /* check recoup request signature */
-    {
-      struct TALER_RecoupRequestPS pr = {
-        .purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_RECOUP),
-        .purpose.size = htonl (sizeof (struct TALER_RecoupRequestPS)),
-        .coin_pub = coin->coin_pub,
-        .h_denom_pub = coin->denom_pub_hash,
-        .coin_blind = *coin_bks
-      };
-
-      if (GNUNET_OK !=
-          GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_RECOUP,
-                                      &pr,
-                                      &coin_sig->eddsa_signature,
-                                      &coin->coin_pub.eddsa_pub))
-      {
-        TALER_LOG_WARNING ("Invalid signature on recoup request\n");
-        TEH_KS_release (key_state);
-        return TALER_MHD_reply_with_error (connection,
-                                           MHD_HTTP_FORBIDDEN,
-                                           
TALER_EC_EXCHANGE_RECOUP_SIGNATURE_INVALID,
-                                           NULL);
-      }
-    }
-    GNUNET_CRYPTO_hash (&coin->coin_pub.eddsa_pub,
-                        sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey),
-                        &c_hash);
-    if (GNUNET_YES !=
-        TALER_rsa_blind (&c_hash,
-                         &coin_bks->bks,
-                         dk->denom_pub.rsa_public_key,
-                         &coin_ev,
-                         &coin_ev_size))
+    if (GNUNET_OK !=
+        GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_RECOUP,
+                                    &pr,
+                                    &coin_sig->eddsa_signature,
+                                    &coin->coin_pub.eddsa_pub))
     {
-      GNUNET_break (0);
-      TEH_KS_release (key_state);
+      TALER_LOG_WARNING ("Invalid signature on recoup request\n");
       return TALER_MHD_reply_with_error (connection,
-                                         MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                         
TALER_EC_EXCHANGE_RECOUP_BLINDING_FAILED,
+                                         MHD_HTTP_FORBIDDEN,
+                                         
TALER_EC_EXCHANGE_RECOUP_SIGNATURE_INVALID,
                                          NULL);
     }
-    TEH_KS_release (key_state);
+  }
+  GNUNET_CRYPTO_hash (&coin->coin_pub.eddsa_pub,
+                      sizeof (struct GNUNET_CRYPTO_EcdsaPublicKey),
+                      &c_hash);
+  if (GNUNET_YES !=
+      TALER_rsa_blind (&c_hash,
+                       &coin_bks->bks,
+                       dk->denom_pub.rsa_public_key,
+                       &coin_ev,
+                       &coin_ev_size))
+  {
+    GNUNET_break (0);
+    return TALER_MHD_reply_with_error (connection,
+                                       MHD_HTTP_INTERNAL_SERVER_ERROR,
+                                       
TALER_EC_EXCHANGE_RECOUP_BLINDING_FAILED,
+                                       NULL);
   }
   GNUNET_CRYPTO_hash (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 9b3a42f9..9533ad53 100644
--- a/src/exchange/taler-exchange-httpd_refreshes_reveal.c
+++ b/src/exchange/taler-exchange-httpd_refreshes_reveal.c
@@ -28,7 +28,6 @@
 #include "taler-exchange-httpd_mhd.h"
 #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"
 
 
@@ -521,9 +520,8 @@ refreshes_reveal_persist (void *cls,
 
 
 /**
- * Resolve denomination hashes using the @a key_state
+ * Resolve denomination hashes.
  *
- * @param key_state the key state
  * @param connection the MHD connection to handle
  * @param rctx context for the operation, partially built at this time
  * @param link_sigs_json link signatures in JSON format
@@ -532,8 +530,7 @@ refreshes_reveal_persist (void *cls,
  * @return MHD result code
  */
 static MHD_RESULT
-resolve_refreshes_reveal_denominations (struct TEH_KS_StateHandle *key_state,
-                                        struct MHD_Connection *connection,
+resolve_refreshes_reveal_denominations (struct MHD_Connection *connection,
                                         struct RevealContext *rctx,
                                         const json_t *link_sigs_json,
                                         const json_t *new_denoms_h_json,
@@ -905,28 +902,11 @@ handle_refreshes_reveal_json (struct MHD_Connection 
*connection,
       return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
   }
 
-  {
-    struct TEH_KS_StateHandle *key_state;
-    int ret;
-
-    key_state = TEH_KS_acquire (GNUNET_TIME_absolute_get ());
-    if (NULL == key_state)
-    {
-      TALER_LOG_ERROR ("Lacking keys to operate\n");
-      return TALER_MHD_reply_with_error (connection,
-                                         MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                         
TALER_EC_EXCHANGE_GENERIC_KEYS_MISSING,
-                                         NULL);
-    }
-    ret = resolve_refreshes_reveal_denominations (key_state,
-                                                  connection,
-                                                  rctx,
-                                                  link_sigs_json,
-                                                  new_denoms_h_json,
-                                                  coin_evs);
-    TEH_KS_release (key_state);
-    return ret;
-  }
+  return resolve_refreshes_reveal_denominations (connection,
+                                                 rctx,
+                                                 link_sigs_json,
+                                                 new_denoms_h_json,
+                                                 coin_evs);
 }
 
 
diff --git a/src/exchange/taler-exchange-httpd_refund.c 
b/src/exchange/taler-exchange-httpd_refund.c
index 6bb94348..ace6e28f 100644
--- a/src/exchange/taler-exchange-httpd_refund.c
+++ b/src/exchange/taler-exchange-httpd_refund.c
@@ -32,7 +32,6 @@
 #include "taler_mhd_lib.h"
 #include "taler-exchange-httpd_refund.h"
 #include "taler-exchange-httpd_responses.h"
-#include "taler-exchange-httpd_keystate.h"
 #include "taler-exchange-httpd_keys.h"
 
 
@@ -448,52 +447,36 @@ verify_and_execute_refund (struct MHD_Connection 
*connection,
   }
 
   {
-    struct TEH_KS_StateHandle *key_state;
-
-    key_state = TEH_KS_acquire (GNUNET_TIME_absolute_get ());
-    if (NULL == key_state)
+    /* Obtain information about the coin's denomination! */
+    struct TEH_DenominationKey *dk;
+    unsigned int hc;
+    enum TALER_ErrorCode ec;
+
+    dk = TEH_keys_denomination_by_hash (&denom_hash,
+                                        &ec,
+                                        &hc);
+    if (NULL == dk)
     {
-      TALER_LOG_ERROR ("Lacking keys to operate\n");
+      /* DKI not found, but we do have a coin with this DK in our database;
+         not good... */
+      GNUNET_break (0);
       return TALER_MHD_reply_with_error (connection,
-                                         MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                         
TALER_EC_EXCHANGE_GENERIC_BAD_CONFIGURATION,
-                                         "no keys");
+                                         hc,
+                                         ec,
+                                         NULL);
     }
-    /* Obtain information about the coin's denomination! */
-    {
-      struct TEH_DenominationKey *dk;
-      unsigned int hc;
-      enum TALER_ErrorCode ec;
-
-      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... */
-        GNUNET_break (0);
-        TEH_KS_release (key_state);
-        return TALER_MHD_reply_with_error (connection,
-                                           hc,
-                                           ec,
-                                           NULL);
-      }
 
-      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;
+    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 */
+      return TALER_MHD_reply_with_error (
+        connection,
+        MHD_HTTP_GONE,
+        TALER_EC_EXCHANGE_GENERIC_DENOMINATION_EXPIRED,
+        NULL);
     }
-    TEH_KS_release (key_state);
+    refund->details.refund_fee = dk->meta.fee_refund;
   }
 
   /* Finally run the actual transaction logic */
diff --git a/src/exchange/taler-exchange-httpd_reserves_get.c 
b/src/exchange/taler-exchange-httpd_reserves_get.c
index a5ebaabf..b901afbb 100644
--- a/src/exchange/taler-exchange-httpd_reserves_get.c
+++ b/src/exchange/taler-exchange-httpd_reserves_get.c
@@ -27,7 +27,6 @@
 #include "taler_json_lib.h"
 #include "taler-exchange-httpd_reserves_get.h"
 #include "taler-exchange-httpd_responses.h"
-#include "taler-exchange-httpd_keystate.h"
 
 
 /**
diff --git a/src/exchange/taler-exchange-httpd_responses.c 
b/src/exchange/taler-exchange-httpd_responses.c
index c0ec6d95..ba050e9f 100644
--- a/src/exchange/taler-exchange-httpd_responses.c
+++ b/src/exchange/taler-exchange-httpd_responses.c
@@ -28,7 +28,6 @@
 #include "taler_util.h"
 #include "taler_json_lib.h"
 #include "taler_mhd_lib.h"
-#include "taler-exchange-httpd_keystate.h"
 #include "taler-exchange-httpd_keys.h"
 
 
diff --git a/src/exchange/taler-exchange-httpd_transfers_get.c 
b/src/exchange/taler-exchange-httpd_transfers_get.c
index b7f24f23..578b9428 100644
--- a/src/exchange/taler-exchange-httpd_transfers_get.c
+++ b/src/exchange/taler-exchange-httpd_transfers_get.c
@@ -25,7 +25,6 @@
 #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"
 #include "taler_json_lib.h"
diff --git a/src/exchange/taler-exchange-httpd_wire.c 
b/src/exchange/taler-exchange-httpd_wire.c
deleted file mode 100644
index 471fa4fa..00000000
--- a/src/exchange/taler-exchange-httpd_wire.c
+++ /dev/null
@@ -1,399 +0,0 @@
-/*
-  This file is part of TALER
-  Copyright (C) 2015-2020 Taler Systems SA
-
-  TALER is free software; you can redistribute it and/or modify it under the
-  terms of the GNU Affero General Public License as published by the Free 
Software
-  Foundation; either version 3, or (at your option) any later version.
-
-  TALER is distributed in the hope that it will be useful, but WITHOUT ANY
-  WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
-  A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more 
details.
-
-  You should have received a copy of the GNU Affero General Public License 
along with
-  TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
-*/
-/**
- * @file taler-exchange-httpd_wire.c
- * @brief Handle /wire requests
- * @author Christian Grothoff
- */
-#include "platform.h"
-#include <gnunet/gnunet_json_lib.h>
-#include "taler-exchange-httpd_keystate.h"
-#include "taler-exchange-httpd_responses.h"
-#include "taler-exchange-httpd_wire.h"
-#include "taler_json_lib.h"
-#include "taler_mhd_lib.h"
-#include <jansson.h>
-
-/**
- * Cached JSON for /wire response.
- */
-static json_t *wire_methods;
-
-/**
- * Array of wire methods supported by this exchange.
- */
-static json_t *wire_accounts_array;
-
-/**
- * Object mapping wire methods to the respective fee structure.
- */
-static json_t *wire_fee_object;
-
-
-/**
- * Convert fee structure to JSON result to be returned
- * as part of a /wire response.
- *
- * @param af fee structure to convert
- * @return NULL on error, otherwise json data structure for /wire.
- */
-static json_t *
-fees_to_json (struct TALER_EXCHANGEDB_AggregateFees *af)
-{
-  json_t *a;
-
-  a = json_array ();
-  if (NULL == a)
-  {
-    GNUNET_break (0); /* out of memory? */
-    return NULL;
-  }
-  while (NULL != af)
-  {
-    if ( (GNUNET_NO == GNUNET_TIME_round_abs (&af->start_date)) ||
-         (GNUNET_NO == GNUNET_TIME_round_abs (&af->end_date)) )
-    {
-      GNUNET_break (0); /* bad timestamps, should not happen */
-      json_decref (a);
-      return NULL;
-    }
-    if (0 !=
-        json_array_append_new (a,
-                               json_pack ("{s:o, s:o, s:o, s:o, s:o}",
-                                          "wire_fee", TALER_JSON_from_amount (
-                                            &af->wire_fee),
-                                          "closing_fee",
-                                          TALER_JSON_from_amount (
-                                            &af->closing_fee),
-                                          "start_date",
-                                          GNUNET_JSON_from_time_abs (
-                                            af->start_date),
-                                          "end_date",
-                                          GNUNET_JSON_from_time_abs (
-                                            af->end_date),
-                                          "sig", GNUNET_JSON_from_data_auto (
-                                            &af->master_sig))))
-    {
-      GNUNET_break (0); /* out of memory? */
-      json_decref (a);
-      return NULL;
-    }
-    af = af->next;
-  }
-  return a;
-}
-
-
-/**
- * Obtain fee structure for @a method wire transfers.
- *
- * @param method method to load fees for
- * @return JSON object (to be freed by caller) with fee structure
- */
-static json_t *
-get_fees (const char *method)
-{
-  struct TALER_EXCHANGEDB_AggregateFees *af;
-  struct GNUNET_TIME_Absolute now;
-
-  af = TALER_EXCHANGEDB_fees_read (TEH_cfg,
-                                   method);
-  now = GNUNET_TIME_absolute_get ();
-  while ( (NULL != af) &&
-          (af->end_date.abs_value_us < now.abs_value_us) )
-  {
-    struct TALER_EXCHANGEDB_AggregateFees *n = af->next;
-
-    GNUNET_free (af);
-    af = n;
-  }
-  if (NULL == af)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Failed to find current wire transfer fees for `%s' at time 
%s\n",
-                method,
-                GNUNET_STRINGS_absolute_time_to_string (now));
-    return NULL;
-  }
-  {
-    json_t *j;
-
-    j = fees_to_json (af);
-    TALER_EXCHANGEDB_fees_free (af);
-    return j;
-  }
-}
-
-
-/**
- * Load wire fees for @a method.
- *
- * @param method wire method to load fee structure for
- * @return #GNUNET_OK on success
- */
-static int
-load_fee (const char *method)
-{
-  json_t *fees;
-
-  if (NULL != json_object_get (wire_fee_object,
-                               method))
-    return GNUNET_OK; /* already have them */
-  fees = get_fees (method);
-  if (NULL == fees)
-    return GNUNET_SYSERR;
-  /* Add fees to #wire_fee_object */
-  if (0 !=
-      json_object_set_new (wire_fee_object,
-                           method,
-                           fees))
-  {
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
-  }
-  return GNUNET_OK;
-}
-
-
-/**
- * Initialize account; checks if @a ai has /wire information, and if so,
- * adds the /wire information (if included) to our responses.
- *
- * @param cls pointer to `int` to set to #GNUNET_SYSERR on errors
- * @param ai details about the account we should load the wire details for
- */
-static void
-load_account (void *cls,
-              const struct TALER_EXCHANGEDB_AccountInfo *ai)
-{
-  int *ret = cls;
-
-  if ( (NULL != ai->wire_response_filename) &&
-       (GNUNET_YES == ai->credit_enabled) )
-  {
-    json_t *wire_s;
-    json_error_t error;
-
-    if (NULL == (wire_s = json_load_file (ai->wire_response_filename,
-                                          JSON_REJECT_DUPLICATES,
-                                          &error)))
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                  "Failed to parse `%s': %s at %d:%d (%d)\n",
-                  ai->wire_response_filename,
-                  error.text,
-                  error.line,
-                  error.column,
-                  error.position);
-      *ret = GNUNET_SYSERR;
-      return;
-    }
-
-    {
-      char *url;
-
-      if (NULL == (url = TALER_JSON_wire_to_payto (wire_s)))
-      {
-        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                    "Wire response file `%s' malformed\n",
-                    ai->wire_response_filename);
-        json_decref (wire_s);
-        *ret = GNUNET_SYSERR;
-        return;
-      }
-      if (0 != strcasecmp (url,
-                           ai->payto_uri))
-      {
-        GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                    "URL in wire response file `%s' does not match URL in 
configuration (%s vs %s)!\n",
-                    ai->wire_response_filename,
-                    url,
-                    ai->payto_uri);
-        json_decref (wire_s);
-        GNUNET_free (url);
-        *ret = GNUNET_SYSERR;
-        return;
-      }
-      GNUNET_free (url);
-    }
-    /* Provide friendly error message if user forgot to sign wire response. */
-    if (NULL == json_object_get (wire_s,
-                                 "master_sig"))
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                  "Wire response file `%s' has not been signed."
-                  " Use taler-exchange-wire to sign it.\n",
-                  ai->wire_response_filename);
-      json_decref (wire_s);
-      *ret = GNUNET_SYSERR;
-      return;
-    }
-    if (GNUNET_OK !=
-        TALER_JSON_exchange_wire_signature_check (wire_s,
-                                                  &TEH_master_public_key))
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                  "Invalid signature in `%s' for public key `%s'\n",
-                  ai->wire_response_filename,
-                  GNUNET_p2s (&TEH_master_public_key.eddsa_pub));
-      json_decref (wire_s);
-      *ret = GNUNET_SYSERR;
-      return;
-    }
-    if (GNUNET_OK ==
-        load_fee (ai->method))
-    {
-      if (0 !=
-          json_array_append_new (wire_accounts_array,
-                                 wire_s))
-      {
-        GNUNET_break (0);
-        *ret = GNUNET_SYSERR;
-      }
-    }
-    else
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                  "Wire fees not specified for `%s'\n",
-                  ai->method);
-      *ret = GNUNET_SYSERR;
-    }
-  }
-  else if (GNUNET_YES == ai->debit_enabled)
-  {
-    if (GNUNET_OK !=
-        load_fee (ai->method))
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                  "Wire transfer fees for `%s' are not given correctly\n",
-                  ai->method);
-      *ret = GNUNET_SYSERR;
-      return;
-    }
-  }
-}
-
-
-/**
- * Handle a "/wire" request.
- *
- * @param rh context of the handler
- * @param connection the MHD connection to handle
- * @param args array of additional options (must be empty for this function)
- * @return MHD result code
-  */
-MHD_RESULT
-TEH_handler_wire (const struct TEH_RequestHandler *rh,
-                  struct MHD_Connection *connection,
-                  const char *const args[])
-{
-  (void) rh;
-  (void) args;
-  GNUNET_assert (NULL != wire_methods);
-  return TALER_MHD_reply_json (connection,
-                               wire_methods,
-                               MHD_HTTP_OK);
-}
-
-
-/**
- * Initialize wire subsystem.
- *
- * @param cfg configuration to use
- * @return #GNUNET_OK on success, #GNUNET_SYSERR if we found no valid
- *         wire methods
- */
-int
-TEH_WIRE_init (const struct GNUNET_CONFIGURATION_Handle *cfg)
-{
-  wire_accounts_array = json_array ();
-  if (NULL == wire_accounts_array)
-  {
-    GNUNET_break (0);
-    return GNUNET_SYSERR;
-  }
-  wire_fee_object = json_object ();
-  if (NULL == wire_fee_object)
-  {
-    GNUNET_break (0);
-    TEH_WIRE_done ();
-    return GNUNET_SYSERR;
-  }
-  {
-    int ret;
-
-    ret = GNUNET_OK;
-    TALER_EXCHANGEDB_find_accounts (cfg,
-                                    &load_account,
-                                    &ret);
-    if (GNUNET_OK != ret)
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                  "Error setting up bank accounts\n");
-      TEH_WIRE_done ();
-      return GNUNET_SYSERR;
-    }
-  }
-  if ( (0 == json_array_size (wire_accounts_array)) ||
-       (0 == json_object_size (wire_fee_object)) )
-  {
-    TEH_WIRE_done ();
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "No bank accounts configured\n");
-    return GNUNET_SYSERR;
-  }
-  wire_methods = json_pack ("{s:O, s:O, s:o}",
-                            "accounts", wire_accounts_array,
-                            "fees", wire_fee_object,
-                            "master_public_key",
-                            GNUNET_JSON_from_data_auto (
-                              &TEH_master_public_key));
-  if (NULL == wire_methods)
-  {
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                "Failed to find properly configured wire transfer method\n");
-    TEH_WIRE_done ();
-    return GNUNET_SYSERR;
-  }
-  return GNUNET_OK;
-}
-
-
-/**
- * Clean up wire subsystem.
- */
-void
-TEH_WIRE_done ()
-{
-  if (NULL != wire_methods)
-  {
-    json_decref (wire_methods);
-    wire_methods = NULL;
-  }
-  if (NULL != wire_fee_object)
-  {
-    json_decref (wire_fee_object);
-    wire_fee_object = NULL;
-  }
-  if (NULL != wire_accounts_array)
-  {
-    json_decref (wire_accounts_array);
-    wire_accounts_array = NULL;
-  }
-}
-
-
-/* end of taler-exchange-httpd_wire.c */
diff --git a/src/exchange/taler-exchange-httpd_wire.h 
b/src/exchange/taler-exchange-httpd_wire.h
index 7df87432..b07bfcfb 100644
--- a/src/exchange/taler-exchange-httpd_wire.h
+++ b/src/exchange/taler-exchange-httpd_wire.h
@@ -29,12 +29,10 @@
 /**
  * Initialize wire subsystem.
  *
- * @param cfg configuration to use
- * @return #GNUNET_OK on success, #GNUNET_SYSERR if we found no valid
- *         wire methods
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on error.
  */
 int
-TEH_WIRE_init (const struct GNUNET_CONFIGURATION_Handle *cfg);
+TEH_WIRE_init (void);
 
 
 /**
@@ -44,6 +42,19 @@ void
 TEH_WIRE_done (void);
 
 
+/**
+ * Something changed in the database. Rebuild the wire replies.  This function
+ * should be called if the exchange learns about a new signature from our
+ * master key.
+ *
+ * (We do not do so immediately, but merely signal to all threads that they
+ * need to rebuild their wire state upon the next call to
+ * #wire_get_state()).
+ */
+void
+TEH_wire_update_state (void);
+
+
 /**
  * Handle a "/wire" request.
  *
diff --git a/src/exchange/taler-exchange-httpd_wire2.c 
b/src/exchange/taler-exchange-httpd_wire2.c
index a80557b4..b4f60b72 100644
--- a/src/exchange/taler-exchange-httpd_wire2.c
+++ b/src/exchange/taler-exchange-httpd_wire2.c
@@ -20,7 +20,6 @@
  */
 #include "platform.h"
 #include <gnunet/gnunet_json_lib.h>
-#include "taler-exchange-httpd_keystate.h"
 #include "taler-exchange-httpd_responses.h"
 #include "taler-exchange-httpd_wire.h"
 #include "taler_json_lib.h"
@@ -76,6 +75,47 @@ destroy_wire_state (struct WireStateHandle *wsh)
 }
 
 
+/**
+ * Free memory assciated with wire state. Signature
+ * suitable for pthread_key_create().
+ *
+ * @param[in] cls the `struct WireStateHandle` to destroy
+ */static void
+destroy_wire_state_cb (void *cls)
+{
+  struct WireStateHandle *wsh = cls;
+
+  destroy_wire_state (wsh);
+}
+
+
+/**
+ * Initialize WIRE submodule.
+ *
+ * @return #GNUNET_OK on success
+ */
+int
+TEH_WIRE_init ()
+{
+  if (0 !=
+      pthread_key_create (&wire_state,
+                          &destroy_wire_state_cb))
+    return GNUNET_SYSERR;
+  return GNUNET_OK;
+}
+
+
+/**
+ * Fully clean up our state.
+ */
+void
+TEH_WIRE_done ()
+{
+  GNUNET_assert (0 ==
+                 pthread_key_delete (wire_state));
+}
+
+
 /**
  * Add information about a wire account to @a cls.
  *
@@ -95,9 +135,9 @@ add_wire_account (void *cls,
       json_array_append_new (
         a,
         json_pack ("{s:s, s:o}",
-                   "url", /* "payto_uri" would be better, but this is the name 
in the spec */
+                   "payto_uri",
                    payto_uri,
-                   "sig",
+                   "master_sig",
                    GNUNET_JSON_from_data_auto (master_sig))))
   {
     GNUNET_break (0);   /* out of memory!? */
@@ -182,7 +222,7 @@ build_wire_state (void)
     json_array_foreach (wire_accounts_array, index, account) {
       char *wire_method;
       const char *payto_uri = json_string_value (json_object_get (account,
-                                                                  "url"));
+                                                                  
"payto_uri"));
       GNUNET_assert (NULL != payto_uri);
       wire_method = TALER_payto_get_method (payto_uri);
       if (NULL == json_object_get (wire_fee_object,
@@ -230,17 +270,8 @@ build_wire_state (void)
 }
 
 
-/**
- * Something changed in the database. Rebuild the wire replies.  This function
- * should be called if the exchange learns about a new signature from our
- * master key.
- *
- * (We do not do so immediately, but merely signal to all threads that they
- * need to rebuild their wire state upon the next call to
- * #wire_get_state()).
- */
 void
-TEH_wire_update_state ()
+TEH_wire_update_state (void)
 {
   __sync_fetch_and_add (&wire_generation,
                         1);
diff --git a/src/exchange/taler-exchange-httpd_withdraw.c 
b/src/exchange/taler-exchange-httpd_withdraw.c
index 035273bc..68a8e5fd 100644
--- a/src/exchange/taler-exchange-httpd_withdraw.c
+++ b/src/exchange/taler-exchange-httpd_withdraw.c
@@ -30,7 +30,6 @@
 #include "taler_mhd_lib.h"
 #include "taler-exchange-httpd_withdraw.h"
 #include "taler-exchange-httpd_responses.h"
-#include "taler-exchange-httpd_keystate.h"
 #include "taler-exchange-httpd_keys.h"
 
 
@@ -124,11 +123,6 @@ struct WithdrawContext
    */
   char *blinded_msg;
 
-  /**
-   * Key state to use to inspect previous withdrawal values.
-   */
-  struct TEH_KS_StateHandle *key_state;
-
   /**
    * Number of bytes in @e blinded_msg.
    */
@@ -384,16 +378,6 @@ TEH_handler_withdraw (const struct TEH_RequestHandler *rh,
     if (GNUNET_OK != res)
       return (GNUNET_SYSERR == res) ? MHD_NO : MHD_YES;
   }
-  wc.key_state = TEH_KS_acquire (GNUNET_TIME_absolute_get ());
-  if (NULL == wc.key_state)
-  {
-    TALER_LOG_ERROR ("Lacking keys to operate\n");
-    GNUNET_JSON_parse_free (spec);
-    return TALER_MHD_reply_with_error (connection,
-                                       MHD_HTTP_INTERNAL_SERVER_ERROR,
-                                       
TALER_EC_EXCHANGE_GENERIC_BAD_CONFIGURATION,
-                                       "no keys");
-  }
   {
     unsigned int hc;
     enum TALER_ErrorCode ec;
@@ -405,7 +389,6 @@ TEH_handler_withdraw (const struct TEH_RequestHandler *rh,
     if (NULL == dk)
     {
       GNUNET_JSON_parse_free (spec);
-      TEH_KS_release (wc.key_state);
       return TALER_MHD_reply_with_error (connection,
                                          hc,
                                          ec,
@@ -415,7 +398,6 @@ TEH_handler_withdraw (const struct TEH_RequestHandler *rh,
     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,
@@ -426,7 +408,6 @@ TEH_handler_withdraw (const struct TEH_RequestHandler *rh,
     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,
@@ -437,7 +418,6 @@ TEH_handler_withdraw (const struct TEH_RequestHandler *rh,
     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,
@@ -454,7 +434,6 @@ TEH_handler_withdraw (const struct TEH_RequestHandler *rh,
                           &dk->meta.fee_withdraw))
     {
       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_AMOUNT_FEE_OVERFLOW,
@@ -483,7 +462,6 @@ TEH_handler_withdraw (const struct TEH_RequestHandler *rh,
     TALER_LOG_WARNING (
       "Client supplied invalid signature for withdraw request\n");
     GNUNET_JSON_parse_free (spec);
-    TEH_KS_release (wc.key_state);
     return TALER_MHD_reply_with_error (connection,
                                        MHD_HTTP_FORBIDDEN,
                                        
TALER_EC_EXCHANGE_WITHDRAW_RESERVE_SIGNATURE_INVALID,
@@ -501,7 +479,6 @@ TEH_handler_withdraw (const struct TEH_RequestHandler *rh,
   {
     GNUNET_break (0);
     GNUNET_JSON_parse_free (spec);
-    TEH_KS_release (wc.key_state);
     return TALER_MHD_reply_with_ec (connection,
                                     ec,
                                     NULL);
@@ -519,7 +496,6 @@ TEH_handler_withdraw (const struct TEH_RequestHandler *rh,
                                 &withdraw_transaction,
                                 &wc))
     {
-      TEH_KS_release (wc.key_state);
       /* Even if #withdraw_transaction() failed, it may have created a 
signature
          (or we might have done it optimistically above). */
       if (NULL != wc.collectable.sig.rsa_signature)
@@ -530,7 +506,6 @@ TEH_handler_withdraw (const struct TEH_RequestHandler *rh,
   }
 
   /* Clean up and send back final (positive) response */
-  TEH_KS_release (wc.key_state);
   GNUNET_JSON_parse_free (spec);
 
   {
diff --git a/src/exchange/test_taler_exchange_httpd.sh 
b/src/exchange/test_taler_exchange_httpd.sh
index 94348891..e8dc46af 100755
--- a/src/exchange/test_taler_exchange_httpd.sh
+++ b/src/exchange/test_taler_exchange_httpd.sh
@@ -27,7 +27,7 @@ unset XDG_CONFIG_HOME
 echo -n "Launching exchange ..."
 PREFIX=
 # Uncomment this line to run with valgrind...
-# PREFIX="valgrind --leak-check=yes --track-fds=yes --error-exitcode=1 
--log-file=valgrind.%p"
+#PREFIX="valgrind --leak-check=yes --track-fds=yes --error-exitcode=1 
--log-file=valgrind.%p"
 
 # Setup database
 taler-exchange-dbinit -c test_taler_exchange_httpd.conf &> /dev/null
diff --git a/src/include/taler_testing_lib.h b/src/include/taler_testing_lib.h
index a5a0b435..1722a833 100644
--- a/src/include/taler_testing_lib.h
+++ b/src/include/taler_testing_lib.h
@@ -446,13 +446,6 @@ struct TALER_TESTING_Interpreter
    */
   char *exchange_url;
 
-  /**
-   * #GNUNET_OK if key state should be reloaded.  NOTE: this
-   * field can be removed because a new "send signal" command
-   * has been introduced.
-   */
-  int reload_keys;
-
   /**
    * Is the interpreter running (#GNUNET_YES) or waiting
    * for /keys (#GNUNET_NO)?
@@ -1227,54 +1220,6 @@ TALER_TESTING_cmd_exec_transfer (const char *label,
                                  const char *config_filename);
 
 
-/**
- * Make the "keyup" CMD.
- *
- * @param label command label.
- * @param config_filename configuration filename.
- * @return the command.
- */
-struct TALER_TESTING_Command
-TALER_TESTING_cmd_exec_keyup (const char *label,
-                              const char *config_filename);
-
-
-/**
- * Make the "keyup" CMD, with "--timestamp" option.
- *
- * @param label command label.
- * @param config_filename configuration filename.
- * @param now Unix timestamp representing the fake "now".
- * @return the command.
- */
-struct TALER_TESTING_Command
-TALER_TESTING_cmd_exec_keyup_with_now (const char *label,
-                                       const char *config_filename,
-                                       struct GNUNET_TIME_Absolute now);
-
-
-/**
- * Make a "check keys" command.  This type of command
- * checks whether the number of denomination keys from
- * @a exchange matches @a num_denom_keys.
- *
- * @param label command label
- * @param generation when this command is run, exactly @a
- *        generation /keys downloads took place.  If the number
- *        of downloads is less than @a generation, the logic will
- *        first make sure that @a generation downloads are done,
- *        and _then_ execute the rest of the command.
- * @param num_denom_keys expected number of denomination keys.
- * @param now timestamp to use when fetching keys
- * @return the command.
- */
-struct TALER_TESTING_Command
-TALER_TESTING_cmd_check_keys_with_now (const char *label,
-                                       unsigned int generation,
-                                       unsigned int num_denom_keys,
-                                       struct GNUNET_TIME_Absolute now);
-
-
 /**
  * Make a "auditor sign" CMD.
  *
@@ -1840,27 +1785,21 @@ TALER_TESTING_cmd_wait_service (const char *label,
 
 
 /**
- * Make a "check keys" command.  This type of command
- * checks whether the number of denomination keys from
- * @a exchange matches @a num_denom_keys.
+ * Make a "check keys" command.
  *
  * @param label command label
  * @param generation how many /keys responses are expected to
  *        have been returned when this CMD will be run.
- * @param num_denom_keys expected number of denomination keys.
- *
  * @return the command.
  */
 struct TALER_TESTING_Command
 TALER_TESTING_cmd_check_keys (const char *label,
-                              unsigned int generation,
-                              unsigned int num_denom_keys);
+                              unsigned int generation);
 
 
 /**
  * Make a "check keys" command that forcedly does NOT cherry pick;
- * just redownload the whole /keys.  Then checks whether the number
- * of denomination keys from @a exchange matches @a num_denom_keys.
+ * just redownload the whole /keys.
  *
  * @param label command label
  * @param generation when this command is run, exactly @a
@@ -1868,20 +1807,15 @@ TALER_TESTING_cmd_check_keys (const char *label,
  *        of downloads is less than @a generation, the logic will
  *        first make sure that @a generation downloads are done,
  *        and _then_ execute the rest of the command.
- * @param num_denom_keys expected number of denomination keys.
  * @return the command.
  */
 struct TALER_TESTING_Command
 TALER_TESTING_cmd_check_keys_pull_all_keys (const char *label,
-                                            unsigned int generation,
-                                            unsigned int num_denom_keys);
+                                            unsigned int generation);
 
 
 /**
- * Make a "check keys" command.  This type of command
- * checks whether the number of denomination keys from
- * @a exchange matches @a num_denom_keys.  Additionally,
- * it lets the user set a last denom issue date to be
+ * Make a "check keys" command.  It lets the user set a last denom issue date 
to be
  * used in the request for /keys.
  *
  * @param label command label
@@ -1890,17 +1824,15 @@ TALER_TESTING_cmd_check_keys_pull_all_keys (const char 
*label,
  *        of downloads is less than @a generation, the logic will
  *        first make sure that @a generation downloads are done,
  *        and _then_ execute the rest of the command.
- * @param num_denom_keys expected number of denomination keys.
  * @param last_denom_date date to be set in the "last_denom_issue"
  *        URL parameter of /keys.
  * @return the command.
  */
 struct TALER_TESTING_Command
-TALER_TESTING_cmd_check_keys_with_last_denom (const char *label,
-                                              unsigned int generation,
-                                              unsigned int num_denom_keys,
-                                              struct GNUNET_TIME_Absolute
-                                              last_denom_date);
+TALER_TESTING_cmd_check_keys_with_last_denom (
+  const char *label,
+  unsigned int generation,
+  struct GNUNET_TIME_Absolute last_denom_date);
 
 
 /**
@@ -2169,6 +2101,22 @@ TALER_TESTING_cmd_exec_offline_sign_keys (const char 
*label,
                                           const char *config_filename);
 
 
+/**
+ * Sign a wire fee.
+ *
+ * @param label command label.
+ * @param config_filename configuration filename.
+ * @param wire_fee the wire fee to affirm (for the current year)
+ * @param closing_fee the closing fee to affirm (for the current year)
+ * @return the command
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_exec_offline_sign_fees (const char *label,
+                                          const char *config_filename,
+                                          const char *wire_fee,
+                                          const char *closing_fee);
+
+
 /**
  * Revoke an exchange denomination key.
  *
diff --git a/src/testing/Makefile.am b/src/testing/Makefile.am
index ceba6220..0e09b146 100644
--- a/src/testing/Makefile.am
+++ b/src/testing/Makefile.am
@@ -56,10 +56,10 @@ libtalertesting_la_SOURCES = \
   testing_api_cmd_exec_aggregator.c \
   testing_api_cmd_exec_auditor-sign.c \
   testing_api_cmd_exec_closer.c \
-  testing_api_cmd_exec_keyup.c \
   testing_api_cmd_exec_transfer.c \
   testing_api_cmd_exec_wirewatch.c \
   testing_api_cmd_insert_deposit.c \
+  testing_api_cmd_offline_sign_fees.c \
   testing_api_cmd_offline_sign_keys.c \
   testing_api_cmd_set_wire_fee.c \
   testing_api_cmd_recoup.c \
diff --git a/src/testing/test_auditor_api.c b/src/testing/test_auditor_api.c
index 48f03119..c4f890e6 100644
--- a/src/testing/test_auditor_api.c
+++ b/src/testing/test_auditor_api.c
@@ -630,8 +630,7 @@ 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",
-                                                2,
-                                                270 /* FIXME: wrong number... 
*/),
+                                                2),
     CMD_RUN_AUDITOR ("virgin-auditor"),
     TALER_TESTING_cmd_exchanges_with_url ("check-exchange",
                                           MHD_HTTP_OK,
diff --git a/src/testing/test_exchange_api.c b/src/testing/test_exchange_api.c
index f24a3ff7..18f7237b 100644
--- a/src/testing/test_exchange_api.c
+++ b/src/testing/test_exchange_api.c
@@ -956,9 +956,12 @@ run (void *cls,
                                   false),
       TALER_TESTING_cmd_exec_offline_sign_keys ("offline-sign-future-keys",
                                                 CONFIG_FILE),
+      TALER_TESTING_cmd_exec_offline_sign_fees ("offline-sign-fees",
+                                                CONFIG_FILE,
+                                                "EUR:0.01",
+                                                "EUR:0.01"),
       TALER_TESTING_cmd_check_keys_pull_all_keys ("refetch /keys",
-                                                  1,
-                                                  270 /* FIXME: wrong 
number... */),
+                                                  1),
       TALER_TESTING_cmd_batch ("wire",
                                wire),
       TALER_TESTING_cmd_batch ("withdraw",
diff --git a/src/testing/test_exchange_api_overlapping_keys_bug.c 
b/src/testing/test_exchange_api_overlapping_keys_bug.c
index 19093243..86f75f93 100644
--- a/src/testing/test_exchange_api_overlapping_keys_bug.c
+++ b/src/testing/test_exchange_api_overlapping_keys_bug.c
@@ -81,14 +81,11 @@ 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,
-                                                1 /* FIXME: wrong number... 
*/),
+                                                1),
     TALER_TESTING_cmd_check_keys ("first-download",
-                                  1,
                                   1),
     /* Causes GET /keys?last_denom_issue=0 */
     TALER_TESTING_cmd_check_keys_with_last_denom ("second-download",
-                                                  3,
                                                   1,
                                                   GNUNET_TIME_UNIT_ZERO_ABS),
     TALER_TESTING_cmd_end ()
diff --git a/src/testing/test_exchange_api_revocation.c 
b/src/testing/test_exchange_api_revocation.c
index 55589149..94e162b9 100644
--- a/src/testing/test_exchange_api_revocation.c
+++ b/src/testing/test_exchange_api_revocation.c
@@ -72,8 +72,7 @@ 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,
-                                                270 /* FIXME: wrong number... 
*/),
+                                                1),
     /**
      * 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 05867f89..818b54de 100644
--- a/src/testing/test_exchange_api_twisted.c
+++ b/src/testing/test_exchange_api_twisted.c
@@ -221,8 +221,7 @@ run (void *cls,
                                         "Wed, 19 Jan 586524 08:01:49 GMT"),
     TALER_TESTING_cmd_check_keys_pull_all_keys (
       "check-keys-expiration-0",
-      2,
-      270),
+      2),
     /**
      * Run some normal commands after this to make sure everything is fine.
      */
@@ -244,8 +243,7 @@ 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,
-                                                270 /* FIXME: wrong number... 
*/),
+                                                1),
     TALER_TESTING_cmd_batch ("refresh-reveal-409-conflict",
                              refresh_409_conflict),
     TALER_TESTING_cmd_batch ("refund",
diff --git a/src/testing/test_exchange_management_api.c 
b/src/testing/test_exchange_management_api.c
index 18f6dedf..b53926fd 100644
--- a/src/testing/test_exchange_management_api.c
+++ b/src/testing/test_exchange_management_api.c
@@ -143,8 +143,7 @@ run (void *cls,
     TALER_TESTING_cmd_exec_offline_sign_keys ("download-future-keys",
                                               CONFIG_FILE),
     TALER_TESTING_cmd_check_keys_pull_all_keys ("refetch /keys",
-                                                1,
-                                                270 /* FIXME: wrong number... 
*/),
+                                                1),
     TALER_TESTING_cmd_end ()
   };
 
diff --git a/src/testing/test_taler_exchange_wirewatch.c 
b/src/testing/test_taler_exchange_wirewatch.c
index 169c959b..08c5936f 100644
--- a/src/testing/test_taler_exchange_wirewatch.c
+++ b/src/testing/test_taler_exchange_wirewatch.c
@@ -92,8 +92,7 @@ run (void *cls,
     TALER_TESTING_cmd_exec_offline_sign_keys ("offline-sign-future-keys",
                                               config_filename),
     TALER_TESTING_cmd_check_keys_pull_all_keys ("refetch /keys",
-                                                1,
-                                                58 /* FIXME: wrong number... 
*/),
+                                                1),
     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_check_keys.c 
b/src/testing/testing_api_cmd_check_keys.c
index 20dbfb85..bdf142dd 100644
--- a/src/testing/testing_api_cmd_check_keys.c
+++ b/src/testing/testing_api_cmd_check_keys.c
@@ -46,12 +46,6 @@ struct CheckKeysState
    */
   unsigned int generation;
 
-  /**
-   * How many denomination keys the exchange is
-   * supposed to have.
-   */
-  unsigned int num_denom_keys;
-
   /**
    * If this value is GNUNET_YES, then the "cherry
    * picking" facility is turned off; whole /keys is
@@ -156,19 +150,6 @@ check_keys_run (void *cls,
     return;
   }
 #endif
-  /* "/keys" was updated, let's check they were OK! */
-  if (cks->num_denom_keys != is->keys->num_denom_keys)
-  {
-    /* Did not get the expected number of denomination keys! */
-    GNUNET_break (0);
-    TALER_LOG_ERROR ("Got %u keys in step %s, expected %u\n",
-                     is->keys->num_denom_keys,
-                     cmd->label,
-                     cks->num_denom_keys);
-    TALER_TESTING_interpreter_fail (is);
-    return;
-  }
-
   /* Let's unset the fake now before moving on.  */
   TALER_EXCHANGE_unset_now (is->exchange);
   TALER_TESTING_interpreter_next (is);
@@ -192,36 +173,16 @@ check_keys_cleanup (void *cls,
 }
 
 
-/**
- * Make a "check keys" command.  This type of command
- * checks whether the number of denomination keys from
- * @a exchange matches @a num_denom_keys.  Additionally,
- * it lets the user set a last denom issue date to be
- * used in the request for /keys.
- *
- * @param label command label
- * @param generation when this command is run, exactly @a
- *        generation /keys downloads took place.  If the number
- *        of downloads is less than @a generation, the logic will
- *        first make sure that @a generation downloads are done,
- *        and _then_ execute the rest of the command.
- * @param num_denom_keys expected number of denomination keys.
- * @param last_denom_date date to be set in the "last_denom_issue"
- *        URL parameter of /keys.
- * @return the command.
- */
 struct TALER_TESTING_Command
-TALER_TESTING_cmd_check_keys_with_last_denom (const char *label,
-                                              unsigned int generation,
-                                              unsigned int num_denom_keys,
-                                              struct GNUNET_TIME_Absolute
-                                              last_denom_date)
+TALER_TESTING_cmd_check_keys_with_last_denom (
+  const char *label,
+  unsigned int generation,
+  struct GNUNET_TIME_Absolute last_denom_date)
 {
   struct CheckKeysState *cks;
 
   cks = GNUNET_new (struct CheckKeysState);
   cks->generation = generation;
-  cks->num_denom_keys = num_denom_keys;
   cks->set_last_denom = GNUNET_YES;
   cks->last_denom_date = last_denom_date;
   {
@@ -237,74 +198,14 @@ TALER_TESTING_cmd_check_keys_with_last_denom (const char 
*label,
 }
 
 
-/**
- * Make a "check keys" command.  This type of command
- * checks whether the number of denomination keys from
- * @a exchange matches @a num_denom_keys.
- *
- * @param label command label
- * @param generation when this command is run, exactly @a
- *        generation /keys downloads took place.  If the number
- *        of downloads is less than @a generation, the logic will
- *        first make sure that @a generation downloads are done,
- *        and _then_ execute the rest of the command.
- * @param num_denom_keys expected number of denomination keys.
- * @return the command.
- */
 struct TALER_TESTING_Command
 TALER_TESTING_cmd_check_keys (const char *label,
-                              unsigned int generation,
-                              unsigned int num_denom_keys)
-{
-  struct CheckKeysState *cks;
-
-  cks = GNUNET_new (struct CheckKeysState);
-  cks->generation = generation;
-  cks->num_denom_keys = num_denom_keys;
-  {
-    struct TALER_TESTING_Command cmd = {
-      .cls = cks,
-      .label = label,
-      .run = &check_keys_run,
-      .cleanup = &check_keys_cleanup
-    };
-
-    return cmd;
-  }
-}
-
-
-/**
- * Make a "check keys" command.  This type of command
- * checks whether the number of denomination keys from
- * @a exchange matches @a num_denom_keys.
- *
- * @param label command label
- * @param generation when this command is run, exactly @a
- *        generation /keys downloads took place.  If the number
- *        of downloads is less than @a generation, the logic will
- *        first make sure that @a generation downloads are done,
- *        and _then_ execute the rest of the command.
- * @param num_denom_keys expected number of denomination keys.
- * @param now timestamp to use when fetching keys
- * @return the command.
- */
-struct TALER_TESTING_Command
-TALER_TESTING_cmd_check_keys_with_now (const char *label,
-                                       unsigned int generation,
-                                       unsigned int num_denom_keys,
-                                       struct GNUNET_TIME_Absolute now)
+                              unsigned int generation)
 {
   struct CheckKeysState *cks;
 
   cks = GNUNET_new (struct CheckKeysState);
   cks->generation = generation;
-  cks->num_denom_keys = num_denom_keys;
-  cks->now = now;
-  cks->with_now = GNUNET_YES;
-
-  /* Force to NOT cherry pick, otherwise they conflict.  */
-  cks->pull_all_keys = GNUNET_YES;
   {
     struct TALER_TESTING_Command cmd = {
       .cls = cks,
@@ -318,30 +219,14 @@ TALER_TESTING_cmd_check_keys_with_now (const char *label,
 }
 
 
-/**
- * Make a "check keys" command that forcedly does NOT cherry pick;
- * just redownload the whole /keys.  Then checks whether the number
- * of denomination keys from @a exchange matches @a num_denom_keys.
- *
- * @param label command label
- * @param generation when this command is run, exactly @a
- *        generation /keys downloads took place.  If the number
- *        of downloads is less than @a generation, the logic will
- *        first make sure that @a generation downloads are done,
- *        and _then_ execute the rest of the command.
- * @param num_denom_keys expected number of denomination keys.
- * @return the command.
- */
 struct TALER_TESTING_Command
 TALER_TESTING_cmd_check_keys_pull_all_keys (const char *label,
-                                            unsigned int generation,
-                                            unsigned int num_denom_keys)
+                                            unsigned int generation)
 {
   struct CheckKeysState *cks;
 
   cks = GNUNET_new (struct CheckKeysState);
   cks->generation = generation;
-  cks->num_denom_keys = num_denom_keys;
   cks->pull_all_keys = GNUNET_YES;
   {
     struct TALER_TESTING_Command cmd = {
diff --git a/src/testing/testing_api_cmd_offline_sign_fees.c 
b/src/testing/testing_api_cmd_offline_sign_fees.c
new file mode 100644
index 00000000..50095320
--- /dev/null
+++ b/src/testing/testing_api_cmd_offline_sign_fees.c
@@ -0,0 +1,192 @@
+/*
+  This file is part of TALER
+  Copyright (C) 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 published
+  by the Free Software Foundation; either version 3, or (at your
+  option) any later version.
+
+  TALER is distributed in the hope that it will be useful, but
+  WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public
+  License along with TALER; see the file COPYING.  If not,
+  see <http://www.gnu.org/licenses/>
+*/
+
+/**
+ * @file testing/testing_api_cmd_offline_sign_fees.c
+ * @brief run the taler-exchange-offline command to download, sign and upload 
wire fees
+ * @author Marcello Stanisci
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include "taler_json_lib.h"
+#include <gnunet/gnunet_curl_lib.h>
+#include "taler_signatures.h"
+#include "taler_testing_lib.h"
+
+
+/**
+ * State for a "offlinesign" CMD.
+ */
+struct OfflineSignState
+{
+
+  /**
+   * Process for the "offlinesign" command.
+   */
+  struct GNUNET_OS_Process *offlinesign_proc;
+
+  /**
+   * Configuration file used by the command.
+   */
+  const char *config_filename;
+
+  /**
+   * The wire fee to sign.
+   */
+  const char *wire_fee_s;
+
+  /**
+   * The closing fee to sign.
+   */
+  const char *closing_fee_s;
+
+};
+
+
+/**
+ * Run the command; calls the `taler-exchange-offline' program.
+ *
+ * @param cls closure.
+ * @param cmd the commaind being run.
+ * @param is interpreter state.
+ */
+static void
+offlinesign_run (void *cls,
+                 const struct TALER_TESTING_Command *cmd,
+                 struct TALER_TESTING_Interpreter *is)
+{
+  struct OfflineSignState *ks = cls;
+
+  ks->offlinesign_proc
+    = GNUNET_OS_start_process (
+        GNUNET_OS_INHERIT_STD_ALL,
+        NULL, NULL, NULL,
+        "taler-exchange-offline",
+        "taler-exchange-offline",
+        "-c", ks->config_filename,
+        "-L", "INFO",
+        "wire-fee",
+        "now",
+        "x-taler-bank",
+        ks->wire_fee_s,
+        ks->closing_fee_s,
+        "upload",
+        NULL);
+  if (NULL == ks->offlinesign_proc)
+  {
+    GNUNET_break (0);
+    TALER_TESTING_interpreter_fail (is);
+    return;
+  }
+  TALER_TESTING_wait_for_sigchld (is);
+}
+
+
+/**
+ * Free the state of a "offlinesign" CMD, and possibly kills its
+ * process if it did not terminate correctly.
+ *
+ * @param cls closure.
+ * @param cmd the command being freed.
+ */
+static void
+offlinesign_cleanup (void *cls,
+                     const struct TALER_TESTING_Command *cmd)
+{
+  struct OfflineSignState *ks = cls;
+
+  (void) cmd;
+  if (NULL != ks->offlinesign_proc)
+  {
+    GNUNET_break (0 ==
+                  GNUNET_OS_process_kill (ks->offlinesign_proc,
+                                          SIGKILL));
+    GNUNET_OS_process_wait (ks->offlinesign_proc);
+    GNUNET_OS_process_destroy (ks->offlinesign_proc);
+    ks->offlinesign_proc = NULL;
+  }
+  GNUNET_free (ks);
+}
+
+
+/**
+ * Offer "offlinesign" CMD internal data to other commands.
+ *
+ * @param cls closure.
+ * @param[out] ret result
+ * @param trait name of the trait.
+ * @param index index number of the object to offer.
+ * @return #GNUNET_OK on success.
+ */
+static int
+offlinesign_traits (void *cls,
+                    const void **ret,
+                    const char *trait,
+                    unsigned int index)
+{
+  struct OfflineSignState *ks = cls;
+  struct TALER_TESTING_Trait traits[] = {
+    TALER_TESTING_make_trait_process (0,
+                                      &ks->offlinesign_proc),
+    TALER_TESTING_trait_end ()
+  };
+
+  return TALER_TESTING_get_trait (traits,
+                                  ret,
+                                  trait,
+                                  index);
+}
+
+
+/**
+ * Sign a wire fee.
+ *
+ * @param label command label.
+ * @param config_filename configuration filename.
+ * @param wire_fee the wire fee to affirm (for the current year)
+ * @param closing_fee the closing fee to affirm (for the current year)
+ * @return the command
+ */
+struct TALER_TESTING_Command
+TALER_TESTING_cmd_exec_offline_sign_fees (const char *label,
+                                          const char *config_filename,
+                                          const char *wire_fee,
+                                          const char *closing_fee)
+{
+  struct OfflineSignState *ks;
+
+  ks = GNUNET_new (struct OfflineSignState);
+  ks->config_filename = config_filename;
+  ks->wire_fee_s = wire_fee;
+  ks->closing_fee_s = closing_fee;
+  {
+    struct TALER_TESTING_Command cmd = {
+      .cls = ks,
+      .label = label,
+      .run = &offlinesign_run,
+      .cleanup = &offlinesign_cleanup,
+      .traits = &offlinesign_traits
+    };
+
+    return cmd;
+  }
+}
+
+
+/* end of testing_api_cmd_exec_offline_sign_fees.c */
diff --git a/src/testing/testing_api_cmd_revoke.c 
b/src/testing/testing_api_cmd_revoke.c
index 8863110b..f17f351e 100644
--- a/src/testing/testing_api_cmd_revoke.c
+++ b/src/testing/testing_api_cmd_revoke.c
@@ -181,8 +181,6 @@ revoke_run (void *cls,
   }
   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
               "Revoke is ongoing..\n");
-
-  is->reload_keys = GNUNET_OK;
   TALER_TESTING_wait_for_sigchld (is);
 }
 
diff --git a/src/testing/testing_api_loop.c b/src/testing/testing_api_loop.c
index f269274e..c89073a1 100644
--- a/src/testing/testing_api_loop.c
+++ b/src/testing/testing_api_loop.c
@@ -463,24 +463,6 @@ maint_child_death (void *cls)
     return;
   }
 
-  // FIXME: remove reload_keys, obsolete!
-  if (GNUNET_OK == is->reload_keys)
-  {
-    if (NULL == is->exchanged)
-    {
-      GNUNET_break (0);
-    }
-    else
-    {
-      GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-                  "Triggering key state reload at exchange\n");
-      GNUNET_break (0 ==
-                    GNUNET_OS_process_kill (is->exchanged,
-                                            SIGUSR1));
-      sleep (5); /* make sure signal was received and processed */
-    }
-  }
-
   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
               "Dead child, go on with next command.\n");
   TALER_TESTING_interpreter_next (is);

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