gnunet-svn
[Top][All Lists]
Advanced

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

[taler-exchange] branch master updated (b5d88fc2 -> 45926ec0)


From: gnunet
Subject: [taler-exchange] branch master updated (b5d88fc2 -> 45926ec0)
Date: Mon, 14 Dec 2020 17:17:18 +0100

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

grothoff pushed a change to branch master
in repository exchange.

    from b5d88fc2 activating implementation of #6175
     new 04f2e9a4 (start to) remove logic no longer needed with new key 
management
     new 45926ec0 remove very obsolete tool and test

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


Summary of changes:
 src/exchange-tools/Makefile.am                     |   17 -
 src/exchange-tools/taler-exchange-keycheck.c       |  336 ---
 src/exchange-tools/test_taler_exchange_keyup.sh    |  142 --
 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_link.h => taler-exchange-httpd_loop.h} |   45 +-
 .../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 +-
 ..._keys.c => testing_api_cmd_offline_sign_fees.c} |   40 +-
 src/testing/testing_api_cmd_revoke.c               |    2 -
 src/testing/testing_api_loop.c                     |   18 -
 45 files changed, 610 insertions(+), 4353 deletions(-)
 delete mode 100644 src/exchange-tools/taler-exchange-keycheck.c
 delete mode 100755 src/exchange-tools/test_taler_exchange_keyup.sh
 delete mode 100644 src/exchange/taler-exchange-httpd_keystate.c
 delete mode 100644 src/exchange/taler-exchange-httpd_keystate.h
 create mode 100644 src/exchange/taler-exchange-httpd_loop.c
 copy src/exchange/{taler-exchange-httpd_link.h => taler-exchange-httpd_loop.h} 
(54%)
 delete mode 100644 src/exchange/taler-exchange-httpd_wire.c
 copy src/testing/{testing_api_cmd_offline_sign_keys.c => 
testing_api_cmd_offline_sign_fees.c} (81%)

diff --git a/src/exchange-tools/Makefile.am b/src/exchange-tools/Makefile.am
index e6096303..43954c44 100644
--- a/src/exchange-tools/Makefile.am
+++ b/src/exchange-tools/Makefile.am
@@ -15,7 +15,6 @@ endif
 bin_PROGRAMS = \
   taler-auditor-offline \
   taler-exchange-keyup \
-  taler-exchange-keycheck \
   taler-exchange-offline \
   taler-exchange-wire \
   taler-exchange-dbinit
@@ -76,16 +75,6 @@ taler_exchange_wire_LDADD = \
   $(XLIB)
 taler_exchange_wire_LDFLAGS = $(POSTGRESQL_LDFLAGS)
 
-taler_exchange_keycheck_SOURCES = \
-  taler-exchange-keycheck.c
-taler_exchange_keycheck_LDADD = \
-  $(LIBGCRYPT_LIBS) \
-  $(top_builddir)/src/util/libtalerutil.la \
-  $(top_builddir)/src/exchangedb/libtalerexchangedb.la \
-  -lgnunetutil \
-  $(XLIB)
-taler_exchange_keycheck_LDFLAGS = $(POSTGRESQL_LDFLAGS)
-
 taler_exchange_dbinit_SOURCES = \
   taler-exchange-dbinit.c
 taler_exchange_dbinit_LDADD = \
@@ -107,12 +96,6 @@ taler_exchange_dbinit_CPPFLAGS = \
 
 AM_TESTS_ENVIRONMENT=export TALER_PREFIX=$${TALER_PREFIX:-@libdir@};export 
PATH=$${TALER_PREFIX:-@prefix@}/bin:$$PATH;
 
-check_SCRIPTS = \
-  test_taler_exchange_keyup.sh
-
-TESTS = \
-  $(check_SCRIPTS)
-
 # Distribution
 
 EXTRA_DIST = \
diff --git a/src/exchange-tools/taler-exchange-keycheck.c 
b/src/exchange-tools/taler-exchange-keycheck.c
deleted file mode 100644
index 917ca879..00000000
--- a/src/exchange-tools/taler-exchange-keycheck.c
+++ /dev/null
@@ -1,336 +0,0 @@
-/*
-  This file is part of TALER
-  Copyright (C) 2014, 2015, 2016 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 taler-exchange-keycheck.c
- * @brief Check exchange keys for validity.  Reads the signing and denomination
- *        keys from the exchange directory and checks to make sure they are
- *        well-formed.  This is purely a diagnostic tool.
- * @author Florian Dold
- * @author Benedikt Mueller
- * @author Christian Grothoff
- */
-#include <platform.h>
-#include <gnunet/gnunet_util_lib.h>
-#include "taler_exchangedb_lib.h"
-
-/**
- * Exchange directory with the keys.
- */
-static char *exchange_directory;
-
-/**
- * Our configuration.
- */
-static const struct GNUNET_CONFIGURATION_Handle *kcfg;
-
-/**
- * Return value from main().
- */
-static int global_ret;
-
-/**
- * Option -i used to print full denomination key hashes for
- * denominations of certain amounts.
- */
-static struct TALER_Amount print_dk_amount;
-
-
-/**
- * Function called on each signing key.
- *
- * @param cls closure (NULL)
- * @param filename name of the file the key came from
- * @param ski the sign key
- * @return #GNUNET_OK to continue to iterate,
- *  #GNUNET_NO to stop iteration with no error,
- *  #GNUNET_SYSERR to abort iteration with error!
- */
-static int
-signkeys_iter (void *cls,
-               const char *filename,
-               const struct TALER_EXCHANGEDB_PrivateSigningKeyInformationP 
*ski)
-{
-  (void) cls;
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Iterating over key `%s' for start time %s\n",
-              filename,
-              GNUNET_STRINGS_absolute_time_to_string
-                (GNUNET_TIME_absolute_ntoh (ski->issue.start)));
-
-  if (ntohl (ski->issue.purpose.size) !=
-      (sizeof (struct TALER_ExchangeSigningKeyValidityPS)))
-  {
-    fprintf (stderr,
-             "Signing key `%s' has invalid purpose size\n",
-             filename);
-    return GNUNET_SYSERR;
-  }
-  if ( (0 != GNUNET_TIME_absolute_ntoh (ski->issue.start).abs_value_us
-        % 1000000) ||
-       (0 != GNUNET_TIME_absolute_ntoh (ski->issue.expire).abs_value_us
-        % 1000000) ||
-       (0 != GNUNET_TIME_absolute_ntoh (ski->issue.end).abs_value_us
-        % 1000000) )
-  {
-    fprintf (stderr,
-             "Timestamps are not multiples of a round second\n");
-    return GNUNET_SYSERR;
-  }
-
-  if (GNUNET_OK !=
-      GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_SIGNING_KEY_VALIDITY,
-                                  &ski->issue,
-                                  &ski->master_sig.eddsa_signature,
-                                  &ski->issue.master_public_key.eddsa_pub))
-  {
-    fprintf (stderr,
-             "Signing key `%s' has invalid signature\n",
-             filename);
-    return GNUNET_SYSERR;
-  }
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Signing key `%s' valid\n",
-              filename);
-  return GNUNET_OK;
-}
-
-
-/**
- * Check signing keys.
- *
- * @return #GNUNET_OK if the keys are OK
- *         #GNUNET_NO if not
- */
-static int
-exchange_signkeys_check ()
-{
-  if (0 > TALER_EXCHANGEDB_signing_keys_iterate (exchange_directory,
-                                                 &signkeys_iter,
-                                                 NULL))
-    return GNUNET_NO;
-  return GNUNET_OK;
-}
-
-
-/**
- * Function called on each denomination key.
- *
- * @param cls closure (NULL)
- * @param dki the denomination key
- * @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
-denomkeys_iter (void *cls,
-                const char *alias,
-                const struct
-                TALER_EXCHANGEDB_DenominationKey *dki)
-{
-  struct GNUNET_HashCode hc;
-  struct TALER_Amount value;
-
-  (void) cls;
-  if (ntohl (dki->issue.properties.purpose.size) !=
-      sizeof (struct TALER_DenominationKeyValidityPS))
-  {
-    fprintf (stderr,
-             "Denomination key for `%s' has invalid purpose size\n",
-             alias);
-    return GNUNET_SYSERR;
-  }
-
-  if ( (0 != GNUNET_TIME_absolute_ntoh (
-          dki->issue.properties.start).abs_value_us % 1000000) ||
-       (0 != GNUNET_TIME_absolute_ntoh (
-          dki->issue.properties.expire_withdraw).abs_value_us % 1000000) ||
-       (0 != GNUNET_TIME_absolute_ntoh (
-          dki->issue.properties.expire_legal).abs_value_us % 1000000) ||
-       (0 != GNUNET_TIME_absolute_ntoh (
-          dki->issue.properties.expire_deposit).abs_value_us % 1000000) )
-  {
-    fprintf (stderr,
-             "Timestamps are not multiples of a round second\n");
-    return GNUNET_SYSERR;
-  }
-
-  if (GNUNET_OK !=
-      GNUNET_CRYPTO_eddsa_verify (
-        TALER_SIGNATURE_MASTER_DENOMINATION_KEY_VALIDITY,
-        &dki->issue.properties,
-        &dki->issue.signature.eddsa_signature,
-        &dki->issue.properties.master.eddsa_pub))
-  {
-    fprintf (stderr,
-             "Denomination key for `%s' has invalid signature\n",
-             alias);
-    return GNUNET_SYSERR;
-  }
-  GNUNET_CRYPTO_rsa_public_key_hash (dki->denom_pub.rsa_public_key,
-                                     &hc);
-  if (0 != GNUNET_memcmp (&hc,
-                          &dki->issue.properties.denom_hash))
-  {
-    fprintf (stderr,
-             "Public key for `%s' does not match signature\n",
-             alias);
-    return GNUNET_SYSERR;
-  }
-  GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
-              "Denomination key `%s' (%s) is valid\n",
-              alias,
-              GNUNET_h2s (&hc));
-  TALER_amount_ntoh (&value,
-                     &dki->issue.properties.value);
-  if ( (GNUNET_OK ==
-        TALER_amount_cmp_currency (&print_dk_amount,
-                                   &value)) &&
-       (0 ==
-        TALER_amount_cmp (&print_dk_amount,
-                          &value)) )
-  {
-    char *dh;
-    struct GNUNET_TIME_Absolute start;
-
-    start = GNUNET_TIME_absolute_ntoh (dki->issue.properties.start);
-    dh = GNUNET_STRINGS_data_to_string_alloc 
(&dki->issue.properties.denom_hash,
-                                              sizeof (struct GNUNET_HashCode));
-    /* output start time first for easy numeric sorting, then
-       the denomination hash, and finally the human-readable start time */
-    printf ("%020llu %s %s\n",
-            (unsigned long long) start.abs_value_us,
-            dh,
-            GNUNET_STRINGS_absolute_time_to_string (start));
-    GNUNET_free (dh);
-  }
-  return GNUNET_OK;
-}
-
-
-/**
- * Check denomination keys.
- *
- * @return #GNUNET_OK if the keys are OK
- *         #GNUNET_NO if not
- */
-static int
-exchange_denomkeys_check ()
-{
-  struct TALER_MasterPublicKeyP master_public_key_from_cfg;
-
-  if (GNUNET_OK !=
-      GNUNET_CONFIGURATION_get_data (kcfg,
-                                     "exchange",
-                                     "master_public_key",
-                                     &master_public_key_from_cfg,
-                                     sizeof (struct
-                                             GNUNET_CRYPTO_EddsaPublicKey)))
-  {
-    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
-                               "exchange",
-                               "master_public_key");
-    return GNUNET_NO;
-  }
-  if (0 > TALER_EXCHANGEDB_denomination_keys_iterate (exchange_directory,
-                                                      &denomkeys_iter,
-                                                      NULL))
-    return GNUNET_NO;
-  return GNUNET_OK;
-}
-
-
-/**
- * Main function that will be run.
- *
- * @param cls closure
- * @param args remaining command-line arguments
- * @param cfgfile name of the configuration file used (for saving, can be 
NULL!)
- * @param cfg configuration
- */
-static void
-run (void *cls,
-     char *const *args,
-     const char *cfgfile,
-     const struct GNUNET_CONFIGURATION_Handle *cfg)
-{
-  (void) cls;
-  (void) args;
-  (void) cfgfile;
-  kcfg = cfg;
-  if (GNUNET_OK !=
-      GNUNET_CONFIGURATION_get_value_filename (kcfg,
-                                               "exchange",
-                                               "KEYDIR",
-                                               &exchange_directory))
-  {
-    GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
-                               "exchange",
-                               "KEYDIR");
-    global_ret = 1;
-    return;
-  }
-
-  if ( (GNUNET_OK != exchange_signkeys_check ()) ||
-       (GNUNET_OK != exchange_denomkeys_check ()) )
-  {
-    global_ret = 1;
-    return;
-  }
-}
-
-
-/**
- * The main function of the keyup tool
- *
- * @param argc number of arguments from the command line
- * @param argv command line arguments
- * @return 0 ok, 1 on error
- */
-int
-main (int argc,
-      char *const *argv)
-{
-  const struct GNUNET_GETOPT_CommandLineOption options[] = {
-    TALER_getopt_get_amount ('i',
-                             "denomination-info-hash",
-                             "AMOUNT",
-                             "print full denomination hashes of all 
denominations with the given AMOUNT value",
-                             &print_dk_amount),
-    GNUNET_GETOPT_OPTION_END
-  };
-
-  /* force linker to link against libtalerutil; if we do
-    not do this, the linker may "optimize" libtalerutil
-    away and skip #TALER_OS_init(), which we do need */
-  (void) TALER_project_data_default ();
-  GNUNET_assert (GNUNET_OK ==
-                 GNUNET_log_setup ("taler-exchange-keycheck",
-                                   "WARNING",
-                                   NULL));
-  if (GNUNET_OK !=
-      GNUNET_PROGRAM_run (argc, argv,
-                          "taler-exchange-keycheck",
-                          "Check keys of the exchange for validity",
-                          options,
-                          &run, NULL))
-    return 1;
-  return global_ret;
-
-}
-
-
-/* end of taler-exchange-keycheck.c */
diff --git a/src/exchange-tools/test_taler_exchange_keyup.sh 
b/src/exchange-tools/test_taler_exchange_keyup.sh
deleted file mode 100755
index 26f7fe13..00000000
--- a/src/exchange-tools/test_taler_exchange_keyup.sh
+++ /dev/null
@@ -1,142 +0,0 @@
-#!/bin/bash
-#
-# 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, If not, see 
<http://www.gnu.org/licenses/>
-#
-#
-# This script uses 'curl' to POST various ill-formed requests to the
-# taler-exchange-httpd.  Basically, the goal is to make sure that the
-# HTTP server survives (and produces the 'correct' error code).
-#
-#
-# Clear environment from variables that override config.
-unset XDG_DATA_HOME
-unset XDG_CONFIG_HOME
-#
-
-
-# Exit, with status code "skip" (no 'real' failure)
-function exit_skip() {
-    echo $1
-    exit 77
-}
-
-# test required commands exist
-echo -n "Testing for jq ..."
-jq -h > /dev/null || exit_skip "jq required"
-echo " OK"
-
-CONF="-c test_taler_exchange_httpd.conf"
-
-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"
-
-# Setup database
-taler-exchange-dbinit $CONF &> /dev/null
-# Setup keys.
-taler-exchange-keyup $CONF &> /dev/null || exit 1
-# Setup wire accounts.
-taler-exchange-wire $CONF > /dev/null || exit 1
-# Run Exchange HTTPD (in background)
-$PREFIX taler-exchange-httpd $CONF 2> test-exchange.log &
-
-# Give HTTP time to start
-
-for n in `seq 1 100`
-do
-    echo -n "."
-    sleep 0.1
-    OK=1
-    wget http://localhost:8081/ -o /dev/null -O /dev/null >/dev/null && break
-    OK=0
-done
-if [ 1 != $OK ]
-then
-    echo "Failed to launch exchange"
-    kill -TERM $!
-    wait $!
-    echo Process status: $?
-    exit 77
-fi
-echo " DONE"
-
-# Finally run test...
-echo -n "Running tests ... "
-
-# Revoke active denomination key
-REVOKE_DENOM_HASH=`taler-exchange-keycheck $CONF -i EUR:1 | sort | head -n1 | 
awk '{print $2}'`
-REVOKE_DENOM_TIME=`taler-exchange-keycheck $CONF -i EUR:1 | sort | head -n1 | 
awk '{print $1}'`
-
-taler-exchange-keyup $CONF -r "$REVOKE_DENOM_HASH" -k 1024
-
-# check revocation file exists
-RDIR=`taler-config $CONF -f -s exchange -o REVOCATION_DIR`
-if [ -f "$RDIR"/$REVOKE_DENOM_HASH.rev ]
-then
-    echo -n "REV-OK "
-else
-    echo -n "REV-FAIL ($RDIR) "
-    RET=1
-fi
-
-# Check we now have two keys for that timestamp
-CNT=`taler-exchange-keycheck $CONF -i EUR:1 | awk '{print $1}' | grep -- 
"$REVOKE_DENOM_TIME" | wc -l`
-
-if [ x2 != x${CNT} ]
-then
-    echo -n "CNT-FAIL (${CNT}) "
-    RET=1
-else
-    echo -n "CNT-OK "
-fi
-
-# Reload keys (and revocation data) at the exchange
-kill -SIGUSR1 $!
-
-# Give exchange chance to parse and reload keys
-sleep 5
-
-# Download (updated) keys
-wget http://localhost:8081/keys -O keys.json -o /dev/null >/dev/null
-
-RK=`jq -er .recoup[0].h_denom_pub < keys.json`
-if [ x$RK != x$REVOKE_DENOM_HASH ]
-then
-    echo -n "KEYS-FAIL ($RK vs $REVOKE_DENOM_HASH)"
-    RET=1
-else
-    echo -n "KEYS-OK"
-fi
-
-echo " DONE"
-# $! is the last backgrounded process, hence the exchange
-kill -TERM $!
-wait $!
-if [ 0 != $? ]
-then
-    RET=4
-fi
-
-echo "Final cleanup"
-# Can't leave revocations around, would mess up next test run
-rm -r "$RDIR"
-# Also cleaning up live keys, as otherwise we have two for the revoked 
denomination type next time
-KDIR=`taler-config $CONF -f -s exchange -o KEYDIR`
-rm -r "$KDIR"
-# Clean up our temporary file
-rm keys.json
-
-exit $RET
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_link.h 
b/src/exchange/taler-exchange-httpd_loop.h
similarity index 54%
copy from src/exchange/taler-exchange-httpd_link.h
copy to src/exchange/taler-exchange-httpd_loop.h
index 4c9651a9..700f5677 100644
--- a/src/exchange/taler-exchange-httpd_link.h
+++ b/src/exchange/taler-exchange-httpd_loop.h
@@ -1,6 +1,6 @@
 /*
   This file is part of TALER
-  Copyright (C) 2014-2017 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,14 +14,14 @@
   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
 */
 /**
- * @file taler-exchange-httpd_link.h
- * @brief Handle /coins/$COIN_PUB/link 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_LINK_H
-#define TALER_EXCHANGE_HTTPD_LINK_H
+#ifndef TALER_EXCHANGE_HTTPD_LOOP_H
+#define TALER_EXCHANGE_HTTPD_LOOP_H
 
 #include <gnunet/gnunet_util_lib.h>
 #include <microhttpd.h>
@@ -29,17 +29,32 @@
 
 
 /**
- * Handle a "/coins/$COIN_PUB/link" request.
+ * Setup our signal handlers.
  *
- * @param rh context of the handler
- * @param connection the MHD connection to handle
- * @param args array of additional options (length: 2, first is the coin_pub, 
second must be "link")
- * @return MHD result code
-  */
-MHD_RESULT
-TEH_handler_link (const struct TEH_RequestHandler *rh,
-                  struct MHD_Connection *connection,
-                  const char *const args[2]);
+ * @return #GNUNET_OK on success
+ */
+int
+TEH_loop_init (void);
+
+
+/**
+ * Finally, tear down our signal handlers.
+ */
+void
+TEH_loop_done (void);
+
+
+/**
+ * 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_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_keys.c 
b/src/testing/testing_api_cmd_offline_sign_fees.c
similarity index 81%
copy from src/testing/testing_api_cmd_offline_sign_keys.c
copy to src/testing/testing_api_cmd_offline_sign_fees.c
index dd6170d9..50095320 100644
--- a/src/testing/testing_api_cmd_offline_sign_keys.c
+++ b/src/testing/testing_api_cmd_offline_sign_fees.c
@@ -18,8 +18,8 @@
 */
 
 /**
- * @file testing/testing_api_cmd_offline_sign_keys.c
- * @brief run the taler-exchange-offline command to download, sign and upload 
keys
+ * @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
  */
@@ -46,6 +46,16 @@ struct OfflineSignState
    */
   const char *config_filename;
 
+  /**
+   * The wire fee to sign.
+   */
+  const char *wire_fee_s;
+
+  /**
+   * The closing fee to sign.
+   */
+  const char *closing_fee_s;
+
 };
 
 
@@ -71,8 +81,11 @@ offlinesign_run (void *cls,
         "taler-exchange-offline",
         "-c", ks->config_filename,
         "-L", "INFO",
-        "download",
-        "sign",
+        "wire-fee",
+        "now",
+        "x-taler-bank",
+        ks->wire_fee_s,
+        ks->closing_fee_s,
         "upload",
         NULL);
   if (NULL == ks->offlinesign_proc)
@@ -141,14 +154,27 @@ offlinesign_traits (void *cls,
 }
 
 
+/**
+ * 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_keys (const char *label,
-                                          const char *config_filename)
+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,
@@ -163,4 +189,4 @@ TALER_TESTING_cmd_exec_offline_sign_keys (const char *label,
 }
 
 
-/* end of testing_api_cmd_exec_offline_sign_keys.c */
+/* 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]